引き続き、setuidを攻略する。コマンドラインからの引数の汚染除去を行う手法を試みてみる。
基本的に、正規表現マッチにより取り出した値を利用すれば良いようである。
#!/usr/bin/perl -T
use strict;
use warnings;
if(@ARGV < 2) {
die "usage: $0 touch_file_name example_integer\n";
}
# コマンドラインからファイル名を取得
my $fn = shift;
# コマンドラインから文字列結合用の値を取得
my $num = shift;
# ファイル名マッチの正規表現で、汚染を除去する。
# ここでは単に一般的なUNIXファイル名であれば良しとしているが、
# 実際はディレクトリなどもっと厳しくする必要がある。
my $re_filename = qr|[0-9A-Za-z_,\-\./]+|;
die "$fn is not untained." unless $fn =~ /^($re_filename)$/;
$fn = $1;
open FH, ">$fn" || die "open failure. $!\n";
print FH "REAL_USER_ID(UID) = $<\n";
print FH "EFFECTIVE_USER_ID(EUID) = $>\n";
print FH "REAL_GROUP_ID(GID) = $(\n";
print FH "EFFECTIVE_GROUP_ID(EGID) = $)\n";
close FH;
# ファイル名に文字列結合する為の整数値を汚染除去する。
my $re_number = qr|[0-9\-\.]+|;
die "$num is not unsecure." unless $num =~ /^($re_number)$/;
$num = $1;
# 上記汚染除去を行っておかないと、続くopenで、
# Insecure dependency in open while running setuid at ...
# が発生する。
$fn .= "_$num";
open FH, ">$fn" || die "open failure. $!\n";
print FH "$fn\n";
print FH "$num\n";
close FH;
(user1)$ chmod ug+s setuserid2.pl (user1)$ ls -l setuserid2.pl -rwsr-sr-x ... user1 ... setuserid2.pl* (user1)$ su user2 Password: (user2)$ ./setuserid.pl /tmp/hoge 256 → (user2)$ cd /tmp; ls -l -rw-r--r-- ... user1 ... hoge.tmp -rw-r--r-- ... user1 ... hoge.tmp_256 (user2)$ more /tmp/hoge.tmp REAL_USER_ID(UID) = 600 EFFECTIVE_USER_ID(EUID) = 500 REAL_GROUP_ID(GID) = 600 600 EFFECTIVE_GROUP_ID(EGID) = 500 600 → user1 が 500, user2 が 600 (user2)$ more /tmp/hoge.tmp_256 /tmp/hoge.tmp_256 256
このように、正規表現でマッチした結果から再取得することで、Perlから見れば「明示的なユーザーチェックにより汚染を除去できた」とされ、コマンドライン引数をファイル名に使用することが可能となることを確認できた。
ただ、上記リンクの http://harapeko.asablo.jp/blog/2007/01/29/1148931 にもあるように、 むやみやたらと安易なREGEXPによる汚染除去を行うのではなく、本当にその引数が必要なのか?といった、設計自体の見直しも忘れない 方が良いらしい。