ハイパフォーマンス・ファジング · 2015-11-07 · →richard johnson / @richinseattle...
TRANSCRIPT
ハイパフォーマンス・ファジング
Richard Johnson | PacSec 2015
はじめに
自己紹介→Richard Johnson / @richinseattle→Research Manager, Vulnerability Development→Cisco, Talos Security Intelligence and Research Group
アジェンダ→なぜパフォーマンスが重要か→ターゲットと入力の選定→エンジンのデザイン→ホストの設定
なぜパフォーマンスが重要か
なぜパフォーマンスが重要か
突然変異型のファジングはたいてい簡単にみえる→ただし、問題発生時にハードウェア例外
CPUサイクルの大半が無駄になっている→プログラムのロード時間 対 ファイルのパース時間→ファジングには高I/O、ブロッキングCPUが必要→巨大ファイルでの突然変異は非効率的
ファザーの設計を定性的に分析ファザーの戦略を定性的にに分析
Microsoft SDL 検証ガイドライン
ファジングはSDLC検証において必須要件
「コードのパースにおいて入力が信頼境界を越えている可能性がある場合には、そのコードにおいてファイルファジングを行う必要があります。セキュリティ開発ライフサイクル(SDL)のバグバーで説明しているように、全ての問題点が修正されなければなりません。推奨されるツールによって、全てのファイルパーサーがファジングされる必要があります。」
https://msdn.microsoft.com/en-us/library/windows/desktop/cc307418.asp
Microsoft SDL 検証ガイドライン
ファジングはSDL検証において必須要件
「Win32/64/Mac: 最適化されたテンプレートのセットを使う必要があります。テンプレートの最適化は最小のテンプレートによるパーサのコードカバレッジの最大数の総量に基づいています。最適化されたテンプレートはファジングの有効性を倍増させることが研究で示されています。SDLバグバーでは、最後のバグが見つかり修正されてから、最小の50万の反復、少なくとも 25 万 の 反 復 の フ ァ ジ ン グ が 見 ら れ ま す 。 」
https://msdn.microsoft.com/en-us/library/windows/desktop/cc307418.asp
Microsoft SDL 検証ガイドライン
ファジングを必須にすることは良いことどのように調整したか?
→反復は現実的なリソースによって制限される→複雑なパーサーはより多くのリソースを必要とする→反復はガイダンスの定義においてはよくない選択肢
どのような属性が利用可能なリソースでの理論的な限界を決定するのかファジングの有効性を最適化するベストプラクティスは何か
過去のパフォーマンス統計
Microsoft Windows Vista 2006→3.5億の反復、250以上のファイルパーサー
l~ パーサーあたり140万の反復(平均)→300超の問題が修正 (1バグあたり 116万のテスト)
Microsoft Office 2010 →8億の反復、400のファイルパーサー→1800のバグが修正 (1バグあたり44444のテスト)
•http://blogs.technet.com/b/office2010/archive/2010/05/11/how-the-sdl-helped-improve-security-in-office-2010.aspx
チャーリー・ミラー 2010→700万の反復、4パーサー
•~ パーサーあたり180万の反復 (平均)→320 - 470 のクラッシュ (1バグあたり14893 - 21875のテスト)
過去のパフォーマンス統計 (cmiller)
チャーリー・ミラーは意図的に悪いデザインで行った→入力を変異させる5行のPython→システムハンドラでファイルを反復するAppleScript→Microsoft minifuzz も同様に愚直である
入力の選択→コードカバレッジの最小セットによって8万のPDFが1515に減少
入力 ソフトウェア 数 平均時間
PDF Adobe Reader 9.2.0 3M 5.35s
PDF Apple Preview (OS X 10.6.1) 2.8M 7.68s
PPT OpenOffice Impress 3.3.1 610k 32s+
PPT MSOffice PowerPoint 2008 Mac 595k 32s
ターゲットと入力の選定
ターゲットの選定
64-bit 対 32-bit アプリケーション (x86 アーキテクチャ)→64-bitバイナリは32-bitよりサイズが肥大→64-bitランタイムのメモリ使用量は32ビットより多い→64-bit OSはVMのためにより多くのメモリとディスクを消費
→いくつかのソフトウェアは32-bitバイナリのみ→いくつかのファザーとデバッガは32-bitのみをサポート
→64-bit CPUは性能向上のためにより多くのレジスタを持つ•最適化はコンパイラに依存
ターゲットの選定
64-bitプログラムは高速か?→x64上では? 小さいながらも変化がある
•Chrome – 無視できる程度–http://www.7tutorials.com/google-chrome-64-bit-it-better-32-bit-version
•Photoshop – イエス? –8-12% (無関係なディスクI/Oの最適化に関するトーク)–https://helpx.adobe.com/photoshop/kb/64-bit-os-benefits-limitations.html
→SPARC上では? ノー•True story, but who cares
–http://www.osnews.com/story/5768/Are_64-bit_Binaries_Really_Slower_than_32-bit_Binaries_/page3/
ターゲットの選定
はるかに重要: コード行を最小限に→プログラムを初期化し実際にパーサーコードの実行に費やされる時間の比率
最適化の戦略→ターゲットライブラリのディレクトリ→APIごとに薄いラッパーを書く
•機能ごとにターゲットとする
→高コストなチェックサム/圧縮を除くパッチを書く•Flayerが詳しい (Drewery & Ormandy WOOT'07)
→ターゲットをメモリ内でファジングできるよう設計
入力の選定
入力は数値セット入力パーサはステートマシンである(べき)
→FSMによって記述された仕様→通常、パーサコードは現実にはFSMでは実装されていない→ハイパフォーマンスなFSMパーサにはLangSecのペーパー
•http://www.cs.dartmouth.edu/~pete/pubs/LangSec-2014-fsm-parsers.pdf
ゴール: 空間の検索と新しい遷移の発見個別の検索は計算コストが高い
→我々は時間のために最適化が必要
入力の選定
入力の選定の最適化→ファイルサイズは非常に重要
•小さい入力サイズでは変異はより意味がある•小さい入力は読み込みとパースがより高速•いくつかのテスト生成方法は入力バイト当たりのメモリを大量に使用する
→入力に応じた特徴セットにより対象へフォーカス可能•手作りまたは最小化されたサンプル•ファジングのフィードバックやconclic testは異なる機能を持つ固有の小さな入力の生成を自動化する
入力の選定
CMU 変形→Optimizing Seed Selection for Fuzzing – USENIX 2014
•https://www.usenix.org/system/files/conference/usenixsecurity14/sec14-paper-rebert.pdf
→最小セットは期待ほど役に立たない→重みなしの最小セットが勝ち→結論: 最小セットは壊れていなければ良好
•Peach minset toolはアルゴリズムの最小セットではない•Peach minsetはランダム選定と同等に機能する
もう少しカバレッジトレーサーの性能について解説…
エンジンのデザイン
エンジンのデザイン
新しい入力の生成新しい入力でターゲットを実行障害状態の検出
エンジンのデザイン
新しい入力の生成新しい入力でターゲットを実行ターゲットの実行をトレーストレースの出力をモニター障害状態の検出障害ではない状態の検出
入力の生成
もっとも重要なのはミューテータの選定→AFL
Deterministic bitflip1, 2, 4, 8, 16, 32 bits
Deterministic addition/subtraction Values { 1 – 35 } for each byte, short, word, dwordLittle endian and big endian
Deterministic 'interesting' constant values 27 boundary values
Dictionary keywords Havoc
Random bitflips, arithmetic, block move/copy, truncateSplice
Merge two previously generated inputs
入力の生成
最も重要なのはミューテータの選定→Radamsa
ab: enhance silly issues in ASCII string data handlingbd: drop a bytebf: flip one bitbi: insert a random bytebr: repeat a bytebp: permute some bytesbei: increment a byte by onebed: decrement a byte by oneber: swap a byte with a random onesr: repeat a sequence of bytessd: delete a sequence of bytesld: delete a line
入力の生成
最も重要なのはミューテータの選定→Radamsa
lds: delete many lineslr2: duplicate a lineli: copy a line closebylr: repeat a linels: swap two lineslp: swap order of lineslis: insert a line from elsewherelrs: replace a line with one from elsewheretd: delete a nodetr2: duplicate a nodets1: swap one node with another onets2: swap two nodes pairwise
入力の生成
最も重要なのはミューテータの選定→Radamsa
tr: repeat a path of the parse treeuw: try to make a code point too wideui: insert funny unicodenum: try to modify a textual numberxp: try to parse XML and mutate itft: jump to a similar position in blockfn: likely clone data between similar positionsfo: fuse previously seen data elsewhere
Mutation patterns (-p)od: Mutate oncend: Mutate possibly many timesbu: Make several mutations closeby once
入力の生成
決定性ミューテータが最初並べ替えと無限のランダムモード現実的なレベルに順列をスタック
新しいミューテータの有効性の評価のためにフィードバックループが必要
ターゲットの実行
実行ループの使用は遅い→プロセス生成、リンク、初期化
fork() サーバを使用→初期化のスキップ→Copy-on-writeによるプロセス複製はLinuxでは高速→WindowsとOSXではプロセスメモリを手動コピー
•COWページより30倍以上のパフォーマンス向上
ターゲットの実行
Windows黒魔術のSUA posix fork() の余談→ZwCreateProcess (NULL, …) – Windows 2000
•セクション、スレッド、CSRSS、User32その他がない
→RtlCloneUserProcess – Windows Vista•限定的な範囲で動作•アプリケーションはWin32 APIを使えない
→RtlCreateProcessReflection - Windows 7•速やかなフルメモリダンプ生成のために設計•スレッドに復元できない
Windows 10 fork...
ターゲットの実行
fork、ふざけてるの??
linux10000 fork()
0.763s → 13106 exec/sec10000 fork/exec(/bin/false)
2.761s → 3621 exec/sec10000 fork/exec(/bin/false) w/ taskset
2.073s → 4823 exec/sec
cygwin 10000 fork()
29.954s → 333 exec/sec10000 fork/exec(/bin/false)
63.898s → 156 exec/secRtlCloneUserProcess (older hardware)
10000 fork()17.457s → 574 exec/sec
ZwCreateUserProcess...
Forkのデモ
ターゲットの実行のトレース
フィードバックループファジングは最終的にはAFLで実現→ファジング方針の定性的評価を可能に→計装方針の最適化→フィードバックシグナルの最適化→ソースコードのみ**
以前のバイナリフィードバックする方法では遅すぎた→EFSは過度に複雑でありPaiMeiを使用した→BCCFはCOSEINCコードカバレッジPintoolを使用→HonggfuzzはBTSを使用
ターゲットの実行のトレース
フックするエンジンの選定は重要→Pin / DynamoRIO は遅い
•** ブロックカバレッジでは5から10倍は遅くなる•フォークサーバのほうが利点がある
TurboTrace:
1. LD_PRELOADされたライブラリから自身をfork.2. フォークされた子をptrace.3. _startでbreak on4. fork()の繰り返しを行うことになる実際の関数をインジェクト.5. callを経由.6. _startを修正し実行を継続.
TurboTracer デモ
ターゲット実行のトレース
フックするエンジンの選定は重要→TurboTraceパフォーマンス, 100回の繰り返し
•20 – 50% スピードが向上
First test (without pintool, just instrumentation):Pin without pintool on test_png : 55.03 secondsTurbotrace without pintool on test_png : 37.24 seconds
Second test (bblocks pintool):Pin bblocks pintool on test_png : 72.62 secondsTurbotrace bblocks pintool on test_png : 51.07 seconds
Second test (calltrace pintool):Pin calltrace pintool on test_png : 106.19 secondsTurbotrace calltrace pintool on test_png : 85.24 seconds
ターゲット実行のトレース
フックするエンジンの選定は重要→QEMU
•QEMUによるユーザーランドのブロッキングトレース•スタティックにコンパイルされたバイナリ•Linuxのみ•Readpng: ~860 ex/s vs ~3800 afl-gcc – 4.5倍低速
→DynInst•スタティックなバイナリ書き換え•ダイナミックにコンパイルされたバイナリ•現在はLinuxのみ (windowsへのポーティング中)•Readpng: ~2400 ex/s vs ~3300 afl-gcc – 1.3倍低速
AFL-DYNINST デモ
トレース出力のモニター
ロギングは重要。トレーサーは大量にI/Oを処理→フィードバックシグナルに十分なものだけを保存
ブロックカバレッジは弱く、エッジ遷移がよいシェアードメモリの使用
cur_location = (block_address >> 4) ^ (block_address << 8);
shared_mem[cur_location ^ prev_location]++;
prev_location = cur_location >> 1;
障害/非障害の検出
障害→Linux
•#define WTERMSIG(status) ((status) & 0x7f)
→Windows •デバッガーが唯一の選択肢
非障害→タイムアウト
•自身の調整•最低限のタイムアウト,
→CPU使用量•Xミリ秒でCPU使用率が0付近まで落ちた場合
ホストの設定
システムキャッシュ
Windows →Windows 7以前ではファイルシステムのキャッシュは8MBのみ
•HKEY_LOCAL_MACHINE¥SYSTEM¥CurrentControlSet¥Control¥Session Manager¥Memory Management•LargeSystemCache = 1に値を設定
→ディスクのプロパティにて書込みキャッシュが有効化
システムキャッシュ
Linux →デフォルトで大容量システムキャッシュが有効→/sbin/hdparm -W 1 /dev/hda 書込みキャッシュの有効化→$ sysctl -a | grep dirty
•vm.dirty_background_ratio = 10•vm.dirty_background_bytes = 0•vm.dirty_ratio = 20•vm.dirty_bytes = 0•vm.dirty_writeback_centisecs = 500•vm.dirty_expire_centisecs = 3000
ストレージ: HDD
~100 MB/sキャッシュはプログラムから積極的に使われる
→Windows Superfetch (デフォルト)→Linux Preload
•http://techthrob.com/tech/preload_files/graph.png
その機能は低メモリしか使えないシナリオでもっとも有用
→通常、ファジングではVMごとに1-2GBのメモリ
ストレージ: HDD
ソリッドステイトUSBドライブのキャッシュ利用→利点は低レンテンシー、広帯域ではない→Windows ReadyBoost (デフォルトで利用可能)
•フラッシュでのランダムアクセスはHDDの10倍高速•http://www.7tutorials.com/files/img/readyboost_performance/readyboost_performance14.png
•まだキャッシュ用にデバイスを使っておらず、新しいデバイスが256MBから32GBのサイズで、4KBのランダム読込みで2.5MB/s以上の転送速度、512KBのランダム書込みで1.75MB/s以上の転送速度のとき
–https://technet.microsoft.com/en-us/magazine/2007.03.vistakernel.aspx
→Linux >3.10 bache / zfs l2arc•12.2K ランダム io/sec -> 18.5K/sec with bcache, 50% の増加
–http://bcache.evilpiepirate.org/
ホストの設定
Standard HDD Raid 0
ストレージ: SSD
HDDを上回る主要なパフォーマンスの向上
Raid 0 SSD
ストレージ: Ram Disk
SSDより高速、フラグメントの排除
→http://superuser.com/questions/686378/can-ssd-raid-be-faster-than-ramdisk (10GB/s - 17GB/s)
Linux - 内蔵→ramfsまたはtmpfs
Windows - 3rd party →差異が大きい
•https://www.raymond.cc/blog/12-ram-disk-software-benchmarked-for-fastest-read-and-write-speed/
→SoftPerfect RamDiskがフリーソフトウェアで秀逸
•https://www.softperfect.com/products/ramdisk/
ホストの設定
SSD Ramdisk
メモリー
32-bit メモリーの限界→Linux – PAEカーネルへの組み込み→Windows
•OSのエディションによる制限•ドライバの互換性が犠牲にされた根拠
–http://blogs.technet.com/b/markrussinovich/archive/2008/07/21/3092070.aspx
•カーネルパッチが必須–http://www.geoffchappell.com/notes/windows/license/memory.htm–http://news.saferbytes.it/analisi/2012/08/x86-4gb-memory-limit-from-a-technical-perspective/–http://news.saferbytes.it/analisi/2013/02/saferbytes-x86-memory-bootkit-new-updated-build-is-out/
まとめ
Cisco Talos VulnDev Team→Richard Johnson
•[email protected]•@richinseattle
→Marcin Noga→Yves Younan→Piotr Bania→Aleksandar Nikolic→Ali Rizvi-Santiago
Thank You!