0%

php 弱类型和函数小结

弱类型

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

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

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

==和===

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

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

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

1
2
3
4
5
6
7
8
9
<?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
var_dump("0x123" == "291"); //true
var_dump(is_numeric("0x123")); //true
?>

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

php7

1
2
3
4
5
6
7
8
php > var_dump("0x123" == "291");
bool(false)
php > var_dump(is_numeric("0x123"));
bool(false)
php > var_dump("0xe" + "0x1");
int(0)
php > var_dump(substr("foo", "0x1"));
string(3) "foo"

可以发现16进制字符串已经不能被认为是数字。

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...";
}
?>

hash

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...";
}
?>

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)

in_array(search,array,type)
array_search(value,array,strict)

in_array()函数用来判断一个值是否在某一个数组列表里面。array_search() 函数在数组中搜索某个键值,并返回对应的键名。
其缺陷在于未指定第三个参数时,存在自动类型转换,相当于逐个 ==

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...";
}
?>

md5 sha1 0e 弱类型碰撞

又可参见:https://www.whitehatsec.com/blog/magic-hashes/
该情形下的哈希碰撞是基于弱类型==或!=的。

下面看个题目,大意是要输入一个字符串和数字类型,并且他们的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!";
}
}
?>

1
2
3
4
5
6
7
8
9
10
11
md5
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020

sha1
10932435112
0e07766915004133176347055865026311692244

Ref

大部分内容转载自来源 1,只进行了少量修改。
1.https://www.mi1k7ea.com/2019/06/21/PHP%E5%BC%B1%E7%B1%BB%E5%9E%8B%E5%B0%8F%E7%BB%93/
2.很多之前的笔记,无法一一列举