rop illmatic: exploring universal rop on glibc x86-64 (ja)

38
ROP ILLMATIC: EXPLORING UNIVERSAL ROP ON GLIBC X86 - 64 @inaz2 AVTOKYO2014 2014/11/15

Upload: inaz2

Post on 02-Jul-2015

2.944 views

Category:

Technology


6 download

DESCRIPTION

2014/11/15 AVTOKYO2014 (Japanese Version) English Version: http://www.slideshare.net/inaz2/rop-illmatic-exploring-universal-rop-on-glibc-x8664-en-41595384

TRANSCRIPT

Page 1: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

ROP ILLMATIC: EXPLORING UNIVERSAL ROP

ON GLIBC X86-64

@inaz2

AVTOKYO2014

2014/11/15

Page 2: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

ABOUT ME

• @inaz2

• Security Engineer & Python Programmer

• Girls Idol Freak

• ブログ「ももいろテクノロジー」

• http://inaz2.hatenablog.com/

2

Page 3: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

“ILLMATIC” ?

• http://en.wikipedia.org/wiki/Illmatic

• アメリカのラッパーNasによる造語

• "supreme ill. It's as ill as ill gets. That s*** is a science of

everything ill."

3

Page 4: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

BACKGROUND

• バッファオーバーフロー脆弱性などからの任意コード実行

• ドキュメント「各種セキュリティ機構が実装されている」

• どこまで突破できるのか?

• 論文「固定アドレスに十分な実行可能メモリが存在するならば…」

噂話「x86-64では固定アドレスに実行可能メモリが足りないので難し

い」

• 実行可能メモリが少ないときでも使える普遍的(universal)な方法はな

いのか?

4

Page 5: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

ENVIRONMENT

• x86-64環境におけるUbuntu Linux最新版

$ uname -aLinux vm-ubuntu64 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -aDescription: Ubuntu 14.04.1 LTS

$ gcc --versiongcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2

$ clang --versionUbuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)

5

Page 6: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

VULNERABLE CODE

• スタックバッファオーバーフロー脆弱性を含む最小限のCコード

6

#include <unistd.h>

int main(){

char buf[100];int size;read(0, &size, 8);read(0, buf, size);write(1, buf, size);return 0;

}

バッファサイズ以上の書き込みが可能

Page 7: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

HOW THE CODE IS EXECUTED

• RIPレジスタにその時点で実行されようとしている命令のアドレスが入っ

ている

• x86-64における関数呼び出し

• call命令は次の命令のアドレスをスタックにpushしてripを変える

• ret命令はスタックからアドレスをpopしてripを戻す

• ELFにおけるライブラリ関数呼び出し

• オブジェクトはGOT (Global Offset Table) および PLT (Procedure

Linkage Table) セクションを持つ

• GOTはライブラリ関数のアドレステーブル

• PLTはGOTに置かれた各アドレスにジャンプするためのエントリポイント

• PLTを経由して間接ジャンプを行う

7

Page 8: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

SMASHING THE STACK [PHRACK 49]

• Published in 1996, written by Aleph One

• 関数からのリターンアドレスを書き換え、シェルコードに向ける

8

AAAA

shellcode &buf

buf[100]

saved

ebp

return

address

higher

address

Page 9: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

SECURITY MITIGATIONS

• Enabled by default

• NX/DEP

• ASLR

• Stack canary

• Additional options

• RELRO + BIND_NOW (FullRELRO)

• FORTIFY_SOURCE

• PIE

9

Page 10: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

NX/DEP (DATA EXECUTION PREVENTION)

• 書き換え可能メモリ(データ)の実行を禁止する

• スタック・ヒープ領域に置いたシェルコードを実行しようとすると落ちる

$ cat /proc/$$/maps00400000-004ef000 r-xp 00000000 fc:00 265344 /bin/bash006ef000-006f0000 r--p 000ef000 fc:00 265344 /bin/bash006f0000-006f9000 rw-p 000f0000 fc:00 265344 /bin/bash006f9000-006ff000 rw-p 00000000 00:00 001cb0000-01ed1000 rw-p 00000000 00:00 0 [heap]...7fffe374e000-7fffe376f000 rw-p 00000000 00:00 0 [stack]

10

Page 11: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

ROP (RETURN-ORIENTED PROGRAMMING)

[BH USA 2008]

• リターンアドレスを書き換え、実行可能コードに向ける

• ret命令で終わるコード断片(ROP gadget)へのリターンを繰り返す

• jmp/call命令を使うものも含めてcode-reuse attackとも呼ばれる

pop rdi;ret;

system(“/bin/sh”)

runs

11

&gadget/bin/sh¥x00 &buf

buf[100]

return

address

higher

address

&system

Page 12: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

X86-64 CALLING CONVENTIONS

• x86-64では、関数の引数はレジスタにセットして渡される

• rdi, rsi, rdx, rcx, r8, r9の順

• 関数にリターンする前に各レジスタへの値のセットが必要

• “pop rdi; ret” / “pop rsi; ret” / “pop rdx; ret” / …

• これらのgadgetが固定アドレスに揃っていないことも多い

12

Page 13: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

LIBC_CSU_INIT GADGETS

• ほぼすべての実行ファイルに埋め込まれている__libc_csu_init関数

にあるコード断片を使う

• 3引数までの任意の関数呼び出しが可能

• 第4引数(rcx)はmemset/memcpyを呼ぶことで操作可能

(1) loc_400626

スタックからレジスタに値をセット

(2) loc_400610

引数にセットして [r12+rbx*8] をcall

13

Page 14: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

ASLR (ADDRESS SPACE LAYOUT RANDOMIZATION)

• スタック・ヒープ領域や共有ライブラリのアドレスをランダム化

• 実行ファイルが置かれるアドレスは固定のまま

14

heapa.out libc.so.6

1st execution:

higher

address

stack

heapa.out libc.so.6

2nd execution:

stack

Page 15: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

ROP STAGER USING IO PRIMITIVES

• PLTにある入出力関数を使い、固定アドレスにROPシーケンスを送り

込む

• read/write, send/recv, fgets/fputs…, だいたい何かしらある

• スタックポインタを送り込んだROPシーケンスに向ける(stack pivot)

• rbpをセットしてleave命令を実行することでrspを差し替える

read@plt # call read(0, 0x601048, 0x400)# 0x601048 = writable address around bss section

pop rbp; ret; # set rbp=0x601048leave; ret; # equiv. to “mov rsp, rbp; pop rbp”

15

Page 16: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

DETERMINING LIBRARY FUNCTION ADDRESS

• ランダム化されたアドレスにあるsystem関数を呼ぶには?

• GOTにある__libc_start_main関数のアドレスを読み出す

• __libc_start_main関数からsystem関数までのオフセットを足す

• ターゲットホストで使われているlibcバイナリを特定する必要がある

• __libc_start_main関数の下位ビットから推測する方法も考えられる

$ nm -D -n /lib/x86_64-linux-gnu/libc-2.19.so0000000000021dd0 T __libc_start_main0000000000046530 W system

offset = 0x046530 − 0x021dd0

= 0x24760

16

Page 17: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

RETURN-TO-DL-RESOLVE [PHRACK 58]

• Published in 2001, written by Nergal

• 偽のシンボル情報一式を作って、ダイナミックリンカ

(_dl_runtime_resolve関数)に読ませる

• libcバイナリの特定なしで、任意のライブラリ関数の呼び出しが可能

17

buf += struct.pack(‘<Q’, addr_plt) # PLT0 jumps to resolverbuf += struct.pack(‘<Q’, offset2rela)buf += ‘NEXT_RIP’buf += ‘A’ * alignment1buf += struct.pack(‘<QQQ’, writable_addr, offset2sym, 0) # Elf64_Relabuf += 'A' * alignment2buf += struct.pack(‘<IIQQ’, offset2symstr, 0x12, 0, 0) # Elf64_Symbuf += ‘system¥x00’ # symstr

Page 18: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

SKIPPING SYMBOL VERSION CHECK

• x86-64ではふつうにReturn-to-dl-resolveを行うと落ちる

• 偽シンボルのバージョンのインデックスを取得する箇所でSEGV (*1)

• GOTセクションにあるlink_map構造体のアドレスを読み出し、

[link_map+0x1c8]の値を0に書き換える

• バージョン情報を取得する処理をスキップ

if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL){const ElfW(Half) *vernum =(const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);

ElfW(Half) ndx =vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; // (*1)

version = &l->l_versions[ndx];if (version->hash == 0)version = NULL;

}18

Page 19: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

RELRO + BIND_NOW (FULLRELRO)

• Relocation read-only with lazy binding disabled

• 実行直後にすべてのGOTアドレスを解決し、Read-onlyにする

• 実行時、メモリ上に_dl_runtime_resolve関数のアドレスがセットされ

ない

• 直接呼び出すことができなくなる

(gdb) x/4gx 0x6010000x601000: 0x0000000000000000 0x00000000000000000x601010: 0x0000000000000000 0x0000000000000000

(gdb) x/4gx 0x6010000x601000: 0x0000000000600e28 0x00007ffff7ffe1c80x601010: 0x00007ffff7df04e0 0x0000000000400456

19

FullRELRO (0x601000 = address of GOT section)

Page 20: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

DT_DEBUG TRAVERSAL

• DynamicセクションにあるDT_DEBUGの値を見る

• 実行時にデバッグ用構造体 r_debugのアドレスが入る

• r_debugから、ロードされている各ライブラリのlink_map構造体が辿

れる

• link_map構造体を通して、ライブラリに関する各種アドレスが得られる

• ロードされたライブラリのGOTセクションから_dl_runtime_resolve関

数のアドレスを特定する

20

Page 21: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

LET’S TRY!

• x86-64かつASLR+NX/DEP+FullRELROが有効な条件下におい

て、シェルを起動する

1. 関数呼び出しにlibc_csu_init gadgetsを用いる

2. IO primitivesを使って固定アドレスにROPシーケンスを送り込み、

stack pivotする

3. DT_DEBUG traversalを行い、_dl_runtime_resolve関数のアドレス

を特定する

4. Return-to-dl-resolveでsystem関数を呼び出す

21

Page 22: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

DEMO 1

• http://inaz2.hatenablog.com/entry/2014/10/12/191047

• gccを使い、ASLR+NX/DEP+FullRELROを有効にした実行ファイルを

作る

• ROP stager + Return-to-dl-resolveでシェルを起動する

22

Enable ASLR:

$ sudo sysctl -w kernel.randomize_va_space=2

Compile with NX/DEP+FullRELRO enabled (w/o stack canary):

$ gcc -fno-stack-protector -Wl,-z,relro,-z,now bof.c

Execute the program and exploit it:

$ python exploit.py 100

Page 23: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

IT WORKS, BUT IS TOO COMPLICATED…

DT_DEBUG Traversal requires

read & write 5 times

23

Page 24: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

DYNAMIC ROP

• Or “Just-in-time Code Reuse” [BH USA 2013]

• libcメモリを丸ごと読み出して使う

1. GOTにある__libc_start_main関数のアドレスを読み出す

2. 読み出したアドレスから0x160000バイト程度を読み出す

3. 読み出したメモリ内にあるROP gadgetを使い、システムコールを実行

するROPシーケンスを構築する

pop rax; ret; # set rax=59 (_NR_execve)pop rdi; ret; # set rdi="/bin/sh"pop rsi; ret; # set rsi={"/bin/sh", NULL}pop rdx; ret; # set rdx=NULLsyscall; # call execve("/bin/sh", {"/bin/sh", NULL}, NULL)

24

Page 25: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

LEAVE-LESS EXECUTABLES

• clangでコンパイルすると、関数エピローグにleave命令が使われない

• “add rsp, XXh” の形で厳密にrspが調整される

• leave命令がない場合のstack pivotはめんどい

• 固定アドレスに送り込んだROPシーケンスへのpivotが難しくなる

gcc clang

25

Page 26: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

RETURN-TO-VULN

• 同一の脆弱な関数に繰り返しリターンする

• stack pivotなしで次のROPシーケンスを実行できる

26

Vulnerable function

Read the address of

__libc_start_main

Read the

libc memoryExecute system call

1. 2.

3.

Page 27: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

ROPUTILS

• https://github.com/inaz2/roputils

• ROPに関する各種タスクを行うツールキット

• readelfコマンドによるELFパース

⇒ セクション、シンボル、よく使うgadgetのアドレスを取得

• パラメータからのROPシーケンス構築

• pipe (local) & socket (remote) 両対応のNon-blocking IO

• Linux i386 / x86-64 シェルコード生成

• 適用されているセキュリティ機構の確認

• Metasploitパターンの生成およびオフセット計算

27

Page 28: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

ELFパース

各種アドレスの取得

ROPシーケンスの構築

すべてのクラスをimport

Non-blocking IOターゲットがremoteなら次のようにする:

Proc(host=x, port=y)

28

Page 29: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

DEMO 2

• https://github.com/inaz2/roputils/blob/master/examples/libc-

dynamic-no-leave-x64.py

• clangを使い、ASLR+NX/DEP+FullRELROを有効にした実行ファイ

ルを作る

• socatを使ってTCPサービスにする

• Dynamic ROP + Return-to-vulnでリモートシェルを起動

Enable ASLR and compile with NX/DEP+FullRELRO enabled (w/o stack canary):

$ sudo sysctl -w kernel.randomize_va_space=2$ clang -fno-stack-protector -Wl,-z,relro,-z,now bof.c

Execute the program as a TCP service:

$ socat tcp-listen:5000,fork,reuseaddr exec:./a.out &

Exploit it:

$ python libc-dynamic-no-leave-x64.py ./a.out 120 29

Page 30: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

HOW ABOUT THE REST?

Stack canary, FORTIFY_SOURCE and PIE

30

Page 31: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

STACK CANARY

• リターンアドレスの前にランダムな値を差し込むことでスタックバッファ

オーバーフローを検知

• 値が変わっていたら強制終了

• ターゲットが単純なfork serverなら、1バイトずつのbruteforceで突

破可能(最大試行数 256 * 8 = 2048)

• Heap overflow / Use-after-free脆弱性などによるポインタ書き換え

に対しては効果なし

31

値が変わってないかチェック

Page 32: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

FORTIFY_SOURCE

• 危険な標準ライブラリ関数を安全なものに置き換える

• gets → gets_chk, strcpy → strcpy_chk, read → read_chk, …

• バッファサイズのチェックを追加

• ほとんどのスタックバッファオーバーフロー脆弱性は塞がれる

• ただし、*_chkを使ったROP stager自体は可能

• Heap overflow / Use-after-free脆弱性などによるポインタ書き換えを

探す

32

read(0, buf, size)

→ __read_chk(0, buf, size, 100)

Page 33: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

PIE (POSITION-INDEPENDENT EXECUTABLES)

• 実行ファイルが置かれるアドレスもランダム化

• もちろんASLRが有効な場合に限る

• 実行ファイルやlibc中にある何かしらのアドレスが得られる場合には

効果なし(information leak)

• Buffer over-readなど他の脆弱性を使う

• 下位バイトのみを書き換えることで、リターンアドレスをずらすことはで

きる(partial overwrite)

• しかし、genericな攻撃は難しいと思われる

33

Page 34: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

FURTHER MITIGATIONS

• Shadow stack

• StackShield (2000), TRUSS (2008), ROPdefender (2011)

• リターンアドレスを別の領域にコピーしておき検証する

• Related: “SCADS: Separated Control- and Data-Stacks” (2014)

• Coarse-grained Control-Flow Integrity (CFI)

• ROPGuard (2012), kBouncer (2013), ROPecker (2014)

• Indirect Branches and Behavior-Based Heuristics policies

• 取り得るジャンプ先を集めておいて検証する

• 短い命令列が連続する回数を制限する(閾値ベース)

• Call-ret-pair gadget、Long-NOP gadgetが存在すればbypass可能

34

Page 35: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

RECAP

• NX/DEP bypass: ROP

• ASLR bypass: ROP stager

• 前提条件: PLT上のIO primitives、十分なROP用バッファ

• x86-64での関数呼び出し: libc_csu_init gadgets

• ライブラリ関数呼び出し: Return-to-dl-resolve

• FullRELRO bypass: DT_DEBUG traversal

• システムコール実行: Dynamic ROP

• leave-less executable対策: Return-to-vuln

• ROP is illmatic

35

Page 36: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

REFERENCES (1/2)

• "Exploit" -記事一覧 - ももいろテクノロジー

• http://inaz2.hatenablog.com/archive/category/Exploit

• Smashing The Stack For Fun And Profit (Phrack 49)

• http://phrack.org/issues/49/14.html

• The advanced return-into-lib(c) exploits: PaX case study (Phrack 58)

• http://phrack.org/issues/58/4.html

• Return-Oriented Programming: Exploits Without Code Injection (Black

Hat USA 2008)

• http://cseweb.ucsd.edu/~hovav/talks/blackhat08.html

• Return to Dynamic Linker (Codegate 2014 Junior)

• http://blog.jinmo123.pe.kr/entry/Codegate-2014-Junior-Presentation

36

Page 37: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

REFERENCES (2/2)

• Ghost in the Shellcode 2014 - fuzzy - Code Arcana

• http://codearcana.com/posts/2014/01/19/ghost-in-the-shellcode-2014-fuzzy.html

• Just-In-Time Code Reuse: The more things change, the more they stay

the same (Black Hat USA 2013)

• https://media.blackhat.com/us-13/US-13-Snow-Just-In-Time-Code-Reuse-

Slides.pdf

• SCADS: Separated Control- and Data-Stacks (SECURECOMM 2014)

• https://www1.cs.fau.de/filepool/scads/scads-securecomm2014.pdf

• Stitching the Gadgets: On the Ineffectiveness of Coarse-Grained Control-

Flow Integrity Protection (USENIX Security 2014)

• https://www.usenix.org/conference/usenixsecurity14/technical-

sessions/presentation/davi

• And many others

37

Page 38: ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)

THANK YOU!

@inaz2

38