コマンドインジェクション

ウェブサーバーのOSで使われるコマンドを注入する攻撃のことを言います。この攻撃は、system、passthru、exec、popen、shell_execなどの関数を利用されることで発生します。OSコマンドインジェクションとも呼ばれていますが、この攻撃はOSのコマンドだけでなく、外部コマンドを実行させることでも発生します。

コマンドインジェクションで攻撃される場所

外部からのリクエストをsystemなどのパラメータ(引数)で受け取ることで発生します。例えば以下のコードを記述したファイルに適当な名前(ここではcommand.php)を付けて、そのURLの末尾に「?file=%3Bchmod+555+command.php%3B」と追記するとパーミッションの値がリアルタイムで変更されてしまいます。また、下記コードではリアルタイムで出力させるために「system('ls -la ' . $command);」としていますが、「system($command);」として「ls -la」をGETパラメータとして渡しても同様です。その場合、一回分遅れて出力されるようですが、FTPでのパーミッションはリアルタイムで変更されます。

if(isset($_GET['file']) === true){
$command = $_GET['file'];
}else{
$command = '/';
}
echo "<pre>";
system('ls -la ' . $command);
echo "</pre>";

GETパラメータを使ったコマンドインジェクション

GET送信で挿入するパラメータはurlencode()関数を使って文字列をURLエンコードすることで簡単に作成されてしまいます。そして下記コードの「555」という値を他の数字にするとパーミッションの値が変更されます。仮に「777」とされた場合、「すべてのユーザが書き込み可能」となります。

echo urlencode(';chmod 555 command.php;');
// systemの引数のコマンドは、「;」で区切ると複数指定できます
// 以下は複数のコマンドをurlencodeでエンコードする例
echo urlencode('ls -la ;chmod 555 command.php;');

コマンドインジェクションの対策

escapeshellarg()を使います。この関数はシェル引数として使われる文字列をエスケープします。また、if文の最初で、nullバイトを除去して誤作動を起こさせないようにします。

if(isset($_GET['file']) === true){
$command = str_replace("\0", '', $_GET['file']);
}else{
$command = '/';
}
echo "<pre>";
system(escapeshellarg($command));
echo "</pre>";

コマンドインジェクション対策:コマンドが使える関数の例

system()は外部プログラムを実行して出力。
passthru()は外部プログラムを実行して未整形のものを出力。
exec()は外部プログラムを実行します。popen()はプロセスへのファイルポインタをオープンします。
shell_exec()はシェルでコマンドを実行し、文字列として出力全体を返す。
いずれも引数にコマンドを指定できる関数です。