0x01 弱类型以及==和===

先看下强类型和弱类型的区别。

强类型指的是每个变量和对象都必须具有声明类型,是在编译的时候就确定类型的数据,在执行时类型不能更改,代表语言如Java、C等;而弱类型在执行的时候才会确定类型, 代表语言如PHP、Python等。

强类型较安全,而且效率高;弱类型相比而言不安全 。

==和===

===在进行比较的时候,会先判断两个变量的类型是否相等,再比较;==在进行比较的时候,会先将变量类型转化成相同,再比较。也就是说,相比之前,==并不会去比较两个变量的类型是否相等。

除此之外,还有!=和!==,原理和前面一样。

如果比较一个数字和字符串或者比较涉及到数字内容的字符串,则字符串会被转换成数值并且比较按照数值来进行:

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
?>
  • 观察上述代码,”admin”==0 比较的时候,会将admin强制转化成数值,由于admin是字符串,转化的结果是0,自然和0相等;
  • “1admin”==1 比较的时候会将1admin强制转化成数值,结果为1,而”admin1”==1等于错误,也就是”admin1”被强制转化成了0,为什么呢?——当一个字符串被当作一个数值来取值,其结果和类型如下:如果该字符串没有包含’.’、’e’、’E’并且其数值值在整形的范围之内,则该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0;
  • “0e123456”==”0e456789”比较的时候,会将0e这类字符串识别为科学计数法的数字,0的无论多少次方都是零,所以相等;

0x02 strcmp()

strcmp(str1, str2)函数用于比较两个字符串是否一致,若一致则返回0,若str1>str2则返回>0,若str1<str2则返回<0。

在PHP版本为5.3.3至5.5中(不包含5.5),当比较数组和字符串的时候,返回值也是0,即可以通过赋值其中一个参数为数组来Bypass:

1
2
3
4
5
6
7
8
9
<?php
$a = "2398ruuu31fj81k2rio8hg9302i9e1i2098t95yh";
$b = $_GET['b'];
if (!strcmp($a, $b)) {
echo "Bypass by strcmp()!";
} else {
echo "No...";
}
?>

在本地测试的PHP版本是5.6,处理弹出警告信息说第二个参数应该给string型外,还是能够成功Bypass:

0x03 intval()

intval(var)函数用于获取变量的整数值。在转换时,函数会从字符串起始处进行转换直到遇到一个非数字的字符,即使出现无法转换的字符串也不会报错而是返回0,从而可以导致如下情形的Bypass:

1
2
3
4
5
6
7
8
9
<?php
$a = $_GET['a'];
if (intval($a) === 666) {
$sql = "Select a From Table Where Id=".$a;
echo $sql;
} else {
echo "No...";
}
?>

0x04 md5()和sha1()

md5()和sha1()都用于计算字符串的散列值,但是两者都无法处理数组、不会抛出异常而是直接返回false。

如下情形,要求输入两个数使得一个参数的md5值和另一个参数的sha1值相等,当都输入数组时会导致NULL=NULL从而Bypass:

1
2
3
4
5
6
7
8
9
<?php
$a = $_GET['a'];
$b = $_GET['b'];
if (md5($a) === sha1($b)) {
echo "Bypass md5() and sha1()!";
} else {
echo "No...";
}
?>

0x05 is_numeric()

is_numeric()函数用于检测变量是否为数字或数字字符串。

但是函数的范围比较广泛,不仅仅是十进制的数字,其可被十六进制的值进行绕过,如下情形可造成二次注入:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$name = $_GET['name'];
$con = mysql_connect("localhost","root","hehe123");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}

mysql_select_db("test", $con);
if (is_numeric($name)) {
mysql_query("insert into users values (3," . $name . ",'test')");
}
?>

1′ union select 1,2,3的十六进制为0x312720756e696f6e2073656c65637420312c322c33

访问:http://127.0.0.1/x.php?name=0x312720756e696f6e2073656c65637420312c322c33

1
2
3
4
5
6
7
8
9
mysql> select * from users;

+----+-----------------------+----------+
| id | username | password |
+----+-----------------------+----------+
| 3 | 1' union select 1,2,3 | test |
+----+-----------------------+----------+

1 row in set (0.00 sec)

0x06 in_array()

in_array()函数用来判断一个值是否在某一个数组列表里面。其缺陷在于存在自动类型转换,如下当输入数字1后再紧跟其他字符串能够Bypass检测数组的功能:

1
2
3
4
5
6
7
8
9
<?php
$id = $_GET['id'];
if (in_array($id, array(1,2,3,4,5,6,7,8,9,0))) {
$sql = "Select a From users Where Id='".$id."'";
echo $sql;
} else {
echo "No...";
}
?>

0x07 ereg()和eregi()

ereg()和eregi()函数都用于正则匹配,两者的区别在于是否区分大小写,使用指定的模式搜索一个字符串中指定的字符串,如果匹配成功则返回true,否则返回false。

该函数可被%00截断来Bypass:

1
2
3
4
5
6
7
8
9
<?php
$passwd = $_GET['passwd'];
if (@ereg("^[a-zA-Z0-9_]+$", $passwd)) {
$sql = "Select username From users Where password='".$passwd."'";
echo $sql;
} else {
echo "No...";
}
?>

0x08 __wakeup()

__wakeup()作为反序列化中的一个魔法函数,自unserialize()从字节流中创建了一个对象后,程序会马上检测是否具有__wakeup()函数存在。若存在,__wakeup()函数会立即被调用。

使用__wakeup()函数的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务。

在如下情形中,在序列化字符串中,前面的数字代表的是后面字符串中字符的个数,如果数字与字符个数不匹配的话,就会报错,因此将1改成2会产生报错,导致不会去执行__wakeup()函数,从而Bypass:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class Mi1k7ea
{
public $text = "h12r0h1f0jfj";
public function __wakeup()
{
exit("[!]Bad Request.");
}
}

// echo serialize(new Mi1k7ea());
// O:7:"Mi1k7ea":1:{s:4:"text";s:12:"h12r0h1f0jfj";}

echo unserialize($_GET['flag']);
echo "Bypass __wakeup()!";
?>

输入正常的序列化内容是会执行__wakeup()函数的:

输入不正常的序列化内容,将数字改为与字符个数不匹配则可以Bypass:

0x09 json_decode()

json_decode()函数用于对json格式数据进行json解码操作,对于一个json类型的字符串,会解密成一个数组。

其存在一个0==”efeaf”的Bypass缺陷:=

1
2
3
4
5
6
7
8
9
10
11
<?php
$key = "JsonTest";
if (isset($_GET['data'])) {
$data = json_decode($_GET['data']);
if ($data->key == $key) {
echo "Bypass json_decode()!";
} else {
echo "No...";
}
}
?>

0x10 md5()哈希碰撞

md5()哈希碰撞是基于弱类型==或!=的。

下面看个题目,大意是要输入一个字符串和数字类型,并且他们的md5值相等,就可以成功执行下一步语句 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
if (isset($_GET['username']) && isset($_GET['password'])) {
$logined = true;
$username = $_GET['username'];
$password = $_GET['password'];

if (!ctype_alpha($username)) {$logined = false;}
if (!is_numeric($password) ) {$logined = false;}
if (md5($username) != md5($password)) {$logined = false;}
if ($logined){
echo "successful";
}else{
echo "login failed!";
}
}
?>

前面提到过,0e在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0。md5(‘240610708’) == md5(‘QNKCDZO’)成功绕过:

介绍一批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
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

s1502113478a
0e861580163291561247404381396064

s1885207154a
0e509367213418206700842008763514

s1836677006a
0e481036490867661113260034900752

s155964671a
0e342768416822451524974117254469

s1184209335a
0e072485820392773389523109082030

s1665632922a
0e731198061491163073197128363787

s1502113478a
0e861580163291561247404381396064

s1836677006a
0e481036490867661113260034900752

s1091221200a
0e940624217856561557816327384675

s155964671a
0e342768416822451524974117254469

s1502113478a
0e861580163291561247404381396064

s155964671a
0e342768416822451524974117254469

s1665632922a
0e731198061491163073197128363787

s155964671a
0e342768416822451524974117254469

s1091221200a
0e940624217856561557816327384675

s1836677006a
0e481036490867661113260034900752

s1885207154a
0e509367213418206700842008763514

s532378020a
0e220463095855511507588041205815

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s214587387a
0e848240448830537924465865611904

s1502113478a
0e861580163291561247404381396064

s1091221200a
0e940624217856561557816327384675

s1665632922a
0e731198061491163073197128363787

s1885207154a
0e509367213418206700842008763514

s1836677006a
0e481036490867661113260034900752

s1665632922a
0e731198061491163073197128363787

s878926199a
0e545993274517709034328855841020