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で書かれた攻撃コード見てもわかりづらいし、詳細はわからないし
最終的にはネイティブコードとしてどうなっているか知りたくなります。
まずは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という部分があり、そこでバイトコード部分を管理しているようです。
この部分が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が動かない場合があります(アンチデバッグ)。