第九回 カーネル/VM探検隊 発表資料 http://peatix.com/event/22490 #kernelvm
LLVM meets kernel@ntddk
View Slide
whoami●Yuma Kurogome(@ntddk)●慶應義塾大学 SFC B1, 武田研●留年の危機●Synclogue Inc.●Windows kernel, RCE, LLVM ← new!
whoami●CTF– EpsilonDelta(一応)– sutegoma2(潜入)– Kazusa●HackInTheBox KL– ROPが解けなかった
今回話すこと●LLVM●Return-oriented programming(ROP)●LLVMによるROP緩和技術●LLVMLinux
LLVM
詳しくは...
LLVM●プログラムの最適化を支援するコンパイラ基盤●任意の言語 → LLVM IR → 任意の言語
LLVMの構成●Frontend●Middlend●Backend
Frontend●字句解析●構文解析●意味解析●コード生成– LLVM IR●Frontendを実装するだけで任意の言語のコンパイラが作れる– MiddlendとBackendはLLVMデフォルトでも十分
Middlend●PassによるLLVM IRの解析と最適化– 関数全てに対するPass– ループ文に対するPass– ジャンプのない一連の処理に対するPass– etc...●自分だけのPassでみんなと差をつけろ
Backend●アセンブリ生成●実行オブジェクト生成●LLVM IRの実行(JIT)●コード生成(トランスレータ)●独自の言語のソースコードを生成したい場合はBackendを実装する必要がある
Clang●C, C++, Obj-C, Obj-C++のFrontend●C++14(N3797)の機能を完全に実装●BSDライクなライセンス– gccはGPLv3
ROP
脆弱性対策技術●scanf("%s", buf);– EIP 0x41414141からが戦い●プログラムに脆弱性があっても悪用されない機能の実現
脆弱性対策技術●StackGuard– リターンアドレス付近に乱数を配置●StackSheild– リターンアドレスを離れた位置に保存●ASLR– アドレスのランダマイズ– 攻撃の成功率を下げる●Exec Shield– データ実行防止
ROP●脆弱性攻略のための一技術– Exec Shieldを突破する●Return-oriented programming●短いコードブロックを組み合わせてシェルコードとする– ROP gadgets– シェルコードとして使える命令とリターンのセット– 命令をずらして解釈することも– ASLRがかかっていないファイルを利用
ROP gadgets684a0f4e:pop eaxret684a2367:pop ecxret684a123a:mov[ecx], eaxret0x684a123a0xfeedface0xdeadbeef0x684a23670x684a0f4e
Return-oriented rootkit●Rootkit– カーネル構造体などを書き換える– 悪意のあるプログラムを隠蔽●Return-oriented rootkit– Return-Oriented Rootkits: Bypassing Kenrel CodeIntegrity Protection Mechanism– ROPを利用したrootkit– ROPでカーネルの整合性検証をバイパスする
Return-oriented rootkit●カーネルの整合性検証– 未許可のLKMを実行しない●LKMにシグネチャを付加して検証– みんな大好きVMM●BitVisorとか– 書き込み可能なアドレスの実行権限を外す
Return-oriented rootkit●rootkitと名が付いているがLKMをロードするわけではない●要するにカーネルモードでのROP●LKMにバッファオーバーフローなどの脆弱性がある場合の攻撃手法
LLVMによるROP緩和技術
Return-less kernel●ROPでは任意の命令とリターンを組み合わせる●じゃあカーネルからリターンをなくせばいいじゃん●Defeating Return-Oriented Rootkits With“Return-less” Kernels●せっかくだから俺はLLVMを使うぜ
LLVMのFreeBSD対応●まともに動くFreeBSDカーネルをビルドできるようになったのは2009年2月25日●Return-less kernelが発表されたのは2010年4月●FreeBSD 8.0対象
Return-less kernel●3つの手法でカーネルからreturnを除去– Return indirection– Register allocation– Peephole optimization●LLVM backendを中心に実装– マシン語の最適化– 1命令を減らす最適化はバックエンドでのみ可能
Return indirection●リターンアドレスをテーブルから取得してくる– retは直接スタック上の戻り先アドレスを読み込む– 別の位置にあるリターンアドレスを読み込んでからそのアドレスにジャンプするようにすればガジェットを無効化できる
Return indirection●call, retを新しい形式に置き換えるpush $indexjmp dst...pop %regjmp *RegAddrBase(%reg)
Register allocation●Return indirectionだけでretを除去できるとは限らない●x86は可変長であるためコンパイラがretを生成してしまうことがあるmov %eax, %ebx -> 89 c3●movなのに何故かret(0xc3)に...●LLVM IRの仮想レジスタマッピング時に調整
Register allocation●llvm::Spiller●LLVMのレジスタ割り当てアルゴリズム– Simple scan– Local scan●これら2つは仮想レジスタから直接マッピング– Linear scan●より高度な割り当て
Register allocation●Linear scanを利用●X86RegisterInfo.tdを拡張– x86におけるレジスタ記述ファイル●危険なレジスタ割り当てにアノテーション●再割り当て●ご安全に!
Peephole optimization●コード中の0xc3を書き換える– 即値でもretと解釈してROPに使えるcmp $0xc3,%ecx→mov $0xc4, %regdec %regcmp %reg, %ecx
Peephole optimization●FreeBSD 8.0におけるコード0xFFFFFFFF801A4F01: E9 C3 00 00 00jmpq 0xFFFFFFFF801A4FC9●この0xc3の意味は?
Peephole optimization●jmpq命令の直後からのオフセット●相対アドレスの調整で0xc3を排除できる0xFFFFFFFF801A4F01: E9 C4 00 00 00jmpq 0xFFFFFFFF801A4FCA0xFFFFFFFF801A4F06: 90nop
LLVMLinux
+
現状●LLVMでLinuxカーネルをビルドしたい!– Linuxカーネルはgccの拡張機能と癒着– LLVM/Clangはgccの規格違反の挙動を絶対に実装しない●LLVMへの機能追加とカーネルへのパッチで実現– gcc拡張の闇を取り除く
gcc拡張の闇●Variable length arrays in structs (VLAIS)– Variable length arrayの拡張– 構造体内で配列の長さを実行時指定– iptablesやHMACなどで利用されているstruct foo_t {char a[n];/* Explicitly not allowed by C99/C11 */} foo;
gcc拡張の闇●__builtin_constant_pによる定数検出の回避– LLVM Bug 4898●Inline syntax handling– GNU89●__initと__exit●レジスタ変数– x86register unsigned long current_stack_pointer asm("esp")__used;– ARMregister unsigned long current_sp asm ("sp");
Return-less Linux kernel●そろそろLLVMでLinuxをビルドできそう●LinuxでもReturn-less kernelを実現できたらいいよね●GSoCとか...●OSvってどうなの