0%

PHP Language Features Summary

弱类型

两种判等类型

php有两种判等的类型,=====

其中,===是强类型的判等,会先判断两边类型是否相等,之后在进行比较。

然而,==是弱类型的判等,会发生隐式类型转换后进行比较。

NULL、0、”0”、false

首先给大家看个好玩的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
var_dump(0=="0"); // bool(true)
var_dump(0==NULL); // bool(true)
var_dump(0==false); // bool(true)

var_dump("0"==false); // bool(true)
var_dump("0"==NULL); // bool(false)
var_dump("0"==0); // bool(false)

var_dump(NULL=="0"); // bool(false)
var_dump(NULL==0); // bool(true)
var_dump(NULL==false); // bool(true)

var_dump(false=="0"); // bool(true)
var_dump(false==NULL); // bool(true)
var_dump(false==0); // bool(true)
?>

字符串与数字的弱类型判等

1
2
3
4
5
6
7
<?php
var_dump("admin"==0); //true
var_dump("1admin"==1); //true
var_dump("admin1"==1) //false
var_dump("admin1"==0) //true
var_dump("0e123456"=="0e4456789"); //true, 0e123456 = 0e4456789 = 0 in scientific notation
?>

"1admin"==1"admin1"==0说明字符串隐式转数值时只看最前面的合法可转的数值表示。

应用

MD5碰撞绕过

当我们需要判断两个字符串的MD5值是否相同,我们通常会这么做:

1
2
3
...
if (md5($Username) != md5($Password)) {$logined = false; }
...

那么利用上面"0e123456"=="0e4456789"的性质,如果存在两个字符串,它们的前两位都恰好是0e,那么就成功绕过了这个分支。

几个MD5值以0e开头的字符串:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

MD5和双MD5都是0e开头的几个值:

1
2
3
CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk
7r4lGXCH2Ksu2JNT3BYM

md5前后都是0e开头的值:

1
2
0e215962017
0eRnJWi9XnKd9Z7x

而如果想要达成这样的条件:

1
2
3
if ($param1 !== $param2 && md5($param1) === md5($param2)) {
// then
}

则可以将$param1$param2定义为含有不同元素的两个数组。由于md5函数传入数组会返回null,两个强类型条件都得到满足。

不过如果两个参数都被强转了就没法传入数组来绕过,类似如下情况:

1
2
3
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
}

只能用一组特别特殊的md5碰撞值来绕过:

1
2
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2
b=b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

json绕过

1
2
3
4
5
6
7
...
$message = json_decode($_POST['message']);
$key ="*********";
if ($message->key == $key) {
echo "flag";
}
...

虽然不知道$key的值,但是可以赌他不以数字开头,只要他不以数字开头,一旦发生弱类型的隐式转换,只要令$message->key为0就可以绕过了。

Reference