screncによるエンコードの意義
前回、screncがなんぼのもんじゃいと思って興味本位でデコードを試みてみた。目が痛くなって頭がボーッとして、根気が続かないので断念したが、(デコードできていないのにこういうのもなんだが)時間をかければいけそうな気がする。自分みたいなハシクレでもいけそうな気がする程度のものなので、頭のいい人だったらササッとデコードできるんじゃないか。
Web上でデコードツールを探してみた。いやあ、あることあること。でもEXEが多い。これじゃロジックが分からないのでつまらん。と思っていたら、スクリプトでデコーダを書いた人がいた。
http://john.bryntze.net/jbkb/index.php?title=Script-kb1_Decode_vbe_files
ヘッダとフッタの部分の認識は合っていた。要はここのファンクションらしい。
Function Decode(Chaine) Dim se,i,c,j,index,ChaineTemp Dim tDecode(127) Const Combinaison="1231232332321323132311233213233211323231311231321323112331123132" Set se=WSCript.CreateObject("Scripting.Encoder") For i=9 to 127 tDecode(i)="JLA" Next For i=9 to 127 ChaineTemp=Mid(se.EncodeScriptFile(".vbs",string(3,i),0,""),13,3) For j=1 to 3 c=Asc(Mid(ChaineTemp,j,1)) tDecode(c)=Left(tDecode(c),j-1) & chr(i) & Mid(tDecode(c),j+1) Next Next 'Next line we correct a bug, otherwise a ")" could be decoded to a ">" tDecode(42)=Left(tDecode(42),1) & ")" & Right(tDecode(42),1) Set se=Nothing Chaine=Replace(Replace(Chaine,"@&",chr(10)),"@#",chr(13)) Chaine=Replace(Replace(Chaine,"@*",">"),"@!","<") Chaine=Replace(Chaine,"@$","@") index=-1 For i=1 to Len(Chaine) c=asc(Mid(Chaine,i,1)) If c<128 Then index=index+1 If (c=9) or ((c>31) and (c<128)) Then If (c<>60) and (c<>62) and (c<>64) Then Chaine=Left(Chaine,i-1) & Mid(tDecode(c),Mid(Combinaison,(index mod 64)+1,1),1) & Mid(Chaine,i+1) End If End If Next Decode=Chaine End Function
このファンクションにヘッダとフッタをトリムしたものを食わせればデコードできる。ロジックについては今度ゆっくり見るとして、そもそもたったこれだけのプログラムでこんなに簡単にデコードできるんじゃ意味ないじゃないか。攻撃する側から見ても一発変換できるような難読化をかけるくらいなら、ゴミコードを大量に書いたほうがよっぽど時間稼ぎになるし、難読化という目的にも合致するのでは?
簡単なロジックをわざわざ長ったらしく冗長に書き出すような難読化変換ツールってないものか。最近、思考が攻撃する側になっている気がするが。
ちなみにScriptのデバッグ方法について。コマンドプロンプトより
>aaa.vbs //d //x
スクリプト難読化と闘ってみる
Windows Script Encoder なるものがある。これは、クライアントサイドで実行される JavaScript や VBScript をエンコードするもの。
スクリプトはソースコードが第三者から丸見えだ。つまり大事な知的財産がダダ漏れとなる。知的財産っていってしまうと大袈裟かもしれないが、大事なノウハウを隠せず、勝手に改変されてしまう可能性もある。そこで、ソースコードの露出度を抑えようということで開発されたのがこの仕組み。なのだが、最近では悪性コードにこのエンコードがかけられている。解析を困難にさせるような流行りの難読化ってやつだ。このエンコード自体はけっこう昔からあるものなのだが、最近になってよく聞くようになった。
このエンコードは「暗号化」とまではいえないものらしい。まぁ広義でいえば「暗号化」といえるかもしれないが、昨今の暗号強度の発展からいえば、とてもその強度は暗号と呼べるものではない。(あくまで「crypt」や「cipher」ではなく「encode」なのだと勝手に思ってみる)。まぁ、そんなに重くもできるわけないけどね。
MSのサイトにも以下のような文言がある。
エンコードされた内容が悪意のあるユーザーによって解読されるのを防ぐことを目的としているものではありません。
なるほど。じゃ、自分みたいなプログラマのハシクレでもあっさりデコードできるんだろうか。まぁ、Web上を探せばデコードツールなんて山ほどあるんだろう思うけど、ここはあえて、まず自力でトライ。自力で解析できれば目や勘を養うこともできるだろうと。
Windows Script Encoderの使い方はこのあたりを見てもらうこととして、
WSHスクリプト・コードを暗号化する
http://www.atmarkit.co.jp/fwin2k/win2ktips/443wshenc/wshenc.html
ツールの動きを見てみる。以下のスクリプト部分を Script Encoder にかけてみる。
<SCRIPT LANGUAGE="VBScript"> Dim x x = "aa" MsgBox x </SCRIPT>
見事に一見では無意味な形にしてくれる。
<SCRIPT LANGUAGE="VBScript.Encode">#@~^MAAAAA==@#@&d7ifb:PX@#@&7di6~',JCCr@#@&i7dt/o~GaPX@#@&d7VAcAAA==^#~@</SCRIPT>
たぶんシンタックスまで見てエンコードしてないだろう(単にスクリプト部分を文字列としてエンコードしているだけ)と思うので、以下のように単純なパターンを変えてエンコードしてみる。
<SCRIPT LANGUAGE="VBScript">a</SCRIPT> ↓ <SCRIPT LANGUAGE="VBScript.Encode">#@~^AQAAAA==CYQAAAA==^#~@</SCRIPT> <SCRIPT LANGUAGE="VBScript">b</SCRIPT> ↓ <SCRIPT LANGUAGE="VBScript.Encode">#@~^AQAAAA==8YgAAAA==^#~@</SCRIPT> <SCRIPT LANGUAGE="VBScript">c</SCRIPT> ↓ <SCRIPT LANGUAGE="VBScript.Encode">#@~^AQAAAA==^YwAAAA==^#~@</SCRIPT> <SCRIPT LANGUAGE="VBScript">ab</SCRIPT> ↓ <SCRIPT LANGUAGE="VBScript.Encode">#@~^AgAAAA==C(wwAAAA==^#~@</SCRIPT> <SCRIPT LANGUAGE="VBScript">abc</SCRIPT> ↓ <SCRIPT LANGUAGE="VBScript.Encode">#@~^AwAAAA==C(mJgEAAA==^#~@</SCRIPT> <SCRIPT LANGUAGE="VBScript">abcd</SCRIPT> ↓ <SCRIPT LANGUAGE="VBScript.Encode">#@~^AwAAAA==C(mJgEAAA==^#~@</SCRIPT>
頭の「#@~」から「==」まではの12バイトはヘッダっぽい。末尾にもフッタのようなものがついている。これも12バイトだとしてこれらを無視すると、「a」→「C」、「b」→「B」、「c」→「^」と単純な1バイトごとの変換か?いや、「ab」→「C(」、「abc」→「C(m」となっているので、そんな単純な話ではなさそうね。
abcde → C(m[ abcdef → C(m[0 abcdefghijklmn → C(m[0TtkN3^hx
こう見えると全体の長さは変えてないようで何となく規則性は見受けられる。でも「a」→「C」、「b」→「(」という単純な変換ではない。
bca → 81l cba → ^(l cab → ^m4
になるから。先頭に来る文字は1文字で変換したものと同じだ (「a」→「C」、「b」→「8」、「c」→「^」)。「b」が2文字目にくる場合は「(」だ。「a」が2文字目にくる場合は「m」だ。では.....
aaa → Cml bbb → 8(4 ccc → ^1m
お、やはり。もしかして、何か決まったパターンと論理演算しているのでは?引き続き...
aaaaaa → CmlCml bbbbbb → 8(48(4 cccccc → ^1m^1m aaaaaaaaa → CmlCmlmll bbbbbbbbb → 8(48(4(44 ccccccccc → ^1m^1m1mm aaaaaaaaaaaaaaaa → CmlCmlmllmlmClml bbbbbbbbbbbbbbbb → 8(48(4(44(4(84(4
やはりそうっぽい。ではマスクしているビットパターンは何だろう。
「a」→「0x61」→「0110 0001」 XOR 0010 0010 「C」→「0x43」→「0100 0011」 「b」→「0x62」→「0110 0010」 XOR 0001 1010 「8」→「0x38」→「0011 1000」 「c」→「0x63」→「0110 0011」 XOR 0011 1100 「^」→「0x5E」→「0101 1111」
あああああ....一致しない......微妙にマスクするビットパターンを変えているのか。
SD_BOTの検体とウィルススキャン信者
ウィルスの検体を入手した。Trend Micro ServerProtectが検知したらしく、それによれば「SD_BOT.CD」とのこと。オンラインスキャンで検知したらしいが、マシンの挙動はすでにおかしいらしい。つまり、もう感染してしまっているようだ。
送ってもらった検体のファイル名は「73.scr」。スクリーンセーバーだ。スクリーンセーバーっていっても実体はただのEXEだ。つまり、「スクリーンセーバーが起動する」ということは、なにもスクリーンセーバー「プレイヤー」が「.scr」を実行しているわけではなく「.scr」自身が起動しているにすぎない。メールの添付とかダウンロードとかで「.scr」を入手したときに、EXEじゃないからといって、うっかり実行してしまってはいけない。このあたり、意外と知られていないのでは?
で、話を元に戻すと、実行形式である限りファイルの先頭にはお決まりのパターンがついているはずだ。たとえば「メモ帳」をバイナリエディタで開いてみると、MZ...PE...というお決まりのパターンが現れる。
MZシグニチャとPEヘッダ
http://codezine.jp/article/detail/412?p=1
でこの検体もプログラムである以上、このようなパターンが必ず含まれているはずだ。「73.scr」をバイナリエディタで開いてみた。
むむむ。PEヘッダなんかついていない。PACK(実行時圧縮)かけられているようなものともなんか違う。いままで、PACKされているプログラムを何回か覗いてみたが、少なくともPEヘッダは認識できた。まったく見たことがないパターンに見える。
これ、検体の送付ミスか?または、ServerProtectの仕業で無効化されているとか?
VIRUSTOTALにかけてみた。Trendのエンジンも然り、ぜんぜんヒットしないやんけ。1/39だって。Kasperskyも「Generic」としているだけで正体は分からないようだ。
もう一度、検体のバイナリパターンを見てみる。妙に「0xff」が多い。
EXEファイルとかってパディングが多い。特にPEヘッダはデータ幅を32ビット幅に揃えるためにパディングされている箇所が多いようだ。なので、「0x00」が連続して出現するのはよく見てきた。しかし、このファイルは「0xff」が連続している箇所が多い。ということはビットが反転されているのでは?ビット反転したEXEがそのまま動くとは思えないんだが........
まぁ、とりあえずビットを逆さにするプログラムをつくってこの検体を食わせてみた。以下が出力。たいぶ読めそうなパターンになった。
「MZ..PE..」のお決まりパターンも出現したが、何やらその前に「VSBF」なる文字から始まるヘッダっぽいものがついている。パスらしき文字列も出現している。これはなんだ?もう一度、VIRUSTOTALにかけてみた。結果は0/39。Kasperskyまでもヒットしなくなった。
どうも「VSBF〜」の正体が不明なので、このヘッダ部分をカットしてみよう。通常の「MZ..PE..」から始まるパターンにする。この部分をカットするプログラムをつくって食わせてみた。食わせてみたところ、突然、ローカルのAvastが反応した。おっと。
まぁ、実行したわけではないので、とりあえず、ここは何もせずにそのまま出力。で、もう一度、VIRUSTOTALにかけてみた。
来た来た。TrendのエンジンもSD_BOTだと言っている。Kasperskyに言わせるとこれは「EPProt」なるものでさらにPackされているらしい。ほんとに解析させないようにあの手この手だね。「EPProt」なるをアンパックする方法が見当たらないのでこれ以上の静的解析は無理か。
感染したマシンではファイルが置かれたときに検知したわけではなくオンメモリ上にロードされて初めて検知したと思われる。つまり、もう実行されてしまったわけだ。「オンラインスキャンで捕まえた」→「実行前だから安心」は成り立たないね。
そもそも、こんなあの手この手の難読化やPACKをかけれらたらシグニチャマッチングなんで土台無理。破たんは目に見えて明らか。今回のウィルスが感染した環境では未パッチ状態が長く続いていたらしい。ウィルススキャンが入っているから大丈夫だと。
ウィルススキャンの過信はほんとに禁物なんだ。これを分かっていない人がいまだに多すぎる。パッチをあてることで大部分のウィルスインシデントが防げるはず。パッチをあてることをまず考えずに安易にウィルススキャンに頼る。パッチコントロール運用なんて端っから検討されていないことが多いようだ。まず検討するだけでもいいので考えることを始めてみようよ。
それにしても、この73.scrについてふたつの疑問が残る。
1.ビット反転したEXEイメージは実行できるのか
2.ファイルの先頭にあった「VSBF」ヘッダはなんなのか
もしかしたら別の親プログラムがいるのかな?73.scrはこの親プログラムからロードされて実行されるなんてことも考えられそうだ。1についてはサンドボックスで実行してみればいいんだけどあいにく使える環境が手元にない。
USBフラッシュメモリの挿入を検知する
いまだに驚異になっているリムーバブルメディアで感染を広げる「MAL_OTORUN」。トレンドマイクロの3月インターネット脅威リポートにおいても8か月連続で被害報告数ベスト(ワースト?)1。大流行のConfickerもリムーバルメディア経由での感染機能を持っている。
マルウェアの実行はautorun.infを用いて自動実行するのは周知のこと。では、感染活動のほうに目を向けるとどうなのか。つまり、USBフラッシュに自分自身をコピーするのはどうやってるんだろう。プログラム側でUSBフラッシュが接続されたことを認識する必要がある。
実はこれは至って簡単で、Windowsは、USBフラッシュメモリの挿入を含めHWデバイスに変更が発生すると、すべてのアプリケーションに対して、「WM_DEVICECHANGE」メッセージを送信する。プログラム側でイベントハンドラを定義してこのメッセージを受け取った時の処理を記述すればいい(ファイルをコピーするとか)。
やってみた。
ダイアログベースのアプリケーション(DetectUSBDrive)を作成。直接処理とは関係ないがワームっぽくするためにダイアログ自体を非表示にする。
これは OnWindowPosChanging() をオーバーライドして、以下のようにとするだけでいい。これでプログラムを実行してもウィンドウは画面上に表示されない。
void CDetectUSBDriveDlg::OnWindowPosChanging(WINDOWPOS * lpwndpos)
{
lpwndpos->flags &= ~SWP_SHOWWINDOW;
CDialog::OnWindowPosChanging(lpwndpos);
}
WM_DEVICECHANGEメッセージ処理関数(今回はOnMyDeviceChange)をメッセージマップを追加。ヘッダファイルにもオーバーライド定義。
BEGIN_MESSAGE_MAP(CDetectUSBDriveDlg, CDialog)
ON_MESSAGE(WM_DEVICECHANGE, OnMyDeviceChange)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_WINDOWPOSCHANGING()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
Dbt.hのincludeを忘れずに。
#include <Dbt.h>
メッセージ処理関数 OnMyDeviceChange() の本体について、単にHWデバイスが追加されたことだけ拾えればいいのであれば以下のようになる。
LRESULT CDetectUSBDriveDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam) { CString mes = _T(""); switch(wParam) { case DBT_DEVICEARRIVAL: mes = _T("デバイスが使用可能になりました"); break; } AfxMessageBox(mes); return 0; }
ここまでで、とりあえずプログラムを実行してUSBフラッシュを差してみた。ちゃんと検出した。
ちなみにイベントの種類はほかにもあって、この場合、wParamには以下のような値が入ってくる。
DBT_DEVICEARRIVAL デバイスが挿入されていて、使用できる状態です. DBT_DEVICEQUERYREMOVE デバイスを削除するための許可が要求されています. DBT_DEVICEQUERYREMOVEFAILED デバイスを削除する要求がキャンセルされました. DBT_DEVICEREMOVEPENDING デバイスが削除されようとしています.拒否できません. DBT_DEVICEREMOVECOMPLETE デバイスが削除されました. DBT_DEVICETYPESPECIFIC デバイス固有のイベントです. DBT_CONFIGCHANGED 現在の設定が変更されました. DBT_DEVNODES_CHANGED デバイス ノードが変更されました.
Device Management Events
http://msdn.microsoft.com/en-us/library/aa363232(VS.85).aspx
検知できることは分かったがそのほかの詳細情報は取得できないものか。ファイルをコピーするんだったら、少なくともマッピングされたドライブ文字くらいは知りたい。
実はこれらの情報 DEV_BROADCAST_USERDEFINED 構造体を辿るとことで分かる。そしてイベント処理関数に渡ってくるlParamにはこの構造体のポインタが格納されている。
DEV_BROADCAST_HDR Structure
http://msdn.microsoft.com/en-us/library/aa363246(VS.85).aspx
そしてこの構造体の dbch_devicetype にはイベント特有の情報が含まれていて、今回の場合ドライブ文字を取得したいので、dbch_devicetype が DBT_DEVTYP_VOLUME であるイベントを取得する。こんな感じ。
で、DEV_BROADCAST_VOLUME構造体 の dbcv_unitmask にドライブ文字を指定する情報が格納されている。これはDWORD型で0bit目が立っていれば「A」1ビット目が立っていれば「B」.....ということ。
DEV_BROADCAST_HDR* pHdr = (DEV_BROADCAST_HDR*)lParam; switch(pHdr->dbch_devicetype) { case DBT_DEVTYP_VOLUME: { DEV_BROADCAST_VOLUME *pVol = (DEV_BROADCAST_VOLUME*)pHdr; if(pVol) { CString str; str.Format(_T(":%d"), pExtend->dbcv_unitmask); mes += str; } } break; }
まとめると、ものすごく簡単だが、デバイスの挿入を検知してマップされた論理ドライブ文字を取得するためだけの機能を持った OnMyDeviceChange() は以下のようになる。
LRESULT CDetectUSBDriveDlg::OnMyDeviceChange(WPARAM wParam, LPARAM lParam) { if(wParam == DBT_DEVICEARRIVAL) { DEV_BROADCAST_HDR* pHdr = (DEV_BROADCAST_HDR*)lParam; if(pHdr) { if(pHdr->dbch_devicetype == DBT_DEVTYP_VOLUME) { DEV_BROADCAST_VOLUME* pVol = (DEV_BROADCAST_VOLUME*)pHdr; if(pVol) { CString mes; mes.Format(_T("Detect->[%ld]"), pVol->dbcv_unitmask); AfxMessageBox(mes); } } } } return 0; }
実行してみた。
4096 は 2の12乗なので「M」を表す。実際にMドライブにマップされている。
ちゃんちゃん。特にまとめはない。だからなんだ?って感じになってしまった。
スパムでばらまいたりWebにホスティングしたりすることに比べて、一気に爆発的に増殖数を増やすことはできなさそうだけど、増殖方法としてはお手軽でいいかもね。
レガシーASPと”Cookie: = ”
超古い穴だらけのASPアプリケーションに某WAFを導入してAppScanかけてみた。まぁ、その某WAFの評価のためなんだが。
やってみた結果、ほぼパーフェクトな検知率。でも、ほんの一部漏れたものがある。つまり、AppScanは「攻撃リクエストだ」と称しているリクエストのほんの一部が通過した。どう見ても攻撃が成立するとは思えないが。
(まぁ、いちがいにAppScanのシグニチャがぜんぶ攻撃リクエストだとは思わないけど。つまり、単にテストパターンとして含めているシグニチャもあると思うので。)
で、その中のひとつのリクエストがこれ。
GET /AAAA/BBBB.asp HTTP/1.0 Accept: */* Accept-Language: ja User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;.NET CLR 2.0.50727; .NET CLR 3.0.04506.30) Host: 192.168.11.142 Connection: Keep-Alive Cookie: =
Cookieに「=」のみを設定している。これがなんだっていうのか?これで攻撃が成立するんか?
確かに実装の仕方によってはASPはCookieの値をHTTPパラメータとして扱ってしまうという話はあれど。
AppScanに言わせるとこのリクエストを投げた結果、応答としてDBのエラーを返したからダメだとのこと。
これがレスポンスの一部。
Microsoft OLE DB Provider for ODBC Drivers エラー'80004005' [Microsoft][ODBC Microsoft Access Driver]一般エラーレジストリキー'Temporary (volatile) Jet DSN for process 0xbe4 Thread 0xcd0 DBC 0x23e6864 Jet' を開くことができません。 /AAAA/BBBB.asp, 行 34
まぁ、確かにDBのエラーを出している(ここから分かるのはDBにAccessを使っていることぐらいか)。
エラーメッセージの内容については、調査してみるとどうやらDBが開けんということらしい。
試しにAppScanが投げたリクエストをこのアプリに投げてみた。今回はburpsuiteをクライアントに入れてリクエストをトラップ。
ヘッダのCookieを「=」にして送信した結果たしかに↑のエラーが出る。
このアプリケーションは超基本的な実装だけの超シンプルな掲示板(セキュリティ的にはザル)。
DBはMDBを使用していて、ユーザ認証も設定しておらず、.aspと同じフォルダに.mdbを配置している(つまり公開フォルダに配置している)。
DBのオープン処理は以下でODBC経由で接続している。
: 31: strProvider="Driver={Microsoft Access Driver (*.mdb)}; DBQ=" & FileName 32: Set Conn = Server.CreateObject("ADODB.Connection") 33: Conn.Open strProvider 34: 35: HOST = request("REMOTE_HOST") : :
Sessionオブジェクトなんか使ってないので、ASPSESSIONIDに影響を与えるような処理は一切していないし、別途Cookieの値を設定したり読んだりもしていない。MDBに認証機構を設定していなし.mdbファイルのACLはEveryone-フルコントロール。
どうみてもCookieがDBのオープンに影響するとは思えないのだが。
以下のような一見不完全なCookieを設定してみても、例のエラーは出ず正常にページが表示される。
・Cookie:ASPSESSIONID=
・Cookie:
・Cookieなし
「Cookie: =」のときだけDBエラーが出る。
アプリケーションのDBオープン部分をコメントアウトして「Cookie: =」に設定してリクエストを投げてみた。
: : 31: 'strProvider="Driver={Microsoft Access Driver (*.mdb)};DBQ=" & FileName 32: 'Set Conn = Server.CreateObject("ADODB.Connection") 33: 'Conn.Open strProvider 34: 35: HOST = request("REMOTE_HOST") : :
今度は次のようなエラーが発生した。
エラー '80004005' /AAAA/BBBB.asp, 行 35
エラー80004005。このエラーコードは情報の取得に失敗していることを示すらしい。
35行目は何かというと、
35: HOST = request("REMOTE_HOST")
リモートホストの取得に失敗だってさ。何のこっちゃ。Cookieの値がリモートホストの取得にどう関係するというのか。そもそもCookieの値がなくてもちゃんと動作しているのに。もちろんリモートホストの取得もできている。
要するにASPエンジンのCookieを含むHTTPヘッダ解析処理のバグなんじゃないのかしら?
「Cookie: =」設定すると落ちるということは分かった。
AppScanのレポートはアプリ側を修正しろなんて書いてあるが、アプリ側ではどうしようもないと思われる。結果的に、もうレガシーASPなんて使うなってことだね。
コーデックがないと偽ってウイルスをダウンロードさせる
職場の同僚がBitTorrentからウィルスを拾ってきた。ウイルスの種類はこれ。
http://canon-its.jp/product/eset/topics/malware0811.html
WMA/TrojanDownloader.GetCodec.Gen はメディアファイルを改ざんするマルウェアです。PC上で見つかったすべてのオーディオファイルをWMA形式に変換し、ユーザーの誘導先URLを指すフィールドをヘッダに追加します。そして、メディアファイルを再生するにはそのURLからコーデックをダウンロードする必要があるというメッセージを表示します。WMA/TrojanDownloader.GetCodec.Genは、Win32/GetCodec.AなどのGetCodecの亜種による感染を支援するWimad.Nと深い関わり合いを持つダウンローダーです。
とのこと。最近、バイナリエディタと仲良しなので面白そうだと思って。できるところまでやってみた。
まず、WMAファイルの仕様があるかどうかを調べてみた。
うーむ、見つからない。
仕様書そのものは見つからなかったけど、WMAファイルについて調べていると実は、WMAファイルのフォーマットは、ASFファイルフォーマットをベースにしているらしい。
初めて知った。
でもって、ASFのファイルフォーマットはMicrosoftから公開されている。
http://www.microsoft.com/windows/windowsmedia/forpros/format/asfspec.aspx
う、英語だ。つらい。
まぁ、とりあえず概要だけでもと思って読み始めてみる。構造体の羅列。そしてオブジェクトの種類ごとに固定GUIDが振られている。そのGUIDは必ずオブジェクトの先頭に付加されているので、
ファイルをそのGUIDで検索すれば、それ以降が実際のデータの中身ということだ。
今回、ストリームデータそのものは特に関係ないと思ったので、メタデータ的なオブジェクトに絞って、ファイル内をGUIDで検索。......でもあまりにその数が多い。でも、だいぶ読み方がわかってきた。
そこで、外部から何やらダウンロードしてくるという動作から、中にURLを持っているはずだと思って、ファイル内を検索してみる。すると、2つのURLらしきものがヒットした。
1.www.773four.com
2.dudethisishowwedoitallnightlong.2myip.net/
1は実際にあるレコードレーベル。問題なさそうだ。名前からしても2が怪しい。
"dude this is how we do it all night long"だって。
なので、この2のURLの記述がある周辺を当たってみた。GUIDでオブジェクトを指定していると言ったが、仕様書と見比べながら、よーく見てみると、URLの記述のちょっと上にそのGUIDが存在する。
これは、仕様書によると、「ASF_Script_Command_Object」と言って、WMAファイルに埋め込めるスクリプトの情報が含まれているらしい。これはいよいよ怪しい。
ちょうどここからが、コマンド文字列。
「URLANDEXIT」?なんのこっちゃ。
Webで、WMAスクリプト+URLANDEXITで検索してみたところ、山ほどヒットした。ほとんどがマルウェア関連。
けっこうこのメディアファイルを使用したマルウェアダウンローダの解析をしている人はいっぱいいた。
http://www.docstoc.com/docs/3715914/sc-sep-08
どうやら、URLANDEXITコマンドはMediaPlayerへダウンロードを行うように発行するコマンドらしい。これは、いささかやばいと思ったのか、マイクロソフトがこのコマンドの有効/無効を切り替えるパラメータを外出し(レジストリ)にしてた。
http://support.microsoft.com/kb/828026
URLAndExitCommandsEnabled : URLAndExit スクリプト コマンドを有効にするかどうかを設定します。デフォルト値は 1 (有効) です。
どうも、MediaPlayerが不明のコーディックをダウンロードする処理を不正に利用しているのではなく、さも、そのように見せかけて、単にWMAスクリプトを実行しているだけのような気がする。
そもそも、WMAにスクリプトを埋め込めること自体知らんかった。知っていれば、Mediaファイルエディタなんかで開いてみれば一発でわかったかも。
ちなみに、ASF_Script_Command_Objectはひとつしか存在できず、その中に複数のコマンドを入れるらしい。今回の場合は、Commnads Countというパラメータの値が「1」だったので、そのほかのスクリプトは入ってなさそうだ。もっとほかに隠し機能があるかもしれないけど。
GIFARの脅威って
GIFARについてやや勘違いをしていました。GIFARファイルをブラウザで表示するだけで、含まれているアプレットが動くのかと思っていました。
これってけっこう勘違いされてそうな気がするんだが...
え?当たり前?もしかして自分だけ?
指摘されているのは「jarファイルをGIFファイルとして偽装できる」というだけであって、ブラウザの脆弱性とかを指摘しているわけではないんだ。
実際にBlack Hat Japan 2008 でGIFARについて発表したネイサン・マクフィーターは、
もし、このGIFARファイルを攻撃対象のコンピュータのローカルファイルシステムに送り込むことができ、そのファイルが格納されたパスをあらかじめ知ることができるならば、Webページなどを通じて被害者のコンピュータ内にあるGIFARファイルをJARファイルとして起動することができる。
と言っている。
でもよく考えたら、もしGIFARファイルをブラウザで表示させるだけで、Appletが動くんなら被害は甚大でもっと大騒ぎになってそうだ。
まず、ものすごく単純なApplet (AppletTest1.class)をつくってみた。これは単にダイアログを表示するだけのもの。これを Applettest.jar で固め、サーバに配置して以下のようなHTMLを作成してブラウザからアクセス。
結果はこうなる。
では、これをGIFARにしてみよう。earth.gifという地球の画像にこのjarのバイナリパターンを追加。
ファイルの先頭は「GIF89a」から始まっていて、これはGIFファイルであることを示している。
そして、ここからがjarの実体。「PK」から始まっている。
実際、これはjarとして機能する。さきほどのHTMLを以下のように、archiveのパラメータをearth.gifに書き換えてみて、ブラウザからアクセスしてみてもさきほどと同じ結果が得られる。
次にHTMLを以下のように書き換えて、ただ画像を表示するだけにしてみる。
結果ただ画像が表示されるだけ....
あー、そうか。ここまで来てようやく気づいた。Appletタグがないので、ブラウザはアプレットとして動作させていないんだ。そりゃあ、そうか....ここまでしなきゃ気付かないのはバカすぎた。
要するに画像ファイルを偽装したjarファイルを、クライアントのキャッシュに保存される、あるいは、ユーザが明示的に画像と思って保存されることが問題なのであって、GIFARファイルののみで何かするわけではないんだ。複合的にその他の脆弱性とあいまって実際に驚異になるわけだ。
つまり、GIFARは「悪意あるコードを送り込む」方法としての一手法ということか。
ネイサン・マクフィーターは
Webページなどを通じて被害者のコンピュータ内にあるGIFARファイルをJARファイルとして起動することができる。
といっているがこれは一体どうやるのだろう。