Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Compromising 470,000+ Wordpress Sites the Lazy Way

ajxchapman
January 08, 2016

Compromising 470,000+ Wordpress Sites the Lazy Way

ajxchapman

January 08, 2016
Tweet

More Decks by ajxchapman

Other Decks in Technology

Transcript

  1. The Target ▪ “WordPress is used by 25.4% of all

    the websites”[1] (Top 10 million websites in Alexa) – Many Fortune 500 users • Disney [https://thewaltdisneycompany.com/] • Sony [http://blog.us.playstation.com/] • Microsoft [http://news.microsoft.com/] – Many not so Fortune 500 users • Oh She Glows [http://ohsheglows.com/] • Carolina Roller Girls [http://www.carolinarollergirls.com/] • Kiss My Spatula [http://kissmyspatula.com/] 07/01/2016
  2. The Target ▪ WordPress is a free and open source

    CMS framework (not just for blogging about your cat) built on PHP and MySQL ▪ Open Source maintained by WordPress Foundation and Automattic. – In development since 2003 as a fork of b2/cafelog – GPLv2+ licensed 07/01/2016
  3. The Problem ▪ Has a large number of community provided

    plugins which run with the same permissions and privileges as the WordPress core(!) ▪ Wordpress.org boasts “41,983 plugins with 1,124,700,998 total downloads” [2] – Everything from “WooCommerce” to “Internet Defense League Cat Signal” – Plugins hosted on Wordpress.org (the primary plugin repository) are all open source ▪ I asked myself a question – “How many of these plugins contain critical security vulnerabilities?” 07/01/2016
  4. The Rich Man’s Solution 07/01/2016 I was going to write

    a slide about how expensive these all were, but couldn’t find prices online and none of my emails asking for quotes were replied to •
  5. The Poor Man’s Solution 07/01/2016 system ( "ls –la" );

    grep -zE 'system[[:space:]]*\([[:space:]]*[^)]+[[:space:]]*\)'
  6. $directory = $_GET["directory"]; system("ls -la $directory"); The Poor Man’s Solution

    07/01/2016 $func = "system"; $func("ls -la" . $_GET["directory"]); * For certain SERVER variables
  7. $directory = $_GET["directory"]; system("ls -la $directory"); $func = "system"; $func("ls

    -la" . $_GET["directory"]); The Poor Man’s Solution 07/01/2016 function run_command($command) { system($command); } run_command("ls -la " . $_GET["directory"]); * For certain SERVER variables
  8. The Poor Man’s Solution 07/01/2016 * For certain SERVER variables

    $directory = $_GET["directory"]; system("ls -la $directory"); $func = "system"; $func("ls -la" . $_GET["directory"]); function run_command($command) { system($command); } run_command("ls -la " . $_GET["directory"]);
  9. The MWFTMTOHH* Solution! ▪ I’m lazy (for a certain definition

    of lazy) – Why do something once quickly when you can spend weeks automating it in case you ever have to do it again? ▪ Let’s write a PHP Code Analyser – How hard can it be? • (It turns out quite hard!) 07/01/2016 * Man With Far Too Much Time On His Hands
  10. The Plan 1. Hack together a PHP code analyser 2.

    Grab the 1,000 most popular WordPress plugins from WordPress.org 3. … 4. Profit! – (Find remotely exploitable unauthenticated critical vulnerabilities) 07/01/2016
  11. The Lexer ▪ "Lexical analysis is the process of converting

    a sequence of characters into a sequence of tokens" [1] 07/01/2016 [1] https://en.wikipedia.org/wiki/Lexical_analysis echo "Result: " . max($a, 100); T_ECHO T_CONSTANT_ENCAPSED_STRING string T_STRING string T_VARIABLE string T_LNUMBER string string
  12. The Parser ▪ “Parsing or syntactic analysis is the process

    of analysing a string of symbols, either in natural language or in computer languages, conforming to the rules of a formal grammar.” [2] 07/01/2016 [2] https://en.wikipedia.org/wiki/Parsing
  13. The Abstract Syntax Tree ▪ “In computer science, an abstract

    syntax tree (AST), or just syntax tree, is a tree representation of the abstract syntactic structure of source code written in a programming language.” [3] 07/01/2016 [3] https://en.wikipedia.org/wiki/Abstract_syntax_tree Simplified abstract syntax tree
  14. The Code Analyser ▪ But as I said, I’m lazy

    – I’m not actually going to implement any of that myself – PHP-Parser by Nikita Popov • https://github.com/nikic/PHP-Parser ▪ Also, did I mention I hate PHP? – So let’s write the analyser in Python! 07/01/2016
  15. The First Problem ▪ Inclusion in PHP is a mess:

    – require, require_once, include, include_once ▪ Not uncommon to see code such as: 07/01/2016 require_once dirname(__ FILE__) . "../" . basename(__ FILE__); ▪ Dynamic analysis is required to evaluate these expressions in order to identify the correct include path ▪ Minimal set of functions to correctly interpret includes: – strtolower, define, defined, dirname, basename, realpath
  16. The Development Iteration ▪ Whilst we are implementing code to

    process includes, we should also – Implement variables – Implement arrays – Implement constants – Implement definitions – Implement operations – Implement function definitions – Implement function calling » Implement function returns – Implement class and method definitions » Implement class inheritance – Implement class instantiation » Implement class constructors and destructors – Implement conditionals » Implement ternary conditionals – Implement variable scoping » Implement global variables » Implement superglobal variables – Implement loops – Implement exception handling – Implement dynamic evaluation 07/01/2016
  17. The Dirty Variable - Taint Analysis 07/01/2016 $sql_template = "SELECT

    * FROM Users WHERE username = '%s'"; $username = $_GET['username']; $sql_query = sprintf($sql_template, $username); $sql_result = mysql_query($sql_query); Uncontrolled Tainted Tainted Vulnerable
  18. The Dirty Variable - Taint Analysis 07/01/2016 $sql_template = "SELECT

    * FROM Users WHERE username = '%s'"; $username = $_GET['username']; $sql_query = sprintf($sql_template, mysqli_escape_string($username)); $sql_result = mysql_query($sql_query); Uncontrolled Tainted Un-Tainted OK
  19. The Dirty Variable - Taint Analysis 07/01/2016 function validate_adid($id) {

    return preg_match("/^[A-Za-z0-9]+/", $id) ? $id : ""; } $id = validatead_id($_GET['id']); if (strlen($id)) { if (in_array($id, $OPTIONS['disabled_adverts'])) $id = $OPTIONS['default_advert']; $cond = sprintf("WHERE ID='%s'", strtolower($id)); } else { $cond = "WHERE ID='" . $OPTIONS['default_advert'] . "'"; } $ad = mysql_query("SELECT * FROM Adverts $cond LIMIT 1;");
  20. The Framework ▪ WordPress wraps a number of the ‘bad’

    PHP functions 07/01/2016 ▪ WordPress uses callbacks heavily as entry points into code – add_action – add_filter mysql_query curl_exec move_uploaded_file $wpdb->query wp_remote_post wp_handle_upload
  21. The Results... ▪ ... were slightly underwhelming TBH. ▪ Found

    unauth exploitable* vulnerabilities in the following plugins: – BestWebSoft Captcha – Gwolle Guestbook – Landing Pages – Multi Plugin Installer – Simple Ads Manager – WooCommerce PDF Invoices & Packing Slips – … and some others I can’t quite remember • ▪ Total active count for blogs running vulnerable plugins at time of writing was 470,000. 07/01/2016 *SQLi, Code Exec, Script Exec, File Download, File Upload
  22. The Good 07/01/2016 177 if ( isset( $_SERVER ) )

    { 178 if ( isset( $_SERVER["HTTP_X_FORWARDED_FOR"] ) ) { 179 $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; 180 } elseif ( isset( $_SERVER["HTTP_CLIENT_IP"] ) ) { 181 $ip = $_SERVER["HTTP_CLIENT_IP"]; 182 } elseif ( isset( $_SERVER["REMOTE_ADDR"] ) ) { 183 $ip = $_SERVER["REMOTE_ADDR"]; 184 } 185 } 186 if ( ! empty( $ip ) ) { 187 $ip_int = sprintf( '%u', ip2long( $ip ) ); 188 $result = $wpdb->get_var( "SELECT `id` FROM " . $wpdb->prefix . "cptch_whitelist WHERE ( `ip_from_int` <= " . $ip_int . " AND `ip_to_int` >= " . $ip_int . " ) OR `ip` LIKE '" . $ip . "' LIMIT 1;" ); [BestWebSoft Captcha 4.1.4] /captcha.php Fixed in version 4.1.5
  23. The Bad 07/01/2016 9 $result = shell_exec('phantomjs --web-security=false server.js '

    . $_GET['url'] ); [Landing Pages 1.9.0] /tests/phantomjs/server.php Fixed in version 1.9.1 9 $result = shell_exec('phantomjs --web-security=false server.js ' . $_GET['url'] ); [Landing Pages 2.0.2] /assets/tests/phantomjs/server.php Fixed in version 2.0.3 The Really Bad
  24. 468 function load_html_file($file) { ... 474 if ( !$this->_protocol &&

    !$this->_base_host && !$this->_base_path ) { 475 list($this->_protocol, $this->_base_host, $this->_base_path) = explode_url($file); 476 } ... 482 if ($this->_protocol == "" || $this->_protocol === "file://") { ... //Sanitize local file paths... 499 $file = $realfile; 500 } 501 502 $contents = file_get_contents($file, null, $this->_http_context); The Ugly 07/01/2016 [Dompdf 0.6.1] /include/dompdf.cls.php Fixed in 0.6.2 file://
  25. The Bugs ▪ This code is *Buggy*! – Hacked together

    a while ago, I can barely remember how it works – Doesn’t work well with non-ascii characters – Seems to find much fewer issues than before • I think I broke something in a recent change – Is available if anyone wants it! 07/01/2016
  26. The Closing Thoughts 1. Would it not have been easier

    to write in PHP? – Definitely not 2. Should WordPress be doing more to secure plugins? – Almost certainly – Basic automated scanning of plugins • Automated vulnerability discovery • Disallowed functions / APIs – Sandboxing? • I’m not sure this is technically feasible with PHP 3. What about ‘wp_magic_quotes’? – Don’t get me started 07/01/2016