-
-
[原创]格式化字符串漏洞
-
2021-6-12 11:17 7246
-
格式化函数
格式化字符串漏洞本身并不算缓冲区溢出漏洞,主要是针对一些格式化函数,如printf、sprintf、vsprintf等。这些格式化函数利用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后面的变参列表中提供相应的变量,最终函数就会使用相应位置的变量来代替那个说明符,产生一个调用者想要的字符串。
下面列出几个比较关键的参数格式:
·%x(%lx):替换为参数的值(十六进制)。
·%p:替换为参数的值(指针形式)。
·%s:替换为参数所指向内存的字符串。
.%n:参数对应整型指针,将该参数之前输出的字符数量写入参数指向的地址中。
参数定位
在C语言程序中,多数含有可变参数的函数都使用stdarg.h头文件中定义的宏stdarg来访问它们的可变参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <stdio.h> #include <stdarg.h> int myprint( int Narg,...) { int i; va_list ap; va_start(ap,Narg); for ( int i = 0 ; i<Narg;i + + ) { printf( "%d\t" ,va_arg(ap, int )); printf( "%f\n" ,va_arg(ap,double)); } va_end(ap); } int main() { myprint( 1 , 2 , 3.5 ); myprint( 2 , 2 , 3.5 , 3 , 4.5 ); } |
- 初始va_list:myprint()函数中定义了一个va_list指针,用于访问可变参数。
- 宏va_start根据传入的第二个参数来计算va_list的起始位置,宏va_start获得Narg的地址(设为A),根据其类型(int)计算它的长度(设为B),然后设置va_list指针(变量ap)指向A+B,实际上就是指向Narg正上面的内存位置。
- 移动va_list指针:宏va_arg()返回va_list指针指向的值,并使指针指向下一个可变参数的位置,这个指针应该移动多少取决于宏的类型参数,int移动4个字节,double移动8个字节。
- va_end():来做必要的清理工作。
printf()函数扫描格式化字符串,直到遇到一个格式说明符'%',此时,printf()函数调用va_arg()来获得当前va_list指针指向的可变参数,同时va_arg()把指针移到下一个可变参数,把获得的参数看成什么类型的数和把va_list指针移动多少距离取决于格式规定符的类型。
在正常的情况下,格式化字符串所需的参数是依次往后索引的,如“%p,%x”,其对应于第1、2个参数。
也有一些特殊情况,如
“%d$m”形式:
其中,d代表数字(1,2,.…),用来定位参数列表中的第d个参数(从1开始算);
m为前面所述的关键参数格式之一(x,p,s,n,...).
原理
通常,格式化函数是一种变长参数函数,后面的参数需要根据栈的参数传递来进行释放,x86的参数全在栈上,x64的参数从第4个开始放在栈上。这些格式化函数遇到格式说明符的关键字符之后,会按照传参规则去寻找参数来进行替换或者修改,并不会关心真实的传参情况。所以如果实际参数数量小于所需的参数数量,则其依然会将对应位置的数值当成参数进行转换,从而引发格式化字符串漏洞。由此可见,利用格式化字符串漏洞既能够泄露信息,又能够修改信息,功能比较强大。
未检查真实的参数列表
“nil”表示无值,任何变量在没有被赋值之前的值都为nil,对于真假判断,只有nil与false、0表示假,其余均为真。
信息修改主要是利用格式化字符串中的‘%n’对参数进行写入,写入的值是格式化字符串中%n之前的字符数量。
修改宽度控制具体如下:
·%n:修改4字节。
.%hn:修改2字节。
.%hhn:修改1字节。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课