ActionScriptバイトコードをJIT化したネイティブコードのアドレスを特定する

これはカーネル/VM Advent Calendar 2013の記事です。

はじめに

セキュリティ関連の話です。今やソフトウェアセキュリティの分野としては
OSやコンパイラのセキュリティ機構(例えばDEP+ASLR)が充実してきていますので、
一昔前に比べると攻撃しづらい、成功しづらい状態になっています。


こういうセキュリティ機構を回避するために、近年はデータファイル(PDFなど)
が攻撃に使われていて、最近ではデータファイル系も
セキュリティ機構(例えば、Adobe Readerサンドボックス)が充実しつつあります。

他のデータファイルに比べると対応が遅れているFlashファイルが2010年くらいから
攻撃に使われるようになりました。きっかけは2010年にBlackHat DCで発表された
JIT Sprayで、DEP+ASLRを回避できる画期的な攻撃手法でした。

これは既に最近のFlash Playerでは対策が入っています。

ここ数年様々なFlash Playerの脆弱性が発見されては、対策されている状態ですが
2013年に入っても脆弱性は発見されており、PoC(Proof of Concept)も出回っているようです。

これらの状況を追うためにはExploit解析するのが一番よいのですが、どこから手をつけたら
いいのかという感じです。Exploit解析のためにはネイティブコードを追う必要がありますので
この記事ではExploit解析の一つの山場となるswfファイルからネイティブコードに変換される
部分を説明したいと思います。

本題

Flashの脆弱性が発見されて、攻撃コード(およびPoC)が出回った時、どういうExploitテクニックが使われているか気になりますよね。
そういうときにswfファイルに含まれているabc部分をデコンパイルするとActionScriptとして内容を追えるのですが、
コンパイラも完璧ではないし、そもそもActionScriptで書かれた攻撃コード見てもわかりづらいし、詳細はわからないし
最終的にはネイティブコードとしてどうなっているか知りたくなります。


f:id:drwtsn64:20131218110509p:plain


まずはswfファイルがどういうふうに実行されるかを確認します。

ActionScriptをビルドしてswfファイルを作るのですが、実装部分はswfファイルにバイトコードとして存在してます。
swfファイルを再生するとFlash PlayerがswfファイルをパースしてAVMという仮想マシン上でバイトコードを実行します。
AVMにはJITコンパイラ(nanojit)が含まれていて、バイトコードは適宜ネイティブコードに変換されて実行されます。
ここで追いたいのはそのネイティブ部分です。


AVMがバイトコードをネイティブコードに変換して実行するまでにはいくつかフェーズがあります。
1. バイトコードを検証して、不正部分があれば終了
2. バイトコードをLIRに変換
3. nanojitがネイティブコードに変換

アーキテクチャはこちらで詳しく説明されています(記事書いてるときに見つけました)
steps to phantasien t(2008-05-06)


あ、あとFlash PlayerをIDAとかで見ていってもいいのですが、さすがにつらいので
TamarinというAVMのオープンソース実装も併用します


では、追っていきましょう


まずはじめに、swfファイルにmethod_bodyという部分があり、そこでバイトコード部分を管理しているようです。

f:id:drwtsn64:20131218113714p:plain

この部分がAVM上でどうなっているかというと
パースされたあと、MethodInfoクラスで管理されます。
結論からいうと、nanojitでネイティブコードに変換された後のアドレスは

MethodInfo._implGPR

に保存されるので、そこが書き変わるとこをブレークポイント
設定して見てればいいということになります。


MethodInfoが作られたあと、バイトコードの検証に移ります。

MethodInfo.verify() {
...
// 長いので省略
...
}

ここで型のチェックなどが行われます。ここでチェックに漏れると
atom confusionとかtype confusionと呼ばれる脆弱性の原因になります


検証を追えるとnanojitに受け渡すため、バイトコードをLIRに変換します

interpBoxed() {
...
// 長いので省略
...

nanojitを使いネイティブコードに変換します

CodegenLIR::emitMD() {
...
        assm->beginAssembly(frag);
        assm->assemble(frag, &bufreader);
        assm->endAssembly(frag);
...
            union {
                GprMethodProc fp;
                void *vp;
            } u;
            u.vp = frag->code();
            info->setNativeImpl(u.fp);
            // mark method as been JIT'd
            info->_flags |= MethodInfo::JIT_IMPL;
...
}

info->setNativeImpl()でMethodInfo._implGPRに
ネイティブコードのアドレスが保存されます


ネイティブコードが実行されます

MethodInfo::verifyCoerceEnter() {
...
return f->invoke(env, argc, args);
}


変換されたネイティブコードはこんな感じです

...
00CAFEF1  mov         dword ptr [ebp-88h],ecx 
00CAFEF7  mov         dword ptr [ebp-84h],edi 
00CAFEFD  mov         dword ptr [ebp-80h],eax 
00CAFF00  mov         dword ptr [ebp-7Ch],0 
00CAFF07  mov         eax,dword ptr [ebx] 
00CAFF09  push        esi  
00CAFF0A  push        3    
00CAFF0C  push        ebx  
00CAFF0D  call        eax  
00CAFF0F  add         esp,0Ch  
...            


これでネイティブコードに行き着けたので、Exploit解析に入れますね
Exploit解析はそれはそれで大変なのですが、ひとつの山場は超えました
ので、とりあえず目的達成です

注意する点

Flash Playerの実装はTamarinを使っていますが、拡張されていたりして、
データ構造やフローが異なることがあります
例えば、Flash Player 11.3.270.ocxでは
MethodInfoクラスの_method_idや_flagsのオフセットが違います

付録

解析の方法

swfファイルを使った攻撃パターンですが
swfファイルをサーバーに設置して、IEで閲覧とか
swfファイルをMS Officeに埋め込むことができるので、
Wordに脆弱性のあるswfファイル埋め込んで、メールに添付とか
がよくあると思います。

でも、解析する時にIEやWordがswfファイルを開いて攻撃コードを展開するとこまでデバッガで
追ってると死にます。IEもWordもサイズが大きいのでデバッグできる環境を整えるのも大変です(貧弱な環境だとすぐ固まります)。
それにIEはマルチプロセスだし、マルチプロセスをソースなしにデバッグとか
考えただけで恐ろしいです。

Adobe Flash Playerサポートセンターってとこで
スタンドアロンのFlash Playerをバージョンごとにダウンロードできるので、これを使うのがおすすめです。
ActiveX版を解析したい場合は、ocxファイルを使うexeを自分で書いてswfファイルを読み込ませる必要があります。
あと、Debug版はExploitが動かない場合があります(アンチデバッグ)。