30.松散比较==
error_reporting(0); highlight_file(__FILE__); if(isset($_POST['f1']) && isset($_POST['f2'])){ $f1 = (String)$_POST['f1']; $f2 = (String)$_POST['f2']; if(preg_match('/^[a-z0-9]+$/', $f1)){ if(preg_match('/^[a-z0-9]+$/', $f2)){ $code = eval("return $f1($f2());"); if(intval($code) == 'ctfshow'){ echo file_get_contents("flag.php"); } } } }
根据代码可知,f1和f2必须是字母和数字。if判断是弱等于,需要intval($code)
的值为0
intval() 成功时,返回参数的 integer 值,失败时返回 0。空的 array 返回 0,非空的 array 返回 1。 字符串有可能返回 0,取决于字符串最左侧的字符。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1
所以需要$f1($f2());
[system(system)] 的返回值,或者是字母开头的字符串,或者是空数组,或者就是0,或者FLASE。
f1=system&f2=system
31.取反~
error_reporting(0); highlight_file(__FILE__); if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){ $v1 = (String)$_GET['v1']; $v2 = (String)$_GET['v2']; $v3 = (String)$_GET['v3']; if(is_numeric($v1) && is_numeric($v2)){ if(preg_match('/^\W+$/', $v3)){ $code = eval("return $v1$v3$v2;"); echo "$v1$v3$v2 = ".$code; } } }
绕过return的方式: php中有个有意思的地方,数字是可以和命令进行一些运算的,例如 1-phpinfo();结合减号是可以执行phpinfo()命令的。(不一定是减号,还有加、乘、除号,若用加号。要用+,要进行URL编码,这是个特殊字符,不进行编码会当作空格)
32.
error_reporting(0); highlight_file(__FILE__); if(isset($_GET['v1'])){ $v1 = (String)$_GET['v1']; if(is_numeric($v1)){ $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d); sleep($d); echo file_get_contents("flag.php"); } }
?v1=0 八进制
?v1=0x0 16进制
?v1=0e123 科学计数法
PHP语言特性
一.弱类型
1.php语言中一些相等的值:
" == false == NULL = 'abc' = 0
'123' == 123
'123a' == 0
'0x01' == 1
'0e123456789' == '0e987654321'
["] == [false] == [NULL] == [0]
true == 1
2.== 与 === 两种符号的区别
"==" 在比较时会自动进行类型转化且不改变原来的值,容易存在漏洞
eg: $input == 1
如果$input输入的值为 1abc,则在比较时 1abc会被转换为 1.
而1又代表true,所以在进行if语句判断时,会满足条件,进而引发漏洞产生
再来一个案例
eg: $GET['a'] != $GET['b'] && md5($GET['a']) == md5($GET['b'])
MD5函数返回值时一个32位的字符串,如果这个字符串以'0e'开头的话.类型转换进制会将它识别为一个科学计数法表示的数字"0",给出两个不同的字符串
但当不是==而是===时
eg: $GET['a'] != $GET['b'] && md5($GET['a']) === md5($GET['b'])
刚才给出的两个字符串不能成功了,但是我们仍然可以利用PHP语言函数错误处理的特性: a[]=1&b[]=2成功绕过.
因为我们令MD5函数的参数为一个数组的时候,函数会报错并返回NULL值
函数参数虽然是两个不同的数组,但是函数的返回值是相同的NULL,因此可以绕过
同理,在程序中返回值判断错误的函数还有很多,eg: strpos函数
strpos函数 -- 查找字符串首次出现的位置
eg: strpos($str1,$str2) == false
当str1在str2开头时,函数的返回值是0
0==false,会造成逻辑以外的结果(如if)
3.截断
通过NULL(\x00)字符的时候,处理函数就会将它作为结束的标记,这个漏洞可以帮我们去掉变量结尾处不想要的字符
eg: ?file=etc/passwd%00其他字符
或者
利用路径长度绕过
eg: ?file=../../..//etc/passwd
当系统在处理过长的路径会选择主动截断它.
还要一个能造成截断的情况:不正确的使用iconv函数:
在变量file中包含非法utf-8字符的时候,iconv函数就会截断这个字符串
eg: iconv("UTF-8","gb2313",$file);
4.变量覆盖
(1) 函数使用不当
extract函数
<?php $auth = false; extract($_GET); if($auth){ echo "flag{...}"; }else{ echo "yuanshen"; } ?>
此外的extract函数将GET传入的数据转换为变量名和变量值,所以这里构造Payload即可将$auth的值变为true并获得flag
?auth=1
parse_str函数
<?php $auth = false; parse_str($_SERVER['QUERY_STRING']); if($auth){ echo "flag{...}"; }else{ echo "yausnhen"; } ?>
此外的parse_str函数同样也是将GET传入的字符串解析为变量,所以Payload与上方extract函数的Payload一样.
import_request_variables函数
<?php $auth = false; import_request_variables('G'); if($auth){ echo "flag{...}"; }else{ echo "yuanshen"; } ?>
此外 import_request_variables函数的值由G,P,C三个字母组合而成,G代表GET,P代表POST,C代表Cookies.
排在前面的字符会覆盖派在后面的字符传入参数的值,如:参数为"GP",且GET和POST同时传入了auth参数,则POST传入的auth会被忽略.
(2)配置不当
当php配置register_globals=ON时便可能出现该漏洞
<?php if($auth){ echo "flag{...}"; }else{ echo "yuanshen"; } ?>
利用register_globals的特性,用户传入参数auth=1即可进入if语句,需要注意的是,如果在if语句前初始化$auth变量,则不会触发这个漏洞
(3)代码逻辑漏洞
在讲述代码逻辑漏洞导致的变量覆盖之前,需要明白php中的$$(可变变量).可变变量可以让一个普通变量的值作为这个可变变量的变量名.
<?php $foo="hello"; //赋值普通变量 $$foo="world"; //使用foo变量的值作为可变变量的变量名 echo "$foo ${$foo}"; //输出:hello world echo "$foo $hello"; //等同于上面的语句,同样输出hello world ?>
但是新版本php移除了import_request_variables函数和register_globals选项之后
我们可以使用foreach遍历数组来注册变量,这样也会存在变量覆盖漏洞
<?php $auth=false; foreach($_GET as $key => $value){ $$key = $value; } if($auth){ echo "flag{...}"; }else{ echo "yuanshen"; } ?>
此外的forteach循环就将GET传入的参数注册为变量,所以和前面一样,传入"?auth=1"可绕过.