uploadfile代码审计1-5
Pass-01
本关审计要点
主要了解前端JS函数功能是如何对上传文件进行检测的
源码
源码
function checkFile() {
//var 为js定义变量的关键字
//document 用来调用功能点函数实现具体功能的
//getElementsByName 方法返回带有指定名称的对象的集合,查询的是name属性的值
//定义一个file变量去获取上传的文件名,比如:9z1nc.jpg 第1个元素:9z1nc
var file = document.getElementsByName('upload_file')[0].value;
//如果file 文件为null或者空,弹出提示,返回false。
if (file == null || file == "") {
alert("请选择要上传的文件!");
return false;
}
//定义允许上传的文件类型
var allow_ext = ".jpg|.png|.gif";
//提取上传文件的类型
//lastIndexOf 从.开始向后匹配
//substring 提取.后的字符串
//将提取到的后缀名赋值给ext_name
var ext_name = file.substring(file.lastIndexOf("."));
//判断上传文件类型是否允许上传
//ext_name + "|"与.jpg|进行对比 不相同则返回-1 弹出errMsg
if (allow_ext.indexOf(ext_name + "|") == -1) {
var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
alert(errMsg);
return false;
}
}
allow_ext.indexOf(ext_name + "|") == -1
只要有.jpg|.png|.gif 就可以被匹配上
漏洞利用
利用思路
js前端校验,在代码中是获取上传数据包 Content-Disposition: form-data; name="upload_file"; filename="9z1nc.jpg"
Content-Disposition字段中的filename元素内容取它的后缀.jpg与allow_ext中的内容对比,正确即可上传,抓包后只需要修改filename=“9z1nc.jpg” 为"9z1nc.php"就可以利用了。
使用蚁剑生产php webshell,先将文件改成.jpg格式上传并使用burp抓包
在burp中修改后缀为php
找到当前上传的路径
成功访问
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.'文件夹不存在,请手工创建!';
}
}
漏洞利用
利用思路
代码中判断了content-type类型是否为image/jpeg,image/png,image/gif 中的一个,如果是则上传成功,不是则弹出提示。我们只需要抓包后修改content-type类型为这3个白名单中的任意一个即可上传成功。
原来content-type类型为Content-Type: application/ocatet-stream
修改为image/png
成功上传
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’)和处理掉了一些绕过方式(apache解析漏洞,windows文件流,大小写,双文件名)。但我们还可以利用其他的后缀名绕过代码的检测(php3、 php4、php5、pht、phtml、phps)
这里我们上传一个php5的文件,成功上传。
解析还需要改配置文件,这里就演示一下上传成功的效果
Pass-04
本关审计要点
本关主要关注move_uploaded_file()函数在上传文件时的移动操作
源码
$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 . '文件夹不存在,请手工创建!';
}
}
漏洞利用
利用思路
代码中deny_ext 黑名单后缀比较齐全,file_ext也过滤掉了一些绕过方式,但问题出现在$img_path = UPLOAD_PATH.'/'.$file_name;
文件上传后并没有进行重命名的操作,直接将文件保存到相应的路径。我们则可以先上传.htaccess文件(apache中才有),将白名单中的文件当做Php来解析。
.htaccess(看网上说要上传到网站根目录下才能执行,这里直接上传到当前路径下也可以被解析)
将png文件当做php来解析
AddHandler application/x-httpd-php .png
Pass-05
本关审计要点
了解.user.ini在文件上传中的作用
源码
$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 . '文件夹不存在,请手工创建!';
}
}
漏洞利用
利用思路
.user.ini文件是由用户自定义的php.ini文件,而我们可以利用auto_prepend_file与auto_append_file 可以在不改动任何页面的情况下,当需要修改顶部或底部require文件时,只需要修改auto_prepend_file与auto_append_file的值即可。
auto_prepend_file 在页面顶部加载文件
auto_append_file 在页面底部加载文件
可以看到黑名单也禁止了上传htacess文件,我们可以尝试上传.user.ini文件指定去包含xxx.png文件,而.user.ini文件它可以在php文件被执行的时候,去包含一个指定的文件当做代码执行,相当于require。
.user.ini
auto_prepend_file=9z1nc_5.png
这里需要注意 php要修改为非线程安全nts版本,还有测试的时候发现 是在执行上传路径中的php 会被包含成功
uploadfile代码审计6-11
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 . '文件夹不存在,请手工创建!';
}
}
漏洞利用
利用思路
代码中未使用strtolower()函数对上传的文件名后缀进行小写转换,所以我们可以通过大小写后缀绕过黑名单。
上传文件为9z1nc.pHP
Pass-07
本关审计要点
仔细查看过滤逻辑,以及过滤中用到的函数。
源码
$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 = $_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
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 . '文件夹不存在,请手工创建!';
}
}
漏洞利用
利用思路
代码中未使用trim()函数对上传文件的后缀名进行收尾去空,我们可以上传文件名为 9z1nc_7.php 的文件
访问成功
本地测试的时候发现 php版本7.1.9以上开始就会出现上传出错
Pass-08
本关审计要点
仔细查看过滤逻辑,以及过滤中用到的函数。
源码
$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 . '文件夹不存在,请手工创建!';
}
}
漏洞利用
利用思路
代码中未使用deldot()函数删除上传文件后缀末尾的.,我们可以上传9z1nc.php.文件,而windwos在读取文件的时候会去掉末尾的.号,上传文件时候php.绕过了代码的过滤,.号windows会自己去掉,所以可以成功访问到自己的webshell
上传成功(php 5.6.9) php7.4.30上传失败
Pass-09
本关审计要点
了解Windows中::$DATA特性
源码
$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 = strtolower($file_ext); //转换为小写
$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 . '文件夹不存在,请手工创建!';
}
}
漏洞利用
利用思路
:: D A T A W i n d o w s 会将 : : DATA Windows会将:: DATAWindows会将::DATA当成文件流来处理
例如:"9z1nc_9.php::$DATA"
Windows会自动去掉末尾的::$DATA
变成"9z1nc_9.php"
代码中未对:: D A T A 进行过滤,我们可以通过 : : DATA进行过滤,我们可以通过:: DATA进行过滤,我们可以通过::DATA绕过代码的限制,上传我们的webshell
上传成功,上传到服务器的文件在Windows中会自动去掉::$DATA
Pass-10
本关审计要点
仔细查看过滤逻辑,以及str_ireplace()函数的作用。
源码
$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 = strtolower($file_ext); //转换为小写
//str_ireplace 将::$DATA替换为空=去除字符串::$DATA
$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 . '文件夹不存在,请手工创建!';
}
}
漏洞利用
利用思路
代码首先会获取上传的文件名并赋值给file_name变量并且使用trim()函数去掉上传文件名两边的空格再使用deldot()函数删除文件名末尾的点号;strrchr()函数处理filename变量获取到的文件名,取最后一次.号到后面的字符串赋值给file_ext 变量,接着对file_ext 变量进行转换为小写,去除字符串::$DATA,首尾去空的操作。这里还有一个问题就是未对上传的文件名进行重命名的操作,导致成功上传
通过以上的代码逻辑,我们发现可以上传 名为**9z1nc.php.**空格. 的文件即可绕过代码逻辑,最后经过代码的处理 文件名为 9z1nc.php.
Pass-11
本关审计要点
仔细查看过滤逻辑,以及str_ireplace()函数的作用。
源码
$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","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess","ini");
$file_name = trim($_FILES['upload_file']['name']);
$file_name = str_ireplace($deny_ext,"", $file_name);
$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 = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
漏洞利用
利用思路
代码中的逻辑问题,$file_name = str_ireplace($deny_ext,"", $file_name);
将deny_ext定义的黑名单文件名进行替换为空“”,也就是我们上传9z1nc_11.php会变成9z1nc。但该代码逻辑并没有循环的去处黑名单后缀名,所以我们可以上传9z1nc_11.pphphp进行绕过。
uploadfile代码审计12-16
Pass-12
本关审计要点
白名单中文件上传的绕过方式
源码
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($temp_file,$img_path)){
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
}
}
漏洞利用
利用思路
strrpos() 函数查找字符串在另一字符串中最后一次出现的位置。echo strrpos("Hello world",w)+1;
//6。substr() 函数返回字符串的一部分。echo substr("Hello world",6);
//word。$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
如果我们的上传文件名为demo.php。strrpos($_FILES['upload_file']['name'],".")+1
获取.号的下标为5,取到后缀名php。in_array(
f
i
l
e
e
x
t
,
file_ext,
fileext,ext_arr)函数搜索数组ext_arr中是否存在指定的值$file_ext。
我们将filename 修改为9z1nc_12.jpg绕过白名单校验,在通过get请求上传文件名为9z1nc_12.php%00
由于在get传参时%00会被url编码转义一次变成0x00,而路径中如果遇到0x00空白字符会丢弃后面的内容
/upload/Pass-12/index.php?save_path=…/upload/9z1nc_12.php%00
filename=“9z1nc_12.jpg”