分析过程
- 拿到题目,可以看到程序用getnline获取输入,把获取的输入传递给sprintf,之后直接用printf将输入的值给输出,存在格式化字符串漏洞
- 接下来就是尝试利用这个格式化字符串漏洞,
- hijack got表(失败),发现call printf之后程序会调用的函数只有___stack_chk_fail,getnline只会拿取用户输入的64个字符,因此无法通过栈溢出来来触发该函数
- hijack retaddr(失败)需要程序调用两次printf,分别用来获得函数的 rbp 与返回地址,根据相对偏移获取存储返回地址的地址,但是本程序只会调用一次printf
- 总结思路:我们要作的是把payload控制在64字符之内,且需要重复调用main实现多次任意地址写(覆盖fini_array为main,覆盖strlen为system)
- 为了程序调用两次printf,我们覆盖fini_array数组中的do_global_dtors_aux为main,使最后程序执行完毕后执行libc_csu_fini时再次调用main函数
- 覆盖hijack got表,把strlen覆盖为system(getnline函数中在调用strlen之前会通过_fgets获取用户输入并把用户输入拷贝在esp上,此时把strlen替换为system相当于直接执行用户的输入)
- 尝试fmtstr_payload来构造payload发现pwntool会利用%xxc%152c%13$hhn来构造而hhn一次只会向某个地址写入单字节,导致payload过长,因此只能用$hn来构造。而且strlen和fini地址高位都是0x0804,可以放在一起写入来缩减payload
- 最后构造出的payload 长度为60<64
4.exp如下
1 | from pwn import * |
ref
只有一次任意地址写,通过修改.fini_array段,利用__libc_csu_fini函数性质构造循环调用main函数,并溢出检查字段绕,变成多次任意地址写
https://xuanxuanblingbling.github.io/ctf/pwn/2019/09/06/317/