ネイティブコード以外の実行ファイルに対してsetuidが効かない件
ネットワークセキュリティHacks―プロが使うテクニック&ツール100選を読むまですっかり忘れていた。Cを使ういい機会なのでやってみる。
TCPの321番ポートを開くだけのプログラム。
まずはC.
#include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define ERROR(x) { perror(x); exit(1); } int main(int argc, char *argv[]) { int sock_s, sock_c; socklen_t len_c; struct sockaddr_in addr_s, addr_c; if ((sock_s = socket(PF_INET, SOCK_STREAM, 0)) < 0) ERROR("socket"); addr_s.sin_family = PF_INET; addr_s.sin_addr.s_addr = inet_addr("0.0.0.0"); addr_s.sin_port = htons(321); if (bind(sock_s, (struct sockaddr*) &addr_s, sizeof(addr_s)) < 0) ERROR("bind"); if (listen(sock_s, 5) < 0) ERROR("listen"); if ((sock_c = accept(sock_s, (struct sockaddr*) &addr_c, &len_c)) < 0) ERROR("accept"); exit(0); }
で、これをコンパイルしてrootのものにしたうえでsetuid属性をつけてあげると一般ユーザーでも321番ポートを開ける。
komamitsu@potato:~/lab/c/tcpserv$ ls -l tcpserv -rwsr-xr-x 1 root komamitsu 9313 2009-04-09 01:23 tcpserv komamitsu@potato:~/lab/c/tcpserv$ ./tcpserv
つぎはRuby.
#!/usr/bin/env ruby require 'socket' s = TCPServer.new('0.0.0.0', 321) s.listen(5) c = s.accept
み、短いな…
で同じようにsetuidして実行すると。
komamitsu@potato:~/lab/ruby$ ls -l tcpserv -rwsr-xr-x 1 root komamitsu 97 2009-04-09 01:27 tcpserv komamitsu@potato:~/lab/ruby$ ./tcpserv ./tcpserv:4:in `initialize': Permission denied - bind(2) (Errno::EACCES) from ./tcpserv:4:in `new' from ./tcpserv:4
setuidはスクリプト形式の実行ファイルには効かないらしい。
いきおいでOCamlも。もはや何が目的かどうでも良くなってきた。
open Unix let _ = let sa = ADDR_INET (inet_addr_any, 321) in ignore (establish_server (fun ich och -> ()) sa)
お、こちらも短いぞ。
で、バイトコードとネイティブコードの実行ファイルをつくってrootのsetuidつけて実行。
komamitsu@potato:~/lab/ocaml/tcpserv$ ls -ltr tcpserv_* -rwsr-xr-x 1 root komamitsu 55072 2009-04-09 01:33 tcpserv_byte -rwsr-xr-x 1 root komamitsu 256736 2009-04-09 01:33 tcpserv_native komamitsu@potato:~/lab/ocaml/tcpserv$ ./tcpserv_byte Fatal error: exception Unix.Unix_error(1, "bind", "") komamitsu@potato:~/lab/ocaml/tcpserv$ ./tcpserv_native
と、まあネイティブコード以外は駄目よ、と。