PHP弱类型总结
1. 什么是弱类型
强类型语言是一种强制类型定义的语言,即一旦某一个变量被定义类型,如果不经强制转换,那么它永远就是该数据类型。代表有Java、.net、Python、C++等语言。
弱类型语言是一种弱类型定义的语言,某一个变量被定义类型,该变量可以根据环境变化自动进行转换,不需要经过现行强制转换。代表有VB,PHP,JavaScript等语言。
简单举一个例子:
1 </p><p style="margin: 0px 0px 10px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;">2 var A<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p><p style="margin: 0px 0px 10px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;">3 var B<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p><p style="margin: 0px 0px 10px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;">4 A=5<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p><p style="margin: 0px 0px 10px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;">5 B="5"<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p><p style="margin: 0px 0px 10px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;">6 sumA=A+B<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p><p style="margin: 0px 0px 10px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;">7 sumB=A-B<br style="margin: 0px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></p><p style="margin: 0px 0px 10px;padding: 0px;outline: 0px;max-width: 100%;box-sizing: border-box;overflow-wrap: break-word !important;clear: both;min-height: 1em;white-space: normal;">
sumA=55,系统默认+字符连接符,将A转化为字符串类型;而sumB=0;系统认为-是算数运算符,从而将B转化为int类型,所以sum为5-5=0;
2. PHP中“==”与“===”
php当中有两种比较符号==与===。
1 2 $a==$b; 3 $a===$b; 4 ?>
在进行===符号比较时,会先判断两种字符串类型是否相同,再比较值是否相同。
在进行==符号比较时,会将字符串类型转换成相同,再比较值是否相同。
1 2 var_dump("admin"==0);//true 3 var_dump("1admin"==1);//true 4 var_dump("admin1"==1)//true 5 var_dump("admin1"==0)//true 6 var_dump("0e123456"=="0e456789");//true 7 ?>
在进行"admin"==0 比较的时候,会将admin转化成数值,强制转化,由于admin是字符串,转化的结果是0自然和0相等。
当一个字符串被当作数值来取值,其结果和类型如下:如果该字符串没有包含'.','e','E'并且其数值在整型的范围之内该字符串被当作int来取值,其他所有情况都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0。因此"1admin"==1的结果为true,"admin1"==1的结果为false。
在进行"0e123456"=="0e456789"相互比较的时候,会将0e这类字符串识别为科学技术法的数字,0的无论多少次方都是零,所以相等。
3. md5绕过(hash比较缺陷)
以BugKu中的前女友题为例:
1 2 if(isset($_GET['v1']&&isset($_GET['v2'])&&isset($_GET['v3'])){ 3 $v1=$_GET['v1']; 4 $v2=$_GET['v2']; 5 $v3=$_GET['v3']; 6 if($v1!=$v2&&md5($v1)==md5($v2)){ 7 if(!strcmp($v3,$flag)){ 8 echo $flag; 9 } 10 } 11 } 12 ?>
这道题的关键是第6行,$v1弱不等于$v2并且$v1的md5值弱等于$v2的md5值
这里构造的思路也很简单,就是想办法使得md5($v1)与md5($v2)的值是以0e开头的字符串,在进行==符号比较的时候,PHP会0e这类字符串识别为科学技术法的数字,0的无论多少次方都是零,因此可以达到md5绕过的效果。
常见的字符串的md5值以0e开头的有QNKCDZO、s878926199a、s155964671a、s214587387a、s214587387a。
那么这题的WriteUp也就出来了
http://114.67.246.176:11589/?v1=QNKCDZO&v2=s878926199a&v3[]=1
4. switch绕过
以BugKu中的前女友题为例:
1 2 $a="3ctf"; 3 switch($a){ 4 case 1; 5 echo"fail1"; 6 break; 7 case 2; 8 echo"fail2"; 9 break; 10 case 3; 11 echo'flag{xxxxxx}';//结果输出success; 12 break; 13 case 4; 14 echo"fail4"; 15 break; 16 default; 17 echo"failall"; 18 break; 19 } 20 ?>
同样是弱类型的利用,当在switch中进行判断时,将$a强制转换成整型,也就是3,正好与case 3匹配上了,输出flag{xxxxx}。
5. Json绕过
if (isset($_POST['message'])) { $message = json_decode($_POST['message']); $key = "*********"; if ($message->key == $key) { echo "flag"; } else { echo "fail"; } } else { echo "~~~~"; } ?>
输入一个json字符串,通过json_decode()函数将其转换为数组,再判断数组当中key对应的值是否弱等于$key,如果相等,则输出flag的值。这里$key我们不知道,但是程序使用了==,我们就可以使用0="admin"这种形式进行绕过,因此最终的payload:message={"key":0}
6. strcmp函数绕过
strcmp(str1,str2)函数的作用是比较字符串。
如果 str1 小于 str2 返回 < 0;如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。
1 2 $password="***************" 3 if(isset($_POST['password'])){ 4 if(strcmp($_POST['password'],$password)==0){ 5 echo"Right!!!login succsess";n 6 exit(); 7 }else{ 8 echo"Wrong password.."; 9 } 10 ?>
这里程序的逻辑是,通过strcmp()函数比较POST方法传递的password的值与$password,如果strcmp()函数的返回值弱等于0,那么登录成功。
这里构造的思路是false==0,当strcmp()函数当中的str1与str2不为字符串时,strcmp()函数的返回值为布尔类型的false,那么false==0的结果是true,就可以达到绕过的效果,因此这里的payload:password[]=xxx(任意)。
7. “array_search"与is_array"绕过
is_array()函数会判断传入的数组是否是一个数组。如果是,返回true,反之false。
array_search()函数会在数组中搜索某个键值,并返回对应的键名。如果找到返回键名,反之false。这边要注意的是array_search()函数搜索键值的时候是使用==进行比较。
1 2 if(!is_array($_GET['test'])){ 3 exit(); 4 } 5 $test=$_GET['test']; 6 for($i=0;$i 7 if($test[$i]==="admin"){ 8 echo"error"; 9 exit(); 10 } 11 $test[$i]=intval($test[$i]); 12 } 13 if(array_saerch("admin",$test)===o){ 14 echo"flag"; 15 }else{ 16 echo"false"; 17 } 18 ?>
这里程序的逻辑是,首先判断GET方法传递的test是否为数组,然后通过for循环遍历数组当中每个值并判断是否强等于字符串admin,并且将值转换为整型。最后使用array_search函数在$test数组搜寻是否有键值弱等于字符串admin,如果有的话返回其键名,最后判断键名强等于0才输出flag。
因此构造思路是"admin"==0,也就是说传递的数组第一个键值必须是0(无论字符串还是整型,因为存在intval函数强制转换),那么payload:test[]=0就可以绕过。
8. 总结
PHP是最好的语言,而且使用很方便,但是特性是弱类型,以及内置函数对于传入参数的松散处理会导致存在许多安全问题,所以要特别注意。
