upload-labs是一个使用php语言编写,专注于文件上传漏洞的靶场。该靶场可以让练习者了解文件上传漏洞的原理、利用方法。
1.首先要下载phpstudy,解压安装
phpstudy下载链接:https://www.xp.cn/download.html
2.下载upload-labs源代码
下载链接:https://codeload.github.com/c0ny1/upload-labs/zip/refs/heads/master
将压缩包解压后的文件名改为upload-labs,然后放入phpstudy\www目录下
3.打开phpstudy面板,启动Apache+MySQL
4.在浏览器中输入http://127.0.0.1/upload-labs/
就会打开一下界面,就安装成功了
任务是上传一个webshell到服务器
为了方便观察,这里我们选择上传,代码成功执行后显示PHP服务器的配置信息
尝试上传phpinfo.php,发现上传失败
我们点击显示源码或查看提示,发现这一关是使用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;
}
}
在浏览器中禁用JavaScript
firefox浏览器按f12调出开发者工具-->调试器-->设置-->禁用JavaScript,重新上传phpinfo.php就可以成功上传了。
在url中输入文件保存的路径
成功执行
点击显示源码跟查看提示可以看出,这一关是通过数据包中的MIME类型验证
$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.'文件夹不存在,请手工创建!';
}
}
使用BurpSuite抓包
将Content-Type修改为允许上传的类型,(image/jpeg、image/png、image/gif)选其中一个修改,修改完后即可上传成功。
访问一下,成功执行
看一下源码,发现禁止后缀为'.asp','.aspx','.php','.jsp'的文件上传,属于黑名单验证。
$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 . '文件夹不存在,请手工创建!';
}
}
过关思路:这里黑名单定义不全,可以将后缀改为'.php5','.phtml','.pht'进行绕过。上传成功后可以直接连接,这里上传的文件名会改变成随机数字组合,不过返回的数据包中有回显,还是可以看到上传的文件名跟路径。
这里由于我环境的问题,操作没有成功。大家可以自行操作试一下
首先我们看一下源码,发现跟第三关一样,也是黑名单验证,只是限制的后缀更多了。
$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 . '文件夹不存在,请手工创建!';
}
}
这里还是因为我环境的问题,操作没有成功
过关思路:我们上传一个.htaccess后缀的文件(注意这里不能取文件名,就是.htaccess文件),文件内容为
SetHandler application/x-httpd-php
然后下载一个图片,将图片名改为1.jpg(对应.htaccess文件内容),用Notepad打开图片,在图片内插入php代码,上传图片后,访问一下1.jpg,图片内的php代码就会被执行。
$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 . '文件夹不存在,请手工创建!';
}
}
过关思路:这一关虽然有对文件后缀名的过滤比较全,但是没有循环过滤,我们可以通过. .进行绕过上传。将文件名后缀改为.php. .这里的验证过程首先是过滤了文件末尾的点,将后缀名改为小写,去除后缀名中的::$DATA,最后再首尾去空。由于这里只过滤了一次,所以将.php. .被过滤成.php.还是可以绕过这里的黑名单验证,最后上传成功。
访问一下
成功通关
$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 . '文件夹不存在,请手工创建!';
}
}
通过源代码可以看出,这一关没有对文件名转化为小写,直接将文件名改为phpinfo.PHP大写后缀即可上传成功。
这一关将后缀名转化为了小写,但是相对上一关少了一个首位去空的过滤。
我们这里抓包,在后缀名后面加一个空格,就可以绕过成功上传。
这一关通过源码可以看出少了一个删除末尾的'.'
$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_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,就可以上传成功
访问一下
成功连接
这里第十关和第五关重复了,按照第五关的方法过就可以了。
这一关的验证是将黑名单中的后缀名过滤一遍,比如说上传.php文件,他就会过滤掉php,我们可以双写php,就可以绕过。
这里它只过滤了一遍php,过滤后还是有一个php,这样成功上传
12关跟13关都是利用%00截断来绕过验证,这里我因为环境问题没有成功。
过关思路:通过将恶意文件名的结尾添加一个%00空字节,来使服务器认为文件名的结尾已经到达,而忽略后面的任何内容。
使用%00截断需要三个条件:
1.php版本低于5.2.6(php5.2.6及之前的版本都存在%00截断漏洞,5.3.0后的版本修复了这个漏洞)
2.文件上传的路径可控
3.magic_quotes_gpc为OFF状态(在php.ini中设置)
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;
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_type = getReailFileType($temp_file);
if($file_type == 'unknown'){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").".".$file_type;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
这一关通过提示可以知道这里有文件包含漏洞
直接制作图片马配合文件包含漏洞就可以过关了
图片马制作方法:
提前准备好一张图片跟一个一句话木马,打开cmd命令行输入“copy 1.png/b + 1.php 3.png”,生成3.png图片马。
用Notepad打开图片,在内容中写入一句话木马。
上传制作好的图片马后,配合文件包含漏洞直接访问上传的文件
成功过关
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
$ext = image_type_to_extension($info[2]);
if(stripos($types,$ext)>=0){
return $ext;
}else{
return false;
}
}else{
return false;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$temp_file = $_FILES['upload_file']['tmp_name'];
$res = isImage($temp_file);
if(!$res){
$msg = "文件未知,上传失败!";
}else{
$img_path = UPLOAD_PATH."/".rand(10, 99).date("YmdHis").$res;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = "上传出错!";
}
}
}
这一关我们看一下源码,这里是用getimagesize函数来验证接受的文件是否为图片信息,跟上一关的过关方法一样,制作图片马上传,配合文件包含漏洞就可以了
16关跟14、15关的方法一直,制作图片马上传配合文件包含漏洞,但是这里需要打开php_exif,在phpstudy里开启就可以了
开启之后重启一下就可以了,再上传图片马就过关了