Author: LoRexxar'@Knownsec 404 Team
Chinese Version: https://paper.seebug.org/897/

On April 11th, 2019, ZDI Blog released a paper--A SERIES OF UNFORTUNATE IMAGES: DRUPAL 1-CLICK TO RCE EXPLOIT CHAIN DETAILED. What’s ingenious is that the attacker links a wonderful utilization chain by means of 3 vulnerabilities and a few small tricks. Next let’s take a full look towards this vulnerability.

Write an unsuffixed file

There is a rule set in the Drupal's mechanism. The name of the picture file uploaded by the user will be retained. If the file name is the same, it will be followed by _0, _1 in order. In order to be compatible with various encodings, when you upload a file name, Drupal will process it accordingly, converting it to _ if the value of a character is less than 0x20.

However, if there is a character ranging from \x80 to \xff in the file name, PHP will send PREG_BAD_UTF8_ERROR. If an error occurs, preg_replace will return "NULL" and $basename will be set to "NULL". When basename is null, the contents of the following files will be written to a file similar to _0. On that basis, it would have been uploaded:  /sites/default/files/pictures/<YYYY-MM>/ Now it will be written with:  /sites/default/files/pictures/<YYYY-MM>/_0 When the server opens the comment avatar upload, or if it has an account with author permission, the attacker can upload a maliciously constructed “gif” picture and the same picture with malicious characters, then the contents of malicious image will be written to _0 in the corresponding directory. But if we access this file directly, the file may not be parsed. The reasons are as follows: (1) The browser first parses the page based on the content-type given by the server, which typically does not set content-type or application/octet-stream to the unsuffixed file. (2) The browser will make a simple judgment based on the contents of the file. If the file starts with <html>, some browsers will parse it into "html". (3) Some browsers may also set a default content-type, but most browsers choose not to parse the file. At this time we need a very special trick: tag A can be set the type(only not for chrome)to open the file. When you access the page, it will be parsed into html and the corresponding code is executed. <html> <head> </head> <body> <a id='a' href="http://127.0.0.1/drupal-8.6.2/sites/default/files/2019-04/_6" type="text/html">321321</a> <script type="text/javascript"> var a = document.getElementById('a') a.click() </script> </body> </html> When the victim accesses the page, we can execute arbitrary "xss", which brings great convenience for subsequent utilization, so we have an arbitrary "js" execution point in a homologous environment. Phar deserialization RCE In BlackHat 2018, Sam Thomas mentioned that there was a Stream API in PHP(the issue: File Operation Induced Unserialization via the “phar://” Stream Wrapper), and the corresponding pseudo protocol can be registered through registration extension, and phar is registered with the stream wrapper as phar://. The security researcher Seaii in Knownsec 404 Team suggests that all file functions support stream wrapper (https://paper.seebug.org/680/). That is to say, if we find a controllable file manipulation function whose parameters can be controlled as phar file, then we can execute the command by deserialization. In Drupal, there is a file system function that will make a is_dir judgment to the incoming address, which exists the problems. Generate the files by using the following payload directly. <?php namespace GuzzleHttp\Psr7{ class FnStream{ public$_fn_close = "phpinfo";

public function __destruct()
{
if (isset($this->_fn_close)) { call_user_func($this->_fn_close);
}
}
}
}

namespace{
$phar = new Phar("phar.phar");$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub，增加gif文件头$o = new \GuzzleHttp\Psr7\FnStream();
$phar->setMetadata($o); //将自定义meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件 //签名自动计算$phar->stopBuffering();
}
?>

After modifying the suffix to "png", send the image to the server and set it in the file system.

phar://./sites/default/files/2019-04/drupal.png

And it can be triggered.

Vulnerability requirements

This vulnerability was fixed in the update of Drupal 8.6.6, so the vulnerability requirement is as follows:

• <= Durpal 8.6.6
• The server opens the function of commenting with picture or the attacker has an account with author permission.
• The victim needs to access the attacker's URL.

When the above three points are satisfied at the same time, the attack chain can be established.

Vulnerability patch

Write SA-CORE-2019-004 to the unsuffixed file

https://www.drupal.org/SA-CORE-2019-004

If the error occurs, throw it out directly and don't continue writing.

https://github.com/drupal/drupal/commit/82307e02cf974d48335e723c93dfe343894e1a61#diff-5c54acb01b2253384cfbebdc696a60e7

Phar deserialization SA-CORE-2019-002

https://www.drupal.org/SA-CORE-2019-002

Conclusion

It is not difficult to find that the whole vulnerability is composed of many small and inconspicuous vulnerabilities. Drupal's deserialized POP chain has been publicly available for a long time, and the phar vulnerability has been out for a year. At the beginning of 2019, Drupal also updated this point, and preg_replace would report and throw an error, which I don’t think it a special feature. Combine these three vulnerabilities with a very special tag A, set the content-type trick, a very wonderful vulnerability chain is thus formed.