Pass-01 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验证源码,前端定义了一个函数先对文件是否为空进行判断,上传文件后对齐的文件格式做了个白名单,然后在白名单里面查找,白名单不存在的不能上传,很简单,这里没有在后台进行操作,仅仅只是前端进行了判断,我们在这里直接禁用此页面的JavaScript,从而能够绕过检测。这里推荐个chrome插件,toggle JavaScript,能够直接进行禁止该界面的JavaScript代码。
具体操作:
笔者这里只会在这里提及相关上传操作,后面基本以文字进行叙述
Pass-02 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.'文件夹不存在,请手工创建!';
}
}
源码分析:
在这里我们可以看到他对文件类型进行了判断,而判断标准都是看这个文件是不是jpg、jpeg、png、gif文件,没有进行黑白名单设置,仅仅只是使用php函数进行判断,只要是这个判断标准中的文件类型都会上传,而没有做后续操作,这里我们就可以想,我们是不是可以直接使用BP进行抓包,然后伪造文件类型就行了?当然是可行的了。
开启代理
上传成功
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 . '文件夹不存在,请手工创建!';
}
}
源码分析:
这里我们可以看到,源码中有个deny_ext拒绝的数组,表示在这里我们设置了一个黑名单,然后利用了trim、deldot、strtolow,str_ireplace,分别去掉了两个空格,删除后缀名的点,大小写等等,这就意味着我们不能通过点绕,空格绕过等操作(这些方式我们后面会提及),从后面的 if(!in_array($file_ext,$deny_ext)){ ……… }这行代码中是说,文件名不在黑名单中,然后就能上传,那么我们就去构造一个不是黑名单中的文件名后缀比如php5,php3等方式
Pass-04 .htaccess绕过
$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");
$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 = '此文件不允许上传!';
}
} else {
$msg = UPLOAD_PATH . '文件夹不存在,请手工创建!';
}
}
源码分析:
这里的黑名单更为详细,黑名单中包含了所有的可执行脚本语言的后缀名,包括后缀名变异,这里我们可以知道,想通过脚本语言来执行webshell是很难的了,但是还是有绕过思路的,这个时候我们就按照他的规矩来,我们上传一个图片,但是在上传图片时,我们先上传一个.htaccess文件,这是php中的一个配置文件,该文件中写入代码
<FilesMatch "123.jpg">
SetHandler application/x-httpd-php
这个代码的意思是,匹配到123.jpg这个文件的话,这个文件使用php的方式执行该文件,然后使用notepad++在图片后面写入木马,上传123.jpg
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 = 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文件,这就意味之,第四关的方法不可行了,怎么办?不好绕啊,但是有没有发现,我们之前审计的代码都有哪些对文件后缀的处理
1、 trim 删除文件名两侧的空格
2、 deldot删除文件名后面的点 ‘ . ’
3、 strtolower 文件名转换为小写
4、 strchr($file_name,’.’) 返回后缀名
5、 str_ireplace(‘::$DATA’,‘ ’,$file_ext)把文件后缀中的::$DATA转换成空字符串
但是我们这里仔细发现,他没有strtolower这个函数,那我们在这里就可以通过构造大小写来进行绕过了
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");
$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 . '文件夹不存在,请手工创建!';
}
}
源码分析:
和第五关一样我们之前审计的代码都有哪些对文件后缀的处理
1、 trim 删除文件名两侧的空格
2、 deldot删除文件名后面的点 ‘ . ’
3、 strtolower 文件名转换为小写
4、 strchr($file_name,’.’) 返回后缀名
5、 str_ireplace(‘::$DATA’,‘ ’,$file_ext)把文件后缀中的::$DATA转换成空字符串
这里没有对文件名后面去掉空格,那么我们可以通过构造空格来绕过
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");
$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 . '文件夹不存在,请手工创建!';
}
}
源码分析:
和第五关一样我们之前审计的代码都有哪些对文件后缀的处理
1、 trim 删除文件名两侧的空格
2、 deldot删除文件名后面的点 ‘ . ’
3、 strtolower 文件名转换为小写
4、 strchr($file_name,’.’) 返回后缀名
5、 str_ireplace(‘::$DATA’,‘ ’,$file_ext)把文件后缀中的::$DATA转换成空字符串
这里我们进行代码审计时,没有对点号 . 进行过滤,要知道我们的文件在存储计算机上时,你在文件后面添加一个点号,计算机会把点号自动给你删除的,这样我们可以利用这样的机制来上传木马
Pass-08 ::$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");
$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 . '文件夹不存在,请手工创建!';
}
}
源码分析:
和第五关一样我们之前审计的代码都有哪些对文件后缀的处理
1、 trim 删除文件名两侧的空格
2、 deldot删除文件名后面的点 ‘ . ’
3、 strtolower 文件名转换为小写
4、 strchr($file_name,’.’) 返回后缀名
5、 str_ireplace(‘::$DATA’,‘ ’,$file_ext)把文件后缀中的::$DATA转换成空字符串
我们这里发现,这里没有对 ::$DATA进行过滤,那么我们可以使用这个来进行绕过
Pass-09 . .绕过(点+空格+点)
$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 . '文件夹不存在,请手工创建!';
}
}
源码分析:
首先需要了解deldot函数,当deldot函数检测到末尾的第一个点时将继续从后向前检测,当检测到空格时就停下来,因此当后缀是php.[空格].时,经过deldot函数过滤后,后缀变成php.[空格],最终绕过黑名单,且上传上去的文件名为php.,在windows下会自动去除点。
Pass-10 双写绕过
$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");
$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 . '文件夹不存在,请手工创建!';
}
}
源码分析:
首先我们审计源码,这里少了很多我们之前遇到的对文件名过滤的措施,这里我们可以使用之前5-9关的方法来绕过,但是,有没有发现,这里出现了一个很粗暴的处理方式,就是匹配到黑名单里面的字符串,就把他替换成空字符串,那我们这里还能采取双写的方式来绕过,就比如,他会把php替换为空字符串,那么我这么构造后缀pphphp,那么被替换成空字符串后,仍然还是php的后缀
Pass-11 %00白名单截断
$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类型文件!";
}
}
源码分析:
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
这句代码含义还是表示返回文件类型,substr(str,num),返回str字符串中第num之后的字符串,strrops,返回一个数值,此处表示 . 出现的位置
源代码中,这里采用了白名单的方式,那么我们只能采取%00截断的方式才能进行绕过,在url中%00表示的ASCII码表示为0,而在url中0,作为特殊字符保留,表示字符串结束,所以在url中出现%00时就会被认为读取已经结束了
但是这个只存在于php5.3.4以下的版本,笔者这里的环境高于该环节,所以不好演示,具体方法为在post url后面添加%00
Pass-12 %00截断 POST型
同样是%00截断,但是这次不同的是save_path在post包里面,GET是可以把url自动转码的,但是POST方式不会自动将%00编码为空字符。因此,我们需要在burp中选中%00右击->url->urldecode go即可。
Pass13 – Pass16 图片马
具体源码分析就不展示了,其源码表示的就是匹配我们上传的文件是不是图片格式
使用二进制编译器notepad++在图片文件后面写上木马然后上传就行,具体怎么运行该木马,就需要使用文件包含的知识了,具体使用方法,比如我上传的是123.jpg,那么使用该木马则为 url + 123.jpg/111.php就能进行利用了
Pass17 条件竞争
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif');
$file_name = $_FILES['upload_file']['name'];
$temp_file = $_FILES['upload_file']['tmp_name'];
$file_ext = substr($file_name,strrpos($file_name,".")+1);
$upload_file = UPLOAD_PATH . '/' . $file_name;
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);
}
}else{
$msg = '上传出错!';
}
}
源码分析:
我们之前接触的代码的逻辑基本上是,先在后台中判断,然后再上传移动文件位置,但是我们这里发现个问题,此处的代码逻辑是先上传到服务器,上传完成之后,再去判断这个文件的后缀名是否合法,如果不合法就使用unlink函数来删除这个文件,所以说这个文件是能存在很短一段时间的,这里我们使用BP,只要利用竞争上传,上传的php文件内容为写入shell文件,然后不断的访问该文件,只要访问成功,便可以写入shell,直接利用burp的intruder模块上传文件,同时不停的访问这个文件。
Pass-18 条件竞争
$is_upload = false;
$msg = null;
if (isset($_POST['submit']))
{
require_once("./myupload.php");
$imgFileName =time();
$u = new MyUpload($_FILES['upload_file']['name'], $_FILES['upload_file']['tmp_name'], $_FILES['upload_file']['size'],$imgFileName);
$status_code = $u->upload(UPLOAD_PATH);
switch ($status_code) {
case 1:
$is_upload = true;
$img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
break;
case 2:
$msg = '文件已经被上传,但没有重命名。';
break;
case -1:
$msg = '这个文件不能上传到服务器的临时文件存储目录。';
break;
case -2:
$msg = '上传失败,上传目录不可写。';
break;
case -3:
$msg = '上传失败,无法上传该类型文件。';
break;
case -4:
$msg = '上传失败,上传的文件过大。';
break;
case -5:
$msg = '上传失败,服务器已经存在相同名称文件。';
break;
case -6:
$msg = '文件无法上传,文件不能复制到目标目录。';
break;
default:
$msg = '未知错误!';
break;
}
}
class MyUpload{
......
......
......
var $cls_arr_ext_accepted = array(
".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
".html", ".xml", ".tiff", ".jpeg", ".png" );
......
......
......
/** upload()
**
** Method to upload the file.
** This is the only method to call outside the class.
** @para String name of directory we upload to
** @returns void
**/
function upload( $dir ){
$ret = $this->isUploadedFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->setDir( $dir );
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkExtension();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
$ret = $this->checkSize();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// if flag to check if the file exists is set to 1
if( $this->cls_file_exists == 1 ){
$ret = $this->checkFileExists();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, we are ready to move the file to destination
$ret = $this->move();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
// check if we need to rename the file
if( $this->cls_rename_file == 1 ){
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, everything worked as planned :)
return $this->resultUpload( "SUCCESS" );
}
......
......
......
};
程序调用了一个MyUpload类,来判断即文件的状态,和上题一样,但是程序会首先调用isUploadedFile来判断是否属于白名单,然后会一步一步检查文件大小、文件是否存在等等,将文件上传后,对文件重新命名,同样存在条件竞争的漏洞。可以不断利用burp发送上传图片马的数据包,由于条件竞争,程序会出现来不及rename的问题,从而上传成功。