通常リモートからサーバOSのコマンドを実行するためには、telnetやsshなどを使ってログインして、コマンドを実行する。通常Webサーバでは、これらのサービスを挙げていたとしてもせいぜい管理用で、だれでも使えるようにはなっていないはずだ。
そこで、telnetやsshではなくWebアプリケーションを経由してOSコマンドを実行してしまおう、というのがこの攻撃だ。
OSコマンドを実行することができれば、任意のファイルの読み出し・変更・削除、OSやデータベースのパスワード漏えい、最悪はサーバの管理者権限まで奪われてしまうなど、被害は計り知れない。
ほとんどのプログラム言語には、外部コマンドを実行する方法が用意されている。
Perl | exec() |
---|---|
system() | |
`command` | |
PHP | exec() |
system() | |
shell_exec() | |
passthru() | |
popen() | |
`command` | |
Java | java.lang.Runtime.exec() |
C言語 | exec() |
system() | |
表2 各プログラム言語の外部コマンドの実行方法 |
アプリケーション内で外部コマンドを使いたいときにはこれらの方法を利用することになるのだが、使い方を誤ると意図していないコマンドまで実行されてしまうことになる。
void CommandExec(String filename) throws IOException{ Process process = Runtime.getRuntime().exec("cat ../html/"+filename+".html"); // ← exec コマンド BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while((line=reader.readLine()) != null){ System.out.println(line); } }
execメソッドを使ってコマンドを実行している。filenameというパラメーターが使われているが、これにユーザー入力値が使われている場合危険となる。
まず、前回紹介したPath Traversalの問題があるのが分かる。filenameに../../admin/secretと入力されると、ほかのディレクトリ内のファイルが表示されてしまう。
それでは、filenameにこんなパラメータを入れるとどうなるだろうか?
`uname -a>/tmp/out.html;echo ../../../../../../tmp/out`
この出力は次のようになる。
Linux okd 2.4.20-13.9 #1 Thu Mar 13 17:54:28 EST 2003 i686 i686 i386 GNU/Linux
つまり、上記パラメータの中で指定したuname -aというコマンドの実行結果が出力される。ここでは「`(バッククオート)」を使ってOSコマンドを実行させたが、ほかにも、
command ; badcommand command && badcommand command | badcommand
などといった手法で、悪意のあるコマンドを実行できる場合もある。
対策は、これも入力値チェックとサニタイジングだ。入力値チェックを確実に行い、それをくぐり抜けてしまう特殊文字についてサニタイジングを行う。各OSやコマンド中のパラメータを使用する場所によって注意する必要がある特殊文字が異なるので、抜けがないようにしていただきたい。
| ! & ` | コマンドの実行に使われる |
---|---|
x00 | Null文字。文字列やファイル名を中断する |
x20 | 空白文字。パラメータの区切りに使われる |
x04 | EOT文字。ファイルの終わりに使われる |
x0D x0A | 改行文字。追加コマンドの実行、メールの偽造、ファイルの変更に使われる |
x1B | エスケープ文字 |
x08 | バックスペース文字。ログファイルの偽造、ファイルの変更に使われる |
x7F | 削除文字 |
" ' | 文字列の区切りに使われる |
/ - | パラメータの指定に使われる |
* ? | 正規表現で使われる |
~ . / | ディレクトリ指定に使われる |
ファイル操作に使われる | |
$ | 環境変数の参照に使われる |
( { [ ] } ) | コマンドの区切りに使われる |
表3 UNIX系OSコマンドでの特殊文字 |
; | & | コマンドの実行に使われる |
---|---|
x00 | Null文字。文字列やファイル名を中断する |
x20 | 空白文字。パラメータの区切りに使われる |
x04 | EOT文字。ファイルの終わりを偽造する |
x0A x0D | 改行文字。追加コマンドの実行、メールの偽造、ファイルの変更に使われる |
x1b | エスケープ文字 |
x08 | バックスペース文字。ログファイルの偽造、ファイルの変更に使われる |
x7f | 削除文字 |
" | 文字列の区切りに使われる |
/ - | パラメータの指定に使われる |
* ? | 正規表現で使われる |
. \ | ディレクトリ指定に使われる |
ファイル操作に使われる | |
% | 環境変数の参照に使われる |
表4 DOS系OSコマンドでの特殊文字 |
注意する必要があるのが、直接外部コマンドを呼び出すためのAPIだけかというとそうではない。ほかのAPIでも、外部コマンドを実行できたり、API内部的に外部コマンドを呼び出している場合がある。
リスト3は、前回載せたファイルを読み出して出力するPerlプログラムである。
#!/usr/bin/perl # ヘッダ部分の出力 &print_header(); # メインコンテンツの出力 open FILE, "$PARAMETER{file}.html"; while(<FILE>){ print; } # フッタ部分の出力 &print_footer();
前回最後に「Path Traversalの問題だけでなくさらに危険なセキュリティホールを含んでいる」としたが、ここにもOS Command Injectionの問題がある。
Perlのopen関数には、ファイル名の先頭に「|」がある場合は、ファイル名をコマンド名と解釈して実行する、という機能がある。これを悪用し、
|cat+/etc/passwd%00
という値をパラメータにわたすことによって、
cat /etc/passwd
というコマンドが実行されることになる。
このような隠れたOSコマンド呼び出しAPIをすべて把握しておくのは、その言語の実装まで詳細に知っている人以外には難しいだろう。サニタイジングしようにも、どの文字が特殊文字なのかも把握しきれない。そのためにも、入力値チェックの段階で安全な文字だけに制限しておきたい。
今回は、最も危険と思われるSQL InjectionとOS Command Injectionについて紹介した。双方とも基本的には入力値チェックとサニタイジングで十分回避できる。これらの対策はXSSと同様に実際のプログラミングを行うプログラマの責任であるから、しっかりと対策をしておいていただきたい。
次回は、プログラミング以前の設計段階で潜り込むセキュリティホールについて説明する。
国分裕(こくぶ ゆたか)
三井物産セキュアディレクション勤務。セキュリティコンサルタントとして、不正アクセス監視やセキュリティ検査などに従事している。金融機関、官公庁、大手製造業などへのセキュリティシ ステムの導入、セキュリティ 検査などの実績を持つ。
主に、不正アクセス監視サービス、セキュリティ検査、セキュリティポリシー策定支援などのサービス提供している。また、セキュリティに関する教育サービスも実施中。
Copyright © ITmedia, Inc. All Rights Reserved.