Upload
code-blue
View
57
Download
5
Embed Size (px)
Citation preview
Microsoft Windowsカーネルのデスノート
Windows カーネルの内部
$whoami
• @zer0mem• Tencent KeenLab の Windows
カーネル研究者• Pwn2Own 優勝者
( 2015/2016 )、 wnie にノミネート( 2015 )
• ファジングで注目するもの:state
• 武術太極拳拳士
Daniel
• @long123king• Tencent KeenLab の
Windows カーネル研究者• Pwn2Own 優勝者( 2016 )• ファジングで注目するも
の:データ「フォーマット」• Windbg の専門家
Peter
アジェンダ
sandbox
ntoskrnl
拡張
clfs
内部
Sandbox• 攻撃対象領域を制限
• バグに対する潜在的景観• それを悪用するために利用可能なメソッド
• ACL vs さまざまなカーネルオブジェクトへのアクセス• 非 ntos 、非 w32k ドライバー• さまざまな ntos オブジェクト
• w32k フィルタリング• sandbox 化されたアプリが必要とするものに依存
• w32k ロックダウン
Sandbox のターゲット
mutex メモリ
スレッド
PE セクション
パイプ
... 他には?
• Nt* トランザクション *• Nt* エンリストメ
ント *• Nt* マネージャ *
何だろう?
• カーネル トランザクション マネージャ
• 目的• カーネルトランザクションマネージャ( KTM )は、トランザ
クションを使うアプリケーション開発を可能とする。トランザクションエンジン自体はカーネルの内部にあるが、トランザクションはシングルホスト内または分散ホスト間でカーネルまたはユーザーモードトランザクション向けに開発可能。
• KTM は、トランザクション NTFS ( TxF )やトランザクションレジストリ( TxR )を実装するために使われる。 TxF は、NTFS ファイルシステム内でトランザクション処理ファイルシステムの操作を可能とする。 TxF は、トランザクション処理レジストリの操作を可能とする。 KTM は、クライアントアプリケーションがトランザクションでのファイルシステムやレジストリ操作と連動することを可能とする。
tm.sys
• シンプルなオブジェクトステート• わずかな syscall が利用可能• それほど多くないコードが含まれている• しかし面白い方法で相互接続される
• 結果 : • 1 回の null ポインターの逆参照• 1 個の悪用可能な脆弱性
tm の回避
• tm.sys 単純目的のドライバー• しかし、興味深い モジュールが バックエンドに含まれる• CLFS.sys
CLFS.sys• 目的
• 共通ログファイルシステム( CLFS ) API は、専用クライアントアプリケーションが利用できる高パフォーマンスの、汎用的なログファイル・サブシステムを提供し、複数のクライアントが共有することでログアクセスを最適化できる。
• ロギングまたはリカバリサポートを必要とするあらゆるユーザーモードのアプリケーションが、 CLFS を使用できる。
• 該当箇所• データやイベント管理、そしてサーバーや企業アプリケーションを開発
する際に CLFS を使用可能。• データ管理に対しては、以下と CLFS を使用可能:
• データベースシステム• ストアアンドフォワードシステムなどのメッセージング• オンライントランザクション処理( OLTP )システム• その他トランザクションシステムの類
CLFS.sys• トランザクションなど色々なものに、かなり統合
されている!• C++ コードベース• 十分な攻撃対象領域を提供
• … しかし、 AppContainer/ 信頼できないレベル、というわけではない…
• いや、そうなのか?
NtCreateTransactionManager• CLFS に依存• 自身のチェックポイント用に CLFS を使う
• それゆえに、以下を暗示する: • CLFS を開く• CLFS を * パース * する • CLFS と相互作用する
• では試してみよう!
CLFS - データファジング I.• わたしは、カーネルのデータファジングのファン
ではない• わたしは、カーネルでデータのパーシングをすることには全く
もって強く反対している☺
• 手っ取り早い調査をしてみよう。以下のものならばわたしも OK : • ファイルをランダムに変更する• 結果 = ゼロ• わたしにとってナイスなもの。というか、わたしはあまり興味
がない• 本来のアイディアに立ち戻る!
CLFS - ステートファジング
• アプローチ 1.• リバース・エンジニアリング: clfs.sys• ioctl に行く• ... あぁ、最初からやるのは面倒すぎる ...
• アプローチ 2.• MSDN ドキュメントを読み通す• その API 群がどのように動くかを理解する
• 自身または別の API を正常に呼び出すために必要なコールスタック
• Qilin (わたし達の内部ファズツール)にそのロジックを実装する
• Qilin のロジックにほんの少し干渉する
bugz++• 最初のファズツール試行の 15 分後、最初のクラッ
シュ• … なんてこった
• しかし DDoS のみ• それを除外• 別の bugz が出現
• ここで再度考える… 結局、データ・ファジングは本当にそんなに悪いアイディアなのか?
CLFS - データファジング II.• リバースエンジニアリング:どこで & どのよ
うにデータがパースされるのか
• エントリー・ポイント : ClfsCreateLogFile
•痛っ… マジック… ダミーのファズ防御• I. crc• II. 再配置が必要
CLFS - もっと真剣にファズしよう•既存コードの再実装は面倒すぎるが、そもそ
もそれは必要なのか?
CLFS - もっと真剣にファズしよう
• crc の実装と再配置は面倒くさすぎる
CLFS { ステート、ダミー、強化 }
データ強化されたファズ27% ++
データダミーファズ40%
ステートファズ33%
CLFS ファジング戦略 => 結果
CLFS の内部… その傘下にあるもの…
BLF ( ベースログファイル ) 形式
コントロールレコード
コントロールレコードシャドウ
ベースログレコード
ベースログレコードシャドウ
情報落ちレコード
情報落ちレコードシャドウ
[2 セクター ] レイアウト、拡張情報、情報落ちを含む
[2 セクター ] コントロールレコードのシャドウコピー
[0x3D セクター ] クライアント情報、コンテナ情報を含む。
[0x3D セクター ] ベースログレコードのシャドウコピー。
[1 セクター ] 情報落ちを含む。
[1 セクター ] 情報落ちレコードのシャドウコピー。
レコードヘッダー
修正上位バイト
セクター数 予備領域 1 チェックサム
フォーマットのバージョン 予備領域 2
予備領域 3
現在の LSN
次の LSN レコードのオフセット配列( 0x10 * DWORD )
固定オフセット
ストリームインデックス
マジックセクター数のコピー
コントロールレコード
レコードヘッダー( 0x70 バイト)
ダンプ数 マジック( 0xC1F5C1F5000005F1C )
予備領域 1 拡張フェーズ 拡張ブロックインデックス
拡張ブロックインデックスシャドー
拡張後のブロックセクター
予備領域 2 予備領域 3
予備領域 4
拡張前のファイルセクター
データファイルセクター 情報落ちの種類
情報落ちフィールド
実際のレコード数 予備領域 5
レコードパラメータの配列( 0x18 * 数)
ベースログレコード
レコードヘッダー( 0x70 バイト)
ダンプ数
予備領域1
ベースログID ( GUID )
クライアント記号のハッシュ配列( 0x0B * QWORD )
+配列の終わり
コンテナ記号のハッシュ配列( 0x0B * QWORD )
共有セキュリティディスクリプタ記号のハッシュ配列( 0x0B * QWORD )
予備領域2
クライアント数 予備領域
3予備領域4
予備領域5
コンテナ数
クライアントコンテキストのオフセット配列( 0x7C * DWORD )
コンテナコンテキストのオフセット配列( 0x400 * DWORD )
予備領域6
更新シーケンス番号
コンテナレコード
レコードヘッダー( 0x70 バイト)
仮想 LSN 補償処理が必要な次の LSN
直前の LSN レコードサイズ 次のレコードサイズ
フラグ レコードタイプ
データオフセット
シンボルヘッダー
タイプ( 0xC1FDF006 ) サイズ チェックサム ネームオフセット
予備領域 1 予備領域 2
予備領域 3ブロックネームオフセット
ブロック属性オフセット
クライアントコンテキスト
タイプ( 0xC1FDF007 ) サイズストリームインデックス 予備領域 1 フラッシュスレッド
予備領域( 0x28 バイト)
予備 LSN1 ベース LSN
最後にフラッシュされたLSN
最後の LSN
予備 LSN2 予備 LSN3
予備領域 3 ( 0x20 バイト)
コンテナコンテキスト
タイプ( 0xC1FDF008) サイズ コンテナファイルサイズ 予備領域 1
物理コンテナインデックス
論理コンテナインデックス 予備 LSN1
予備領域 2フラグストリーム数
CClfsBaseFilePersisted::ReadImage
実際のレコード数
レコードパラメータの配列
コントロールレコード
レコードパラメータ
バッファポインタ サイズ オフセット
CClfsBaseFile::GetBaseLogRecord
CClfsBaseFile::GetBaseLogRecord(CClfsBaseFile* this) xor eax, eax cmp ax, [rcx+28h] jz short locret_1C00335DB mov rcx, [rcx+30h] mov rcx, [rcx+30h] test rcx, rcx jz short locret_1C00335DB mov eax, [rcx+28h] add rax, rcx
locret_1C00335DB: retn
CClfsBaseFile::AcquireMetadataBlock
Use of AcquireMetadataBlock
CClfsBaseFilePersisted::OpenImage
クライアント記号ハッシュ配列
ベースログレコード
コンテナ記号ハッシュ配列
SD記号ハッシュ配列
シンボルハッシュ関数__int64 ClfsHashPJW(const struct _UNICODE_STRING *a1){ unsigned int v1 = 0, v4 = 0, v6; PWSTR wchar_buffer = a1->Buffer; const struct _UNICODE_STRING *v3 = a1; if ( a1->Length & 0xFFFE ){ do{ int v5 = 0x10 * v1 + RtlUpcaseUnicodeChar(*wchar_buffer); v6 = v5 & 0xF0000000; if ( v5 & 0xF0000000 ) v5 ^= v6 >> 0x18; v1 = v6 ^ v5; ++wchar_buffer; ++v4; } while ( v4 < (unsigned int)v3->Length >> 1 ); } return v1;}
強化 CFLS フォーマットファジング• ターゲットを熟知している場合、それを上手にファズ
できる。• 現在わかっていること :
• BLF ファイルフォーマット• コントロールレコード• ベースログレコード
• 記号ヘッダー• クライアントコンテキスト• コンテナコンテキスト
• コンテナレコード
• Clfs.sys は、これらのフォーマットをパースするための固有のロジックを持っているが、それは十分頑強なのだろうか?
強化 CFLS フォーマットファジング
Select
Deserialize
Mutate
Immune
Serialize
push デシリアライズ
シリアライズ
抗体
強化 CFLS フォーマットファジング
class CControlRecord : public CFormatBase<CControlRecord>{ …… virtual bool serialize(ostream & out) const override; virtual bool deserialize(istream & in) override; virtual bool mutate() override; ……};
class CBaseLogRecord : public CFormatBase<CBaseLogRecord>{ …… virtual bool serialize(ostream & out) const override; virtual bool deserialize(istream & in) override; virtual bool mutate() override; ……};……
強化 CFLS フォーマットファジング
bool CCLFSFormat::deserialize(istream & in){ …… m_controlRecord.deserialize(in); m_controlRecordShadow.deserialize(in); m_baseLogRecord.deserialize(in); m_baseLogRecordShadow.deserialize(in); m_truncateRecord.deserialize(in); m_truncateRecordShadow.deserialize(in); ……}
bool CCLFSFormat::mutate(istream & in){ …… }
bool CCLFSFormat::serialize(istream & in){ …… }
強化 CFLS フォーマットファジング
CCLFSDocument::CCLFSDocument(const string filename) :m_template_filename(filename) ,m_template_stream(filename, ios::in | ios::binary){ /* number: 0 */m_engine.registerFilter(make_unique<CCommonErrorBypass>()); /* number: 1 */m_engine.registerFilter(make_unique<CPOC_XXX_1>()); /* number: 2 */m_engine.registerFilter(make_unique<CPOC_XXX_2>()); /* number: 3 */m_engine.registerFilter(make_unique<CPOC_XXX_3>()); /* number: 4 */m_engine.registerFilter(make_unique<CPOC_XXX_4>()); /* number: 5 */m_engine.registerFilter(make_unique<CPOC_XXX_5>()); ……}
void CCLFSDocument::mutate(){ m_clfs_format.mutate(); m_engine.triggerFilter(3, m_orginal_clfs_format, m_clfs_format);}
強化 CFLS フォーマットファジング
bool CPOCFilterEngine::triggerFilter(size_t filterIndex, CCLFSFormat& originalFormat, CCLFSFormat& format){ bool b_triggered = false; for (size_t i = 0; i < m_filters.size(); i++) { if (i == filterIndex) { m_filters[i]->infect(originalFormat, format); b_triggered = true; } else m_filters[i]->immune(originalFormat, format); }
return b_triggered;}
Q & A
ご清聴ありがとうございました!