php弱类型比较
php是弱类型语言,它含有很多隐式的转换。常见的php弱类型,主要分为类型转换问题和内置函数参数的松散性两类。
类型转换问题
类型转换是无法避免的问题。例如需要将GET或者是POST的参数转换为int类型,或者是两个变量不匹配的时候,PHP会自动地进行变量转换。
“ == “ 和 “ === “
1 | $a == $b ; |
=== 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较
== 在进行比较的时候,会先将字符串类型转化成相同,再比较
如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行。
1 | <?php |
在上述代码中,
“admin”==0比较的时候,会将admin转化成数值,强制转化,由于admin是字符串,转化的结果是0自然和0相等。
“1admin”==1比较的时候会将1admin转化成数值1,而”admin1”却被转化成0。
“0e123456”==”0e456789”相互比较的时候,会将0e这类字符串识别为科学技术法的数字,0的无论多少次方都是零,所以相等。
php手册中解释如下:
当一个字符串当作一个数值来取值,其结果和类型如下:如果该字符串没有包含’.’,’e’,’E’并且其数值值在整形的范围之内,该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0
Hash比较
PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。
攻击者可以利用这一漏洞,通过输入一个经过哈希后以”0E”开头的字符串,即会被PHP解释为0,如果数据库中存在这种哈希值以”0E”开头的密码的话,他就可以以这个用户的身份登录进去,尽管并没有真正的密码。
1 | "0e132456789"=="0e7124511451155" //true |
在进行比较运算时,如果遇到了0e\d+这种字符串,就会将这种字符串解析为科学计数法。所以上面例子中2个数的值都是0因而就相等了。如果不满足0e\d+这种模式就不会相等。
十六进制转换
1 | "0x1e240"=="123456" //true |
当其中的一个字符串是0x开头的时候,PHP会将此字符串解析成为十进制然后再进行比较,0x1e240解析成为十进制是123456,所以与int类型和string类型的123456比较都相等。
类型转换
常见的转换主要就是int转换为string,string转换为int。
int转string:
1 | $var = 5; |
string转int:intval()函数。
对于这个函数,可以先看2个例子。
1 | var_dump(intval('2')) //2 |
说明intval()转换的时候,会将从字符串的开始进行转换知道遇到一个非数字的字符。即使出现无法转换的字符串,intval()不会报错而是返回0。
官网注释为:
1 | It seems intval is interpreting valid numeric strings differently between PHP 5.6 and 7.0 on one hand, and PHP 7.1 on the other hand. |
bool类型的true比较
bool类型的true可以和任意字符串弱类型相等
1 | <?php |
json绕过
json是一种轻量级的数据交换格式。它采用完全独立于编程语言的文本格式来存储和表示数据。
在 JS 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。但是对象和数组是比较特殊且常用的两种类型:
- 对象表示为键值对
- 数据由逗号分隔
- 花括号保存对象
- 方括号保存数组
1 | <?php |
输入一个json类型的字符串,json_decode函数解密成一个数组,判断数组中key的值是否等于 $key的值,但是$key的值我们不知道,但是可以利用0==”admin”这种形式绕过
php的json_decode()函数会根据json数据中的数据类型来将其转换为php中的相应类型的数据,也就是说,如果我们在json中传一个string类型,那么该变量就是string,如果传入的是number,则该变量为number。因此,我们如果传入一个数字,就可以使之相等。网页中的表单可能限制了所有的输入都是string,即使输入数字,传入的东西也是1
{"key":"0"}
这是一个字符串0,我们需要让他为数字类型,用burp拦截,把两个双引号去掉,变成这样:
1 | {"key":0} |
最终payload message={“key”:0}
内置函数的参数的松散性
md5()绕过
1 | <?php |
题目大意是要输入一个字符串和数字类型,并且他们的md5值相等,就可以成功执行下一步语句。
上文提到过,0e在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0。md5(‘240610708’) == md5(‘QNKCDZO’)成功绕过!
以0e开头的md5值:
1 | QNKCDZO |
当传递一个array时,md5()不会报错,但是会无法正确地求出array的md5值,这样也会导致任意两个array的md5值相等。
strcmp()漏洞绕过 php -v < 5.3
1 | <?php |
strcmp是比较两个字符串,如果str1< str2 则返回< 0,如果str1大于str2返回>0, 如果两者相等返回0。strcmp函数比较字符串的本质是将两个变量转换为ascii,然后进行减法运算,然后根据运算结果来决定返回值。
我们是不知道$password的值的,题目要求strcmp判断的接受的值和$password必需相等,strcmp传入的期望类型是字符串类型,如果传入的是个数组会怎么样呢?
1 | $array=[1,2,3]; |
所以我们传入 password[]=xxx 可以绕过,是因为函数接受到了不符合的类型,将发生错误,但是还是判断其相等。
payload: password[]=xxx(eg:password[]=admin)
switch()
1 | <?php |
switch()会将参数进行类型转换,在上述代码中将4admin转换成4,于是输出success。
in_array()
在PHP手册中,in_array()函数的解释是bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ),如果strict参数没有提供,那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strince的值为true时,in_array()会比较needls的类型和haystack中的类型是否相同。
1 | $array=[0,1,2,'3']; |
可以看到上面的情况返回的都是true,因为’abc’会转换为0,’1bc’转换为1。
array_search()与in_array()也是一样的问题。
参考:
https://www.secpulse.com/archives/69529.html
https://blog.spoock.com/2016/06/25/weakly-typed-security/