warmup
目录
注意
本文最后更新于 2021-05-12,文中内容可能已过时。
总结
根据本题,学习与收获有:
- 刚开始尝试利用
read
的返回值来构造execve
的syscall
,后来发现基本没有办法。后来查了下资料,才知道需要从alarm
函数的返回值进行利用。虽然之前一直都知道alarm
函数的功能,但是并不清楚其返回值是啥。 - 根据C语言alarm()函数:设置信号传送闹钟_C语言中文网 (biancheng.net)的解释,
alarm()
用来设置信号SIGALRM
在经过参数seconds
指定的秒数后传送给目前的进程。如果闹钟设置成功,那么之前设置的闹钟会被取消, 并将之前闹钟剩下的时间返回。根据这一性质,控制返回值,就控制了eax
寄存器。
题目分析
checksec
除了开启了NX
保护,其他保护全部关闭。运行环境为ubuntu18
。
函数分析
start
函数流程为:
- 设置闹钟为
10
秒 - 输出一段话
- 执行
main
函数 - 退出
main
很明显的栈溢出,读取结束后程序结束。
漏洞点
漏洞点就在main
函数的read
处。addr
距离ebp
只有0x20
的距离,但是可以读取0x34
个字节大小。
这时需要使用gdb
来看一下,需要覆盖多少。
看一下偏移,为0x20
利用思路
如果利用read
的返回值的话,需要提前布置好栈空间,但是溢出只有0x14
个字节,并且每次需要溢出的时候,都会重新把retaddr
置为exit
函数的地址。如果溢出了,那么输入的字节数肯定超过0xb
,所以这是相互矛盾的。因此,考虑从alarm
函数入手。由于第一次设置闹钟seconds
为0xa
,那么往小于这个值的系统调用好来找,发现只有open
符合我们的需求。所以,最后的利用思路为:
- 利用栈溢出,往
data
段写入flag
字符串,为open
的系统调用做准备,然后再回到main
函数 - 程序休眠
5
秒,然后利用栈溢出,执行set_alarm
,得到返回值为0x5
,然后执行open
的系统调用,并回到main
函数 - 栈溢出,利用
read(3, data_segment, 0x40)
,把flag
读取到data
段 - 栈溢出,使用
write(1, data_segment, 0x40)
输出flag
EXP
调试过程
不太好调试,但是但其实只要分析栈溢出那一步就行了,之后都是循环执行main
函数
-
调试一下栈溢出的点
1 2 3 4 5 6 7 8 9 10 11
welcome_str_addr = 0x80491bc good_luck_str_addr = 0x80491d3 read_addr = 0x804811d write_addr = 0x8048135 main_addr = 0x804815a alarm_addr = 0x804810d mov_ebx_syscall = 0x8048122 layout = ['a' * 0x20, read_addr, main_addr, 0, good_luck_str_addr, 0x60] payload = flat(layout) sh.sendafter("Welcome to 0CTF 2016!\n", payload)
这里会执行
read
函数:符合自己构造的栈布局
最后远程的攻击效果为:
完整exp
|
|
引用与参考
1、My Blog
2、Ctf Wiki
Buy me a coffee~
支付宝
微信