前言
最近强网杯还有其他比赛中,出现了许多php弱类型的题目。用这篇文章总结一下到目前学到的php弱类型。
基础知识
学过php的都知道,php中有两种比较符==和===。
== 在进行比较的时候,会先将字符串类型转化成相同,再比较
=== 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较
如果一个字符串和数字比较,则字符串会被转换成数值。例如:
1 2 3 4 5 6 7 8
| <?php var_dump("abc"==0); var_dump("1abc"==1); var_dump("0abc"==0); var_dump("abc1"==1); var_dump("abc0"==1); var_dump("0e123"=="0e456"); ?>
|
当”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弱类型的一点点知识而已,还有更多的东西没有写出来(其实我不会了)。
之前很早就想写这篇文章,一直拖到现在,不过也好,又学到了新的知识一起写到文章里了。
学习永远都是靠自己一个人,别人再厉害也只是别人而已,只有自己脚踏实地才能学到真的知识。