首先说下强弱类型,根据维基百科的定义:
In computer programming, programming languages are often colloquially classified as strongly typed or weakly typed (loosely typed). These terms do not have a precise definition, but in general, a strongly typed language is more likely to generate an error or refuse to compile if the argument passed to a function does not closely match the expected type. On the other hand, a very weakly typed language may produce unpredictable results or may perform implicit type conversion.
对于强弱类型,并没有明确的定义,但是可以通过函数传参进行判断:在某个编程语言里面,给某个函数传了一个不是所需类型的参数,如果编译器报错了,那么可以认为它是强类型语言(strongly typed),反之则认为是弱类型语言(weakly typed)。
这里我们先不直接下结论说PHP是什么类型的(当然大家都知道了),我们按照维基百科的标准来测试一下:
<?php $a = "string"; function test($b) { $b = $b + 1; echo $b; } test($a); ?>
最后的结果是在屏幕上输出1,并没有报错,所以PHP是弱类型语言。
弱类型语言在使用上更为方便快捷,但是同样也带来了一些弊端,也就是这篇文章要总结的PHP弱类型导致的漏洞:
1、使用松散比较“==”
(1)$a=null;$b=flase ;
(2)$a=”;$b=null;
(3)$a=’0′;$b=0;
(4)$a=’abcdefg’;$b=0;
(5)$a=’1abcdefg’;$b=1;
(6)$a=’0e391845′;$b=’0e1948284′;
(7)$a=’0x1240′;$b=123456;
以上这些当我们用$a==$b进行比较时,都会返回true,其中(6)中例如0e\b+这种形式的都会认为是0,所以返回true,这点特性在后面md5()中也会体现,(7)中0x\b+ PHP会认为是16进制,等于10进制的123456.
2、switch() 函数自动转换
在使用switch函数时,switch会把传入的参数经过intval处理,这样例如“0euqie”就会变成0,“1eidue”就会变成1,这样会导致运行错误的条件结果
3、md5()
md5函数在PHP中有两个问题,第一个问题我们前面提到型如0e\b+类型的变量都会认为相等,这样就导致了两个变量它们本身的值不相同,但是经过md5返回的哈希值都是0e\b+类型的,结果变成了“相等”的。例如在wargame.kr里面有一题:
<?php if (isset($_GET['view-source'])) { show_source(__FILE__); exit(); } if (isset($_GET['v1']) && isset($_GET['v2'])) { sleep(3); // anti brute force $chk = true; $v1 = $_GET['v1']; $v2 = $_GET['v2']; if (!ctype_alpha($v1)) {$chk = false;} if (!is_numeric($v2) ) {$chk = false;} if (md5($v1) != md5($v2)) {$chk = false;} if ($chk){ include("../lib.php"); echo "Congratulations! FLAG is : ".auth_code("md5_compare"); } else { echo "Wrong..."; } } ?> <br /> <form method="GET"> VALUE 1 : <input type="text" name="v1" /><br /> VALUE 2 : <input type="text" name="v2" /><br /> <input type="submit" value="chk" /> </form> <br /> <a href="?view-source">view-source</a>
经过测试,我们发现:
$ echo -n 240610708 | md5sum 0e462097431906509019562988736854 - $ echo -n QNKCDZO | md5sum 0e830400451993494058024219903391 - $ echo -n aabg7XSs | md5sum 0e087386482136013740957780965295 -
这三组最后生成的md5都是0e\b+形式的,所以我们最后构造v1=QNKCDZO 和 v2=240610708就能成功绕过判断
另外一个就是:PHP手册中的md5()函数的描述是string md5 ( string $str [, bool $raw_output = false ] ),md5()中的需要是一个string类型的参数。但是当你传递一个array时,md5()不会报错,知识会无法正确地求出array的md5值,这样就会导致任意2个array的md5值都会相等。
$array1[] = array( "foo" => "bar", "bar" => "foo", ); $array2 = array("foo", "bar", "hello", "world"); var_dump(md5($array1)==var_dump($array2)); //true
4、in_array()
在PHP手册中,in_array()函数的解释是bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] ),如果strict参数没有提供,那么in_array就会使用松散比较来判断$needle是否在$haystack中。当strince的值为true时,in_array()会比较needls的类型和haystack中的类型是否相同。
$array=[0,1,2,'3']; var_dump(in_array('abc', $array)); //true var_dump(in_array('1bc', $array)); //true
可以看到上面的情况返回的都是true,因为’abc’会转换为0,’1bc’转换为1。
array_search()与in_array()也是一样的问题