一.什么是upload-labs?
upload-labs是一个使用php语言编写的,专门收集渗透测试过程中遇到的各种上传漏洞的靶场。目前一共19关,每一关都包含着不同上传方式
二.漏洞
三.安装环境
文件上传漏洞总结:upload-labs
安装环境:
1.phpstudy
2.upload-labs靶场
3.burp suite 安装(自行安装,忘记保存安装链接了)
1.phpstudy安装
windows安装网址(本人使用的安装网址):https://www.xp.cn/download.html
Linux 推荐集成环境Xampp,搭建方法参考上篇:
https://blog.csdn.net/qq_36711453/article/details/83713631
安装成功之后打开phpstudy.exe文件,如图,即安装成功
2.upload-labs靶场安装
Upload-labs是一个所有类型的上传漏洞的靶场
项目地址:https://github.com/c0ny1/upload-labs
打开网址
点击download ZIP即可安装
注:upload-labs靶场安装包下载完之后,解压到phpstudy中的www同目录下,并将其名字解压后的文件名中的master去掉:
在浏览器直接打开:
http://localhost/upload-labs/ 或者:http://127.0.0.1/upload-labs/
四.小试牛刀
第一关 pass-01 客户端检测绕过(js检测)
第一关:pass-01 客户端检测绕过(js检测)
访问Pass-01 ,打开源码:
查看到允许上传的文件后缀为:jpg/png/gif这三种
若想上传一个php文件,直接将该文件的后缀改为以上三种中之一,就可以成功上传。
上传文件名后缀为jpg的文件,使用burp suite进行抓包(使用火狐代理服务器插件)
在浏览器打开http://localhost/upload-labs/upload/1.jpg打开可以看到图片,说明文件上传成功
尝试上传文件后缀为php的文件,显然直接上传该文件是不成功的。
方案:首先,先将该文件名的后缀改为jpg ,使用抓包软件burp ruite进行抓包
将抓到的包的文件名后缀改为php,并进行放包,这样文件就上传成功了
验证:在浏览器打开http://upload-labs/upload/2.php,可以查看到该文件,说明文件上传成功
第二关 pass-02 content-type(服务器端检测–MIME 类型)
在HTTP 协议中,使用Content-Type 字段表示文件的MIME 类型。当我们上传文件的时候,抓到HTTP 数据包
常见的媒体格式如下:
进入正题:
源代码如下:
分析:由该源代码可以看出该服务器端将会对该content-type类型进行检查,从中可以看出image/jpeg image/png image/gif文件类型content-type为以上,文件即可上传成功
那么解题思路就可以Get到了:
首先先随便下载一个php文件(或者自己写的),打开代理服务器,phpstudy,打开burp suite,将php文件上传,使用burp suite软件进行抓包,因为上传的是Php文件,因此content-type的类型不是服务器认可的类型,那么我们就可以使用burpsuite进行修改该文件类型,并将该文件进行上传。
鼠标右键->发送给Repeater
此时文件类型并不是服务器所允许的,那么我们可以尝试更改它的类型,使它可以成功上传
将文件类型修改为image/jpeg并点击发送就可以看到右边的响应数据
最后进行放包,在浏览器打开http://localhost/upload-labs/upload/1.php并打开upload所在文件夹,可以看到该文件上传成功。
第三关 上传可解析的文件后缀名
上传可解析的文件后缀名
黑名单绕过:本题不允许上传文件名后缀为asp|.aspx|.php|.jsp的文件
虽然用黑名单不允许上传.asp,.aspx,.php,.jsp后缀的文件
但可以上传.phtml .phps .php5 .pht
主要代码如下:
首先查看apache配置文件中的配置是否允许该文件名后缀
解决方案:直接将php文件的文件名后缀改为phtml,直接将该文件进行上传,如果出现不能解析php代码,可以采用下面这个靶场:
BUUCTF在线评测
BUUCTF 是一个 CTF 竞赛和训练平台,为各位 CTF 选手提供真实赛题在线复现等服务。
https://buuoj.cn
上传文件之后,很可能文件名会被更改,因此要确定是否上传文件成功,只需要在上传的文件图片那里右键,在新标签页中打开图像,就可以了。
第四关 黑名绕过 .htaccess
主要代码如下:
$deny_ext中的文件后缀都是不允许的,本题不允许上传文件后缀为phtml,htm,php3…
黑名单中没有禁止.htaccess
了解一下.htaccess
文件上传之 .htaccess文件getshell_buffedon的博客-CSDN博客_htaccess文件
https://blog.csdn.net/weixin_46684578/article/details/119141109
在这里插入代码片
$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); //收尾去空
那么解决方案就来了:可以先上传一个文件名为.htaccess的文件,该文件内容为:
<FilesMatch “phpinfo.jpg”>
SetHandler application/x-httpd-php
FilesMatch中的文件为允许上传的文件,因此我们只要将php代码写在记事本中,再将其文件名改为phpinfo.jpg,然后再将其上传就可以解决问题啦。
第五关 黑名单 大小写绕过
黑名单中.htaccess已被列出,不允许上传该文件
可以看出,该代码中禁用后缀名为.php|.php5…|pHp…,该代码没有将文件名转化为小写。
但没有禁用.PHP|.Php|.phP
主要代码如下:
$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); //首尾去空
那么解决方案就出来了:直接将php文件的文件后缀名改为.PHP|.Php|.phP,然后将文件上传就可以了,问题解决。nice!!!
第六关 黑名单 空格绕过
主要代码如下:该黑名单中不仅包含有.htaccess文件,并且还将其文件名转化为小写
因此不能通过更改大小写实现绕过,但是该题目中没有对空格进行首尾进行去空格
$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
那么解决思路就来啦:我们可以直接写一个php文件,然后在php文件后缀名.php在加一个空格,然后将该文件上传,然后就发现了报错,很有可能是我们在上传文件的时候,Windows系统会将空格删除掉,那么这时我们可以尝试一下使用burp ruite进行文件上传,首先先上传一个后缀名为.php的文件,用burp ruite进行抓包,在抓到的文件的后缀名后面加空格,文件就上传成功啦!!!
问题解决,那么我们进入下一关啦啦啦啦啦!!!!
第七关 黑名单 点绕过
主要代码如下:黑名单中包含了.htaccess文件,因此不能使用.htacess,并且该代码将文件的大小写全部改为小写,并且进行了首尾去空格,因此大小写绕过,空格绕过也不能考虑,但还是抓到了一个Bug,该代码并没有进行删除文件名末尾的点
$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); //首尾去空
还是一样,不能直接上传文件名.php.文件,因为windows系统会将文件名后缀的点删除
那么解决方案就来啦:我们直接上传文件后缀名为.php的文件,使用burp ruite进行抓包,并将抓到的文件的后缀名在加点,这样就成功啦啦啦。
上传成功的结果如下:
第八关 黑名单 ::$DATA
源代码如下:文件大小写,空格,点绕过都已经不可行了,但该黑名单没有::$DATA
在后缀名加入::$DATA,实现将当前文件不会进行黑、白名单检测
(此种方法仅仅针对windows平台+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 = 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代码中没有对::
D
A
T
A
进
行
过
滤
,
在
w
i
n
d
o
w
s
中
会
将
文
件
名
:
:
DATA进行过滤,在windows中会将文件名::
DATA进行过滤,在windows中会将文件名::DATA之后的数据当成文件流处理,保持::
D
A
T
A
之
前
的
文
件
名
。
那
么
我
们
可
以
使
用
B
u
r
p
上
传
文
件
名
为
x
x
.
p
h
p
:
:
DATA之前的文件名。那么我们可以使用Burp上传文件名为 xx.php::
DATA之前的文件名。那么我们可以使用Burp上传文件名为xx.php::DATA的文件,实际上传的是xx.php
实操如下:
记得把.php后面的::$data去掉,这样文件就上传成功啦啦啦啦!!!
第九关 点 空格绕过黑名单
php代码中在后端使用了黑名单,过滤了大小写,点,空格,但未对文件重命名,并且只过滤了一边点和空格
主要代码如下:
$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;
那么解决方案来啦:我们使用burp 进行抓包,在抓到的文件名后再加.空格.(xx.php. .),这样过滤完之后该文件命名为:xx.php.,黑名单匹配不到,从而放行,文件上传至保存目录时,windows会自动清除后缀名中的点,使得文件可以正常使用。
实操结果如下:
文件上传成功啦啦啦啦!!!进入下一关啦啦啦!!!!
第十关 双写绕过黑名单
代码如下:
$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 . '文件夹不存在,请手工创建!';
}
}
代码中str_ireplace函数对该数组中的文件后缀名进行替换,用空格替换掉该数组中的文件后缀名。
关于php 中str_ireplace函数,可以了解以下链接:
https://www.w3school.com.cn/php/func_string_str_ireplace.asp
那么解决思路来啦:
直接上传文件名后缀为.php的文件,使用Burp进行抓包,将抓到的文件名后缀更改为.pphphp,上传即可成功!!!
第十一关 白名单 %00截断
首先了解一下00截断的原理https://blog.csdn.net/weixin_44840696/article/details/90581104
代码如下:
该php代码中只设置了允许通过的(白名单)文件后缀名为: jpg|png|gif, 并且$img_path = $_GET[‘save_path’]将获取到的文件路径进行拼接。
$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类型文件!";
}
}
那么解决方案来啦:
那么既然直接将获取到的文件上传路径拼接,那么我们可以直接更改文件上传路径,并且将我们不需要的东西采用00截断的方式,因为00截断的原理,在后缀中插入一个空字符(不是空格),会导致之后的部分被丢弃,而导致绕过的发生
这种情况常出现在ASP程序中,PHP 版本<5.3.4时也会有这个情况,JSP中也会出现。大概了解了,那么我们来进行实操。
实操:
1.需要将php的版本切换到<5.3.4
2.需要将php.ini文件中magic_quotes_gpc=Off
3.加入我们要上传一个php文件,那么我们在上传文件的窗口进行上传该文件,使用burp进行抓包,将抓到的文件的后缀名改为jpg|png|gif,将文件上传路径改为upload/xx.php%00,然后进行放包。
然后我们只需要将4.php后面的东西删掉就可以啦啦啦
第十二关 白名单 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类型文件!";
}
}
其实截断原理都一样,%00只不过是对ascii码中的0对应的字符编码后的结果。0x开头表示16进制,0在十六进制中是00, 0x00则是%00解码成的16进制
注意:当url中的参数是通过GET方式获取时,%00会被自动解码。而当参数是通过POST方式获取时,是不会自动解码的,也就是说%00只会原样被当成字符串来输出。所以通过POST方式请求此类参数的时候,我们需要手动将它的十六进制改写为0x00
实操:
1.PHP的版本要小于5.3.4和在php.ini文件中 magic_quotes_gpc=Off。
2.上传php文件,使用抓包软件进行抓包
将图中的25改为00就可以了,接下来的步骤和第11关一致,就不给截图了
第十三关 图片马
代码如下:
该代码中对文件读取头两个字节,并以此判断文件类型
我们可以制作图片马
图片马实际上就是用一张图片和一个php文件(即我们需要的木马病毒)进行合成,最后合成一张带有php代码的图片。
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;
制作图片马
1.首先保证图片和php文件在同一个目录中(直接在相关的文件夹打开cmd)
2.copy 1.jpg/b+ 4.php 2.jpg 合成之后的2.jpg即我们需要的图片马
若出现这种情况,可以尝试提高php版本,我们对图片马进行上传,那么我们要如何进行解析该图片马中的php语句呢?
文件有包含漏洞的php文件include.php文件,简单来说就是在这个include.php文件中需要引用其他的应用程序,php中的应用程序是.php,也就是说它本来是想引用一个php文件,但漏洞就是,它不会识别什么是php文件,所以会将引用的文件当做php进行解析,我们就可以利用这个漏洞,将图片马最为它引用的文件,那么就可以解析图片马中的php代码喽!!!
采用include.php?file=upload/xxxx.jpg进行解析图片马,图片的名字可能重命名,可以直接在burp中查看文件名。
成功啦啦啦!!!!
第十四关 getimagesize()-图片马
代码如下:
function isImage($filename){
$types = '.jpeg|.png|.gif';
if(file_exists($filename)){
$info = getimagesize($filename);
//getimagesize获取宽度,高度,图片类型(文件类型)
$ext = image_type_to_extension($info[2]);
//$info[2]刚好是图片类型(文件类型)
//找出字符串$ext在$types中的位置,若该位置大于0,说明为该字符串中的某一种文件名后缀
if(stripos($types,$ext)>=0){
return $ext; //返回该文件后缀名
}else{
return false;
}
}else{
return false;
}
}
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){ //isset函数用于检测变量是否已设置并且非null
$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 = "上传出错!";
}
}
}
分析代码:本题中的PHP代码只对图片的前几个字节进行处理,并且只允许上传图片,那么我们任然可以采用和第13关一样的方式进行上传图片马,再用include.php进行解析图片马中的php代码。具体过程就不再演示了。
第十五关 图片马 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内置函数中用来获取图片类型,因此我们可以通过上传图片马来达到上传php木马的目的。当在上传图片马之前,需要打开php_exif,打开之后,步骤和第13关一样!!!记得还是要使用include.php进行解析!!!
第十六关 二次渲染绕过
代码如下:
$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的图片文件!";
}
}
分析:
该php代码中允许上传图片,但会对图片进行二次渲染,因此我们需要绕过二次渲染的部分,也就是在在二次渲染不会改变的部分加入我们需要的php木马的代码
满足move_uploaded_file就可以上传成功!!!
实操如下:
我们需要安装winhex软件,在winhex中打开图片,首先我们需要准备一个木马图(操作步骤和第13关一致),文件名后缀为gif的图片,下面两张图是进行二次渲染(左边)之后的和未进行二次渲染(右边)的图片的内容,因为制作图片马时,PHP代码放在了最后,在进行二次渲染时,该代码部分被改变了。
对比两张图片的内容,很容易在靠近头部的位置有很多位置是是二次渲染之后没有改变的部分,那么我们就可以尝试将我们的Php木马写入这段区间中喽!(注意:不要在头部插入Php代码,会破坏gif文件头,验证图片会失败,就上传不了图片喽)
将php代码隐藏在二次渲染不会改变的部分
再次将隐藏好Php代码的图片进行上传,发现该区间没有被改变,因此,图片马上传成功。
接下来就是使用Include.php包含漏洞的原理进行解析该图片马,成功啦啦啦!!!
第十七关 条件竞争
代码如下:
$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()函数进行删除该文件。首先,代码执行过程是需要消耗时间的,因此我们要在上传的php文件被删除之前,成功的解析访问它,因此我们需要使用burp进行发送无限多的数据包,并使用python脚本去连接该文件,以此来判断该文件是否上传成功,并且在浏览器不停访问该文件的默认上传路径,一直不停刷新,直至访问成功,实验就成功啦啦啦!!!!
点击attack进行攻击,无限次发包
使用以下的python进行判断,当文件上传成功时,会出现ok
import requests # 第三方库
import os # 为了让屏幕停留导入的库
url = "http://localhost/upload-labs/upload/4.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
os.system('pause') # 停留
疯狂点击刷新,直至访问到该php文件
成功啦啦啦!!!!
第十八关 上传重命名竞争
代码如下:
//index.php
$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 ){
$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" );
}
......
......
......
};
分析:
审计后发现文件的白名单只有以上这些
打开了完整的代码,发现文件上传保存的地址为空,也就是会将上传的文件保存和upload同级目录下,而不是子目录
最后上传的文件名会被改名!!!
查看了源码:发现该php代码会先对文件类型进行匹配,先检查文件类型,如果文件类型匹配成功之后,就会将该文件进行移动,并且移动到和upload的同级目录下面,然后再对该文件进行重命名。
重点:因此我们需要上传一个文件名后缀为白名单中存在的,否则,下面的过程都无法进行啦!!!浏览器遇到不能解析的后缀时,会向前解析,发现只有7z结尾的后缀不会被解析。所以我们可以考虑一下上传4.php.7z的文件,这时我们要利用上传重命名的竞争性,我们已经知道文件上传之后是先保存后再重命名,改名是需要时间的,那么我们就使用Burp无限上传4.php.7z的文件,这样我们就可以直接在浏览器进行访问http://localhost/upload-labs/upload4.php.7z直到访问成功(要一直疯狂刷新哦),那么文件就上传成功啦啦啦啦!!!
我们在重命名成功之前执行木马程序(其实就是在浏览器解析):
4.php.7z
<?php
echo md5(1);
fputs(fopen('shell.php','w'),'<?php phpinfo(); ?>');
?>
成功上传解析之后可以在upload同级目录下查看到shell.php文件,然后我们可以对该文件进行访问。
我们还可以采用以下python代码验证是否有上传成功:
# 注意:使用时记得删去注释,否则cmd窗口会一闪而过
import requests # 第三方库
import os # 为了让屏幕停留导入的库
url = "http://localhost/upload-labs/upload4.php.7z"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
os.system('pause') # 停留
文件上传成功并解析成功之后文件目录时这样的(发现了shell.php文件):
对shell.php进行解析
成功!!!
第十九关 黑名单绕过 00截断
代码如下:
$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 . '文件夹不存在,请手工创建!';
}
}
分析:
从源码看,服务器定义了一系列黑名单,并且都是小写的文件名后缀,save_name参数为我们保存的名称的参数。并且该代码并没有对文件类型进行处理,仅仅只是对保存的文件名做了黑名单处理,那么我们可以直接上传4.PHP文件,完全可以直接绕过(记得保存名称的后缀名要改为PHP)!!!!
上传成功!!!,可以直接在浏览器解析该文件!!
这是第一种解决方案,接下来是第二种,采用的00截断的方式:
分析:
mov_upload_file()函数中的Img_path是由post参数save_name控制地,因此我们可以利用这一漏洞,使用00截断(记得环境要符合)绕过:
上传图片马
将+号对应的2b改成00
将包上传,在浏览器解析,成功啦啦啦!!!!
第二十关 数组绕过
代码如下:
$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进检查,该代码允许的content-type类型:‘image/jpeg’,‘image/png’,‘image/gif’,end会判断该数组的最后一个元素是否合理,也就是图片格式的后缀名,我们可以直接设置数组中参数的值,只要保证最后一个参数能满足条件(jpg/png/gif)就可以成功绕过检验。
实操:
1.先准备好一个php文件
2.使用burp进行抓包,需要将该包中的content-type改成允许的类型,如:image/jpeg
其次,将索引[0]为xx.php/,索引[2]为jpg/png/gif如图:
然后进行放包,会发现生成了一个索引[0]的文件。将该文件进行解析,成功啦啦啦!!!!
靶场闯关就先告一段落喽!欢迎大家批评指正!!!一起学习啦啦啦!!!