2011年1月4日火曜日

escapeshellcmdの危険な実例

補足

この記事は旧徳丸浩の日記からの転載です(元URLアーカイブはてなブックマーク1はてなブックマーク2)。
備忘のため転載いたしますが、この記事は2011年1月4日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり

先日の日記PHPのescapeshellcmdの危険性ではPHPのescapeshellcmd関数の危険性について指摘しましたが、脆弱となる実例を挙げていなかったので、「本当に危険なのか」と半信半疑の方もおられると思います。そこで、同関数が危険となる実例を考えたので報告します。
grepを使って、サーバー内を検索するスクリプトを考えます。
<?php
  header('Content-Type: text/html; charset=UTF-8');
?>
<html>
<body><pre>
<?php
  $key = @$_GET['key'];
  $out = shell_exec('grep --no-filename "' . escapeshellcmd($key) . '" /var/data/*');
  echo htmlspecialchars($out, ENT_COMPAT, 'UTF-8');
?>
</pre></body>
</html>
このスクリプトは、外部からのキーワードを/var/data/内のファイル群から検索して表示するものです。PHPのshell_execute関数を用いて、実行結果を文字列として返し、htmlspecialcharsでHTMLエスケープしています。検索キーはescapeshellcmdでエスケープした結果をダブルクォートで囲っています。
一見、なんの問題もないスクリプトに見えます。まずは正常系の結果です。key=bookで実行してみます。
book:村上春樹
book:芥川龍之介
book:シェークスピア
bookを含む行が表示されました。
次に攻撃です。key=:"+"/etc/passwd として実行してみます。以下の結果となります。
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
…略
/etc/passwdの内容が表示されました。エスケープしているのに、なぜこの結果になるのでしょうか。
それは、escapeshellcmdが「' および " は、対になっていない場合にのみエスケープされます。」という仕様のためです。このため、grepの起動文字列は次のようになっています。
grep --no-filename ":" "/etc/passwd" "/var/data/*"
/etc/passwdの各行にはコロンが含まれていますから、これで/etc/passwdの全行が表示されます。適当な文字がなければ「$」(行末にマッチ)などを指定してもいいですね。 あるいは、PHPのソースを表示することもできます。key=$"+"/var/www/html/grep.php と指定すると、以下の表示になります。
<?php
  header('Content-Type: text/html; charset=UTF-8');
?>
<html>
<body><pre>
<?php
  $key = @$_GET['key'];
この検索プログラム自体のソースが表示されました。スクリプトのソースが見えれば、他の脆弱性を探すのも楽ちんですよね。
escapeshellcmdの「' および " は、対になっていない場合にのみエスケープされます。」という仕様により、1つのパラメータが2つのパラメータに分かれてしまうことが問題です。これはescapeshellcmdの脆弱性というよりは、仕様の不備と言うしかないでしょうね。マニュアルに書いてある通りの動作が問題な訳ですから。というわけで、escapeshellcmdよりはescapeshellargを使えということと、そもそもOSコマンドを呼ばないなどのもっと良い方法を検討しましょう、というお話です。

0 件のコメント:

コメントを投稿

フォロワー