sprintf中怎么格式化字符串漏洞

本篇文章为大家展示了sprintf中怎么格式化字符串漏洞,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

首先我们先了解sprintf()函数
sprintf() 函数把格式化的字符串写入变量中。 

sprintf(format,arg1,arg2,arg++) arg1、arg2、++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。 注释:如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 "\$" 组成。

通过几个例子回顾一下sprintf

<?php $number = 123; $txt = sprintf("带有两位小数:%1\$.2f<br>不带小数:%1\$u",$number); echo $txt; ?> 输出结果: 带有两位小数:123.00 不带小数:123

例子2: 

<?php $num1 = 123456789; $num2 = -123456789; $char = 50; // ASCII 字符 50 是 2 //注释:格式值 "%%" 返回百分号 echo sprintf("%%b = %b",$num1)."<br>"; // 二进制数 echo sprintf("%%c = %c",$char)."<br>"; // ASCII 字符 echo sprintf("%%s = %s",$num1)."<br>"; // 字符串 echo sprintf("%%x = %x",$num1)."<br>"; // 十六进制数(小写) echo sprintf("%%X = %X",$num1)."<br>"; // 十六进制数(大写) ?> 输出结果: %b = 111010110111100110100010101 %c = 2  //注意var_dump('2')为string %s = 123456789 %x = 75bcd15 %X = 75BCD15

0x02 sprintf注入原理
底层代码实现
我们来看一下sprintf()的底层实现方法 

switch (format[inpos]) { case 's': { zend_string *t; zend_string *str = zval_get_tmp_string(tmp, &t); php_sprintf_appendstring(&result, &outpos,ZSTR_VAL(str),width, precision, padding,alignment,ZSTR_LEN(str),0, expprec, 0); zend_tmp_string_release(t); break;    }    case 'd':        php_sprintf_appendint(&result, &outpos,                              zval_get_long(tmp),                              width, padding, alignment,                              always_sign);        break;    case 'u':        php_sprintf_appenduint(&result, &outpos,                              zval_get_long(tmp),                              width, padding, alignment);        break;    case 'g':    case 'G':    case 'e':    case 'E':    case 'f':    case 'F':        php_sprintf_appenddouble(&result, &outpos,                                 zval_get_double(tmp),                                 width, padding, alignment,                                 precision, adjusting,                                 format[inpos], always_sign                                );        break;    case 'c':        php_sprintf_appendchar(&result, &outpos,                            (char) zval_get_long(tmp));        break;    case 'o':        php_sprintf_append2n(&result, &outpos,                             zval_get_long(tmp),                             width, padding, alignment, 3,                             hexchars, expprec);        break;    case 'x':        php_sprintf_append2n(&result, &outpos,                             zval_get_long(tmp),                             width, padding, alignment, 4,                             hexchars, expprec);        break;    case 'X':        php_sprintf_append2n(&result, &outpos,                             zval_get_long(tmp),                             width, padding, alignment, 4,                             HEXCHARS, expprec);        break;    case 'b':        php_sprintf_append2n(&result, &outpos,                             zval_get_long(tmp),                             width, padding, alignment, 1,                             hexchars, expprec);        break;    case '%':        php_sprintf_appendchar(&result, &outpos, '%');        break;    default:        break; }

可以看到, php源码中只对15种类型做了匹配, 其他字符类型都直接break了,php未做任何处理,直接跳过,所以导致了这个问题: 没做字符类型检测的最大危害就是它可以吃掉一个转义符\, 如果%后面出现一个\,那么php会把\当作一个格式化字符的类型而吃掉\, 最后%\(或%1$\)被替换为空 因此sprintf注入,或者说php格式化字符串注入的原理为: 要明白%后的一个字符(除了%,%上面表格已经给出了)都会被当作字符型类型而被吃掉,也就是被当作一个类型进行匹配后面的变量,比如%c匹配asciii码,%d匹配整数,如果不在定义的也会匹配,匹配空,比如%\,这样我们的目的只有一个,使得单引号逃逸,也就是能够起到闭合的作用。
这里我们举两个例子
NO.1
不使用占位符号 

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。