格式化字符串漏洞研究杜旭涛1,2,∗邢春晓2,†周立柱1,‡1清华大学计算机系,北京1000872清华大学WEB与软件技术中心,北京100087摘要格式化字符串漏洞对计算机系统和网络具有很大的危胁。
攻击者利用格式化字符串漏洞可以远程侵入网络上的计算机系统,偷看系统信息甚至得到系统的完全控制权。
由于与传统的缓冲区溢出漏洞攻击的原理不同,对于缓冲区溢出漏洞进行防范的方法对于格式化字符串漏洞通常会失效。
对于高可信软件来说,如何避免和防范格式化字符串漏洞的危害就显得格外重要。
本文对格式化字符串漏洞进行了综合的介绍。
指出了格式化字符串漏洞的成因和危害。
使用多个例子从预防的角度说明了利用该漏洞进行攻击的基本原理。
然后对相关的漏洞防范工具作了介绍。
关键词格式化字符串漏洞;shellcode;攻击;漏洞防范工具Survey of Format String VulnerabilityXu-Tao Du1,2Chun-Xiao Xing21Department of Computer Science,Tsinghua University,Beijing1000802Research Center of WEB and Software Engineeing,Tsinghua University,Beijing100080Abstract Format string vulnerability is a vital threat to computer systems and networks.By exploiting the format string vulnerability,attackers can break into computer systems in the internet.They can get various information about the computer systems and can even get full control of them.Since the principle and methods of exploiting format string vulnerability and that of exploiting traditional buffer overflow vulnerability are different,tools for preventing buffer overflow attacks will usually fail in front of format string attacks.For the High-Confidential Software,its very important to avoid and prevent from the damage of format string vulnerability.This paper introduce the format string vulnerability.After point out its origin and potential damage,we show the method to exploit this vulnerability through several concrete examples.Then we introduced some protecting tools for this vulnerability.Keywords Format String Vulnerability;Shellcode;Attack;Protecting Tools∗通讯作者。
电子邮件:dxt05@†电子邮件:xingcx@‡电子邮件:dcszlz@1格式化字符串漏洞(Format String Vulnerability)1999年,Tymm Twillman发现了第一个格式化字符串漏洞[1]。
2000年,tf8在BugTraq上发布的wu-ftpd中的格式化字符串漏洞[2]使得该漏洞广为人知。
之后,众多的格式化字符串漏洞被发现。
虽然已经过去好几年,但是新的格式化字符串漏洞仍不断被发现(见BugTraq)。
美国国家漏洞数据库(NVD)上2007年8,9,10三个月报告的格式化字符串漏洞有29个(如:Oracle OPMN daemon([3]),TIBCO SmartPGM FX([4]))。
这些漏洞大都使攻击者可以远程攻击系统,运行任意代码,从而控制系统。
格式化函数是一系列ANSI C函数(如printf函数),它们可以接受可变数量的参数,其中一个称为格式化字符串参数(format string)。
格式化函数对输入的格式化字符串参数进行解释,根据该参数的要求使用其它的输入参数,形成输出的字符串。
格式化字符串函数几乎被用在所有的C程序中,用来输出、打印错误信息或处理字符串。
正因为这些函数使用的广泛性,它们的漏洞对系统安全有重要的影响。
攻击者利用格式化字符串漏洞可以获得系统的控制权,这一点对于高可信软件和服务器软件来说是致命的。
本文在第2节对格式化字符串函数作一般性的介绍;第3节,我们利用格式化字符串漏洞的基本方法;第4节,我们对格式化字符串漏洞相关的防范工具作出介绍;第5节是结论。
2格式化字符串函数2.1格式化字符串函数家族C99标准定义的格式化字符串函数包括:•int fprintf(FILE*stream,const char*format,...)根据format提供的控制信息将输出内容输出到stream;•int printf(const char*format,...)根据format提供的控制信息将输出内容输出到stdout;•int sprintf(char*str,const char*format,...)根据format提供的控制信息将输出内容输出到s,以 \0 结尾;•int snprintf(char*str,size_t size,const char*format,...)将不大于size个字节的内容输出到str(包括结尾的 \0 );•vfprintf(FILE*stream,const char*format,va_list arg)•vprintf(const char*format,va_list arg)•vsprintf(char*str,const char*format,va_list arg)•vsnprintf(char*str,size_t size,const char*format,va_list arg)以上4个函数用固定长度的arg参数取代了原来的变长参数表,其它功能分别与fprintf, printf,sprintf,snprintf相同。
格式化字符串函数的主要功能为:1.将某些C类型的数据转换为字符串类型进行打印;2.根据参数format提供的控制信息,将其后的参数转换为某种输出格式;3.输出到不同的目标。
2.2格式化字符串函数与栈的关系格式化字符串函数的功能由作为输入参数之一的格式化字符串(即参数format)控制。
函数的所有参数都存放在栈上。
图1给出了一个简单的例子,可以说明栈在调用格式化字符串函数时的情况。
/*f s−exmp1.c*/−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−v o i d f u n c t i o n(i n t c){p r i n t f("Number o f p a r a m e t e r s on s t a c k:%d\n",c);}v o i d main(){f u n c t i o n(2);}图1:fs-exmp1.c图2是fs-exmp1.c的反汇编代码,通过该代码,我们可以清楚地看到function函数调用printf时所做的工作。
图2:fs-exmp1.c的反汇编代码首先,<function+0>和<function+1>是函数的序言。
<function+3>在栈上分配空间。
function函数本身并没有定义局部变量,所分配的空间是给向printf函数传递参数准备的。
<function+6>将main函数向function函数传递的参数2存入eax寄存器。
<function+9>将eax存入栈。
<function+13>将一个立即数0x80a056c存入栈。
图片的最后一行中,我们通过gdb的x命令显示了该立即数地址的内容。
该立即数就是格式化字符串“Number of parameters on stack:%d\n”的首地址。
这时需要向printf传递的参数已经全部存入栈中,于是<function+20>调用printf函数。
printf函数开始执行后,根据栈上的格式化字符串参数(在本例中就是“Number of parameters on stack:%d\n”)对栈上的其它参数进行对应的操作(通常是变换和组合),形成输出字符串,打印到stdout。
很显然,本例中printf从栈上取得整型参数2,将其转换为字符格式,然后与前面的字符一起输出到屏幕:Number of parameters on stack:2这里存在一个重要的问题,那就是,栈和其它内存区域一样,只是存储某些内容,而没有一个标签说明某个地址的内容是否printf的参数。
printf只是根据格式化字符串参数的控制要求从栈上取得其它参数。
如果格式化字符串参数被故意或不故意的写错了,那么printf函数就可能被利用来对系统进行攻击。
3利用格式化字符串漏洞的方法本节将使用具体例子介绍利用格式化字符串漏洞的基本方法。
这些例子并不是实际的系统攻击程序,而是为了说明格式化字符串漏洞而编写的说明性程序。
3.1偷看栈的内容图3给出了一个利用printf来偷看栈内容的程序。
在function函数中,调用printf时,格式化字符串参数要求后面有3个参数,分别以10进制(%d)和16进制(%x)来打印。
也就是说,该printf调用要求有4个参数,分别是格式化字符串参数和其后的3个需要打印的参数。
但是,给printf调用却只提供了2个参数。
printf根据格式化字符串参数的控制要求,从栈上读取3个字分别按10进制和16进制进行打印。
该程序的输出见图4:可以看到,将参数2正确打印出来后,printf又从栈上参数2之后的地址取得2个字的内容并以16进制打印出来。
以这种方式,通过对格式化字符串参数的控制就可以将栈上任意地址的内容打印出来。