upload-labs Writeups
2023-06-15 17:08:29 # Web Security # File Upload

前言

最近在练习文件上传漏洞,正好拿upload-labs靶场练手。这里是在windows系统环境下进行的,建议直接使用作者提供的windows集成环境(之前我自己搭建的环境因为中间件和PHP配置不同,有些上传漏洞无法复原)。

Pass-01 - 前端JS后缀名检测绕过

解题思路

首先常规思路,选择一个shell文件上传,同时用Burp Suit抓包

image-20210714224722994

提示文件不允许上传,同时观察Burp Suite中并未抓到数据,说明此处是通过前端js文件来检查文件类型的。在chrome上用Network功能抓包,发现并未有任何js文件加载,于是猜测检测代码是直接写在当前页面的。

于是直接右键查看网站源代码,在源码底部可以看见我们需要的javascript代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script type="text/javascript">
function checkFile() {
var file = document.getElementsByName('upload_file')[0].value;
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
if (allow_ext.indexOf(ext_name) == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
</script>

我们把shell文件后缀修改为jpg,然后再上传抓包,在filename处将文件名后缀改回php。

image-20210714233757638

上传成功,复制图像链接即可获得shell文件地址。

方法总结

主要是绕过前端javascript的文件名检测,可以先改成图片的格式,绕过js后通过抓包再修改回来。

Pass-02 - Content-Type绕过

解题思路

直接上传php文件,出现image-20210715012843947

Burpsuite抓包发现被拦截,所以我们推测是后端代码检测文件类型。

Burpsuite抓包后直接修改Content-Typeimage/gif

image-20210715013059979

文件上传成功,成功绕过。

方法总结

根据其源码更好理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
if (($_FILES['upload_file']['type'] == 'image/jpeg') || ($_FILES['upload_file']['type'] == 'image/png') || ($_FILES['upload_file']['type'] == 'image/gif')) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}

可知其仅判断文件类型:$_FILES['upload_file']['type'],使用Burp Suite修改Content-Type即可。

Pass-03 - PHP文件的其他格式绕过

解题思路

先直接上传一个shell.php, 发现提示image-20210717095818207,抓包也抓到了,说明此处是后端检测后缀名,但它并未检测.phtml, .php3.php5这样的后缀,所以我们直接上传该格式即可。

如果不解析上述后缀,我们可以在httpd.conf添加 AddType application/x-httpd-php .php .phtml .php3 .php5

方法总结

看代码:

image-20210717101506612

采用黑名单时,过滤后缀不全,我们可以尝试.phtml, .php3.php5这样的后缀。

Pass-04 - .htaccess绕过(Apache 特性)

解题思路

先上传一个shell.php,提示image-20210717101648489,抓包抓到了,说明此处时后端检测文件。

再次按第三关的思路上传.phtml, .php3.php5这样的后缀,发现也被检测了。于是我们尝试上传**.htaccess**文件,内容为:SetHandler application/x-httpd-php

image-20210717102255081

再次上传任意后缀的shell木马,即可被解析成php文件

方法总结

概述来说,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

这一关我们就利用.htaccess文件指定所有文件后缀均解析为php,可见过滤.htaccess也尤为重要

Pass-05 - 大小写绕过

解题思路

观察源码:

1
2
3
4
5
6
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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并未被禁止,于是尝试抓包上传PHP后缀文件

image-20210724141808920

成功上传并得到文件地址,使用菜刀连接即可。

方法总结

这关虽然过滤了.htaccess,但与第四关不同的是,他去掉了$file_ext = strtolower($file_ext); //转换为小写的操作,导致我们能使用大写字符绕过检测。再次证明,采用黑名单的形式来检测文件上传是不可靠的!

Pass-06 - 后缀名末尾的空格绕过(Windows特性)

解题思路

观察源码:

1
2
3
4
5
6
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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 = $_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

我们发现此处与之前的关卡相比,少了首尾去空的操作:$file_ext = trim($file_ext); //首尾去空

那么这关是怎么处理文件名的呢 - 先删除末尾的点,查找到 . 最后一次出现的位置,获取从该位置开始一直到文件末尾的字符,再转换成小写、去掉::$DATA字符后得到最终的后缀名,去和上面的deny_ext做对比。那么我们可以想办法构造它的后缀名。

在Windows中,后缀名最后是不能带空格的,那么我们可以构造shell.php .,这样经过处理后,这里过滤器得到的后缀是.php[空格] ,而黑名单中并没有此文件,于是绕过了过滤。加上在Windows中,就算我们上传的后缀名尾部是带空格的,系统也会在保存文件的时候自动给我们去掉空格,这样我们可以直接访问shell.php

那么我们来测试下:

image-20210724143037092

成功连接菜刀。

image-20210724143207754

方法总结

学会阅读源码和查阅资料很重要。

比如

  • 这里的strrchr()方法是指查找到指定字符最后一次出现的位置,并获取从该位置开始一直到文件末尾的字符。

  • Windows中,后缀名最后是不能带空格的,会被自动去掉。

有能力通过了解作者的代码,找出逻辑漏洞,这是学会Web安全的必要条件。

Pass-07 - 后缀名末尾的点绕过(Windows特性)

解题思路

这一关,我们看源码,

1
2
3
4
5
6
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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_ext = strrchr($file_name, '.');
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

相比前一关,发现其并没有对末尾的点做处理。

根据Windows的特性 - 后缀名最后的点会被自动去掉,我们可以构造文件名shell.php.,这样获取到的后缀是.,能够绕过黑名单。在文件被保存在windows系统上后,后缀名最后的.会被自动去掉,我们访问shell.php即可。

演示:

image-20210724144725750

菜刀连接成功:

image-20210724144838086

方法总结

根据Windows的特性,后缀名最后的点会被自动去掉。利用逻辑漏洞解出题目。

Pass-08 - $DATA文件流绕过(Windows特性)

NTFS文件流

在做这关之前,我们需要了解以下NTFS文件流。

NTFS 是微软 NT 系列内核支持的较为安全先进的磁盘文件格式。 NTFS 数据交换流(简称 ADS )是 NTFS 磁盘的一个特性。每个文件都可以存在多个数据流,虽然我们无法看到数据流文件,但是它却是可以真实存在于我们的系统中的。

平时我们看见的文件,其实它的全名是这样的:

<filename>:<stream name>:<streamtype>

<文件名>:<流名称>:<流类型>

用户不能创建一个新的流类型,流类型都是以$开头。

所有的文件NTFS上,都至少包含一个流:主流(即DATA流)

默认的DATA流是没有stream name的,如果一个文件被指派了流,但该流没有stream type的话,在储存时会自动添加$DATA,但在访问查询的时候需要去掉$DATA

对于文件夹而言,没有DATA流,它的主流是directory流,( stream type$INDEX_ALLOCATION ), directory 流默认的 stream name$130

尽管文件夹默认没有 data 流,但用户可以指派 data 流。

解题思路

观察源码:

1
2
3
4
5
6
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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 = strtolower($file_ext); //转换为小写
$file_ext = trim($file_ext); //首尾去空

发现此处并未检测::Data

根据上文所讲,我们抓包修改上传文件名为shell.php::DATA,即指定文件名为shell.php,无流名,流类型为DATA

当我们上传以后,过滤器判断出后缀是.php::DATA,黑名单中未查询到,于是我们可以绕过检测。但Windows文件在保存的时候,该数据流的格式名不会显示,所以实际上还是以shell.php的名字保存在服务器上的。

那么我们就可以开始操作了:

image-20210724152210037

连接菜刀成功!

image-20210724152308213

方法总结

Windows上的NTFS文件流帮助我们绕过后缀检测。这个有时间要好好了解下,后期还会遇到和NTFS文件流有关的绕过。

Reference

Windows下的ADS NTFS交换数据流

Pass-09 - 点空格点绕过(Windows特性)

解题思路

观察源码,发现和pass07其实没有什么区别,这一关加了去掉末尾的点,但只去掉了一次。

1
2
3
4
5
6
7
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".pht",".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 = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空

如果我们文件名是. .,删除文件名末尾的点后,文件后缀变成了.[空格],接着去掉空格,最终检测的后缀名为.,这不是和Pass-09一样吗?

直接上burp修改:

image-20210729144343197

上传成功。

上菜刀即可

image-20210729144504045

方法总结

还是分析源码,找出逻辑问题。

了解Windows的特性,后缀名末尾的点或空格会被自动去掉。

Pass-10 - 关键字替换绕过

解题思路

我们直接上传一个php文件shell.php,发现上传成功。image-20210729150201358

复制看看文件地址,发现文件地址是http://127.0.0.1/upload/shell.后面的php被去掉了,那么我们猜测应该是触发了黑名单,然后后缀名被替换为空了。

观察代码:

1
2
3
4
$deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","pht","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);//替换deny_ext中的后缀为空

那么我们可以构造以下文件名

test.pphphp

这样检测到php字段并替换为空,这样我们上传的文件名仍然为test.php

image-20210729150320292

连接菜刀:image-20210729150350556

方法总结

直接替换非法后缀名是很不安全的,尽量使用白名单检测。

相对的,对于替换后缀名检测的方法,我们可以选择重复后缀进行绕过。

Pass-11

解题思路

直接上传PHP文件,发现只能上传图片格式

image-20210729150848554

抓包看看: