一次有趣的非栈上格式化字符串攻击
参考:孤鸿师傅的博客
题目链接:NSS靶场
前言:
litctf中出现的一道奇怪的题目,打完比赛后gets师傅告诉我这种题出现过几次,还甩给我一篇博客.结果打开一看,发现是楠哥的文章.
世界可真小
glibc的知识怎么永远学不完啊
题目分析
题目给了我们栈地址和libc地址,,并给了我们一次非栈上格式化字符串的机会,之后立马使用_exit(0)退出
由于使用的是_exit(0)我们无法使用exit_hook或FSOP
本题几乎是摆明了要让我们修改printf的返回地址
回顾:非栈上格式化字符串实现任意地址写
在常规的非栈上格式化字符串利用中,我们总是有着无限的格式化机会
我们会
- 修改栈上的三联栈链,使得链尾的地址在栈上拥有一个可以指向任意栈地址的二联栈链
- 然后对二联栈链进行格式化攻击,使得
返回地址
/got
表被修改
可以看到常规的一次非栈上格式化字符串攻击至少需要两次printf
调用,很明显此时我们没有这个条件
新手法:利用包含$符
和非$符
的解析速度差异一次性修改三联栈链
和二联栈链
我们知道,$xxxp
会被直接解析为第xxx
个参数的地址,比如我们想表示第九个参数的地址,可以使用%9$p
但我们还有另一种手法,还记得刚刚学格式化字符串时的%p%p%p%p%p%p...
每一个%符都会将解析符号对应的参数自动向后移动
如果我们使用了8个%p
,然后使用%n
,此时前8个%p
被打印出的字符串长度就会被写入第九个参数指向的地址
第一枚攻击载荷
那我们就可以使用这个手法,实现在栈上写入一个包含printf
返回地址的三联栈链,
同时对返回地址进行修改,使得执行流回到read
1 | key=ret&0xffff#ret为printf返回地址 |
可以看到,我们成功将返回地址修改为read,此时我们可以进行二次读入
同时还拥有了三联和二联栈链
在后续的攻击中,我们可以直接对%39$hn
进行修改,就不需要再使用大量%p
与%n
竞争了
同时我们还通过大量%p
获得了PIE
,现在可以使用elf中的gadget了
后续利用
此时我们拥有了无限次格式化字符串的机会,那问题就转化为了普通的非栈上格式化字符串问题
- 不过有一点需要注意的就是我们已经占用了一条栈链用于修改返回地址,我们想要继续利用还需要另一条三联栈链.刚刚好,栈中一般都会有包含
elf文件名
和/bin/bash
的栈链 - 同时我们不能直接对printf的返回地址进行修改,而是对printf返回地址的下一条地址进行修改,修改好后一次性把printf返回地址修改为elf中一个ret的地址,这样就不会影响无限次的格式化机会
- 本题中刚刚好有一个one_gadget可以在printf返回时被使用,这就节约了我们几步
EXP
1 | from pwn import * |