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

Commit a06e17c

Browse files
committed
Merged development into master
2 parents 58fc9b4 + b3687d1 commit a06e17c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1246
-85
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# wordpress-exploit-framework
2-
[![Build Status](https://travis-ci.org/rastating/wordpress-exploit-framework.svg?branch=master)](https://travis-ci.org/rastating/wordpress-exploit-framework) [![Code Climate](https://codeclimate.com/github/rastating/wordpress-exploit-framework/badges/gpa.svg)](https://codeclimate.com/github/rastating/wordpress-exploit-framework) [![Dependency Status](https://gemnasium.com/rastating/wordpress-exploit-framework.svg)](https://gemnasium.com/rastating/wordpress-exploit-framework)
2+
[![Build Status](https://img.shields.io/travis/rastating/wordpress-exploit-framework/master.svg?colorB=007ec6)](https://travis-ci.org/rastating/wordpress-exploit-framework) [![Code Climate](https://img.shields.io/codeclimate/github/rastating/wordpress-exploit-framework.svg?colorB=007ec6)](https://codeclimate.com/github/rastating/wordpress-exploit-framework) [![Dependency Status](https://img.shields.io/gemnasium/rastating/wordpress-exploit-framework.svg?colorB=007ec6)](https://gemnasium.com/rastating/wordpress-exploit-framework) [![GitHub release](https://img.shields.io/github/release/rastating/wordpress-exploit-framework.svg)](https://github.com/rastating/wordpress-exploit-framework/releases/latest) [![License](https://img.shields.io/badge/license-GPL%20v3-blue.svg)](https://github.com/rastating/wordpress-exploit-framework/blob/master/LICENSE) [![Trello](https://img.shields.io/badge/trello-wordpress--exploit--framework-blue.svg)](https://trello.com/b/XuChenHg)
33

44
A Ruby framework for developing and using modules which aid in the penetration testing of WordPress powered websites and systems.
55

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.4.1
1+
1.5

data/json/commands.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
"cmd": "clear",
1313
"desc": "Clear the screen."
1414
},
15+
{
16+
"cmd": "exit",
17+
"desc": "Exit the WordPress Exploit Framework prompt."
18+
},
1519
{
1620
"cmd": "gset [option] [value]",
1721
"desc": "Set the [value] of [option] globally, so it is used by the current and future modules."

lib/cli/auto_complete.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,21 @@ def refresh_autocomplete_options
2828

2929
if mod.exploit_module?
3030
opts_hash['payload'] = {}
31-
Wpxf::Payloads.payload_list.each { |p| opts_hash['payload'][p] = {} }
31+
Wpxf::Payloads.payload_list.each { |p| opts_hash['payload'][p[:name]] = {} }
3232
end
3333
end
3434

3535
@autocomplete_list['set'] = opts_hash
3636
@autocomplete_list['unset'] = opts_hash
37+
@autocomplete_list['gset'] = opts_hash
38+
@autocomplete_list['gunset'] = opts_hash
3739
end
3840

3941
def build_cmd_list
4042
cmds = {}
4143
permitted_commands.each { |c| cmds[c] = {} }
42-
Wpxf::Auxiliary.module_list.each { |m| cmds['use'][m] = {} }
43-
Wpxf::Exploit.module_list.each { |m| cmds['use'][m] = {} }
44+
Wpxf::Auxiliary.module_list.each { |m| cmds['use'][m[:name]] = {} }
45+
Wpxf::Exploit.module_list.each { |m| cmds['use'][m[:name]] = {} }
4446
cmds['show'] = {
4547
'options' => {},
4648
'advanced' => {},

lib/cli/console.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ def prompt_for_input
6464

6565
prompt += " [#{context.module_path}]" if context
6666
prompt += ' > '
67-
Readline.readline(prompt, true)
67+
68+
input = Readline.readline(prompt, true).to_s
69+
puts if input.empty?
70+
input
6871
end
6972

7073
def can_handle?(command)
@@ -103,6 +106,7 @@ def on_event_emitted(event)
103106
end
104107

105108
def execute_user_command(command, args)
109+
command = 'quit' if command == 'exit'
106110
if can_handle? command
107111
puts unless commands_without_output.include? command
108112
send(command, *args) if correct_number_of_args?(command, args)

lib/cli/context.rb

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
module Cli
22
# A context which modules will be used in.
33
class Context
4-
def initialize
5-
end
6-
74
def class_name(path_name)
85
return path_name if path_name !~ /_/ && path_name =~ /[A-Z]+.*/
96
path_name.split('_').map(&:capitalize).join
@@ -14,22 +11,7 @@ def verbose?
1411
end
1512

1613
def load_module(path)
17-
match = path.match(/(auxiliary|exploit)\/(.+)/i)
18-
raise 'Invalid module path' unless match
19-
20-
type = match.captures[0]
21-
name = class_name(match.captures[1])
22-
23-
begin
24-
if type.eql? 'auxiliary'
25-
@module = Wpxf::Auxiliary.const_get(name).new
26-
elsif type.eql? 'exploit'
27-
@module = Wpxf::Exploit.const_get(name).new
28-
end
29-
rescue NameError
30-
raise 'Invalid module name'
31-
end
32-
14+
@module = Wpxf.load_module(path)
3315
@module_path = path
3416
@module
3517
end
@@ -45,19 +27,7 @@ def reload
4527
end
4628

4729
def load_payload(name)
48-
clsid = class_name(name)
49-
50-
if Wpxf::Payloads.const_defined?(clsid)
51-
payload_class = Wpxf::Payloads.const_get(clsid)
52-
if payload_class.is_a?(Class)
53-
self.module.payload = payload_class.new
54-
else
55-
fail "\"#{name}\" is not a valid payload"
56-
end
57-
else
58-
fail "\"#{name}\" is not a valid payload"
59-
end
60-
30+
self.module.payload = Wpxf::Payloads.load_payload(name)
6131
self.module.payload.check(self.module)
6232
self.module.payload
6333
end

lib/cli/module_info.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ def print_author
1313
def print_description
1414
print_std('Description:')
1515
indent_cursor do
16-
print_std(wrap_text(context.module.module_desc))
16+
if context.module.module_description_preformatted
17+
print_std(indent_without_wrap(context.module.module_desc))
18+
else
19+
print_std(wrap_text(context.module.module_desc))
20+
end
1721
end
1822
end
1923

lib/cli/modules.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ def use(module_path)
2727
mod = context.load_module(module_path)
2828
mod.event_emitter.subscribe(self)
2929
print_good "Loaded module: #{mod}"
30+
mod.emit_usage_info
3031
@context_stack.push(context)
3132
rescue StandardError => e
3233
print_bad "Failed to load module: #{e}"
@@ -40,15 +41,20 @@ def use(module_path)
4041
refresh_autocomplete_options
4142
end
4243

44+
def module_name_from_class(klass)
45+
klass.new.module_name
46+
end
47+
4348
def search_modules(args)
4449
pattern = /#{args.map { |m| Regexp.escape(m) }.join('|')}/i
4550
module_list = Wpxf::Auxiliary.module_list + Wpxf::Exploit.module_list
4651

4752
results = []
48-
module_list.select { |m| m =~ pattern }.each do |path|
49-
context = Context.new
50-
context.load_module(path)
51-
results.push(path: path, title: context.module.module_name)
53+
module_list.select { |m| m[:name] =~ pattern }.each do |mod|
54+
results.push(
55+
path: mod[:name],
56+
title: module_name_from_class(mod[:class])
57+
)
5258
end
5359

5460
results

lib/cli/output.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ def wrap_text(s, padding = 0, width = 78)
1212
.gsub(/\s+$/, '')
1313
end
1414

15+
def indent_without_wrap(s)
16+
s.gsub(/\n/, "\n#{@indent * @indent_level}")
17+
end
18+
1519
def print_std(msg)
1620
puts "#{@indent * @indent_level}#{msg}"
1721
end

lib/wpxf/core/module_info.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def update_info(info)
1717

1818
@info.merge!(info)
1919
@info[:date] = Date.parse(@info[:date].to_s)
20-
@info[:desc] = @info[:desc].split.join(' ')
20+
@info[:desc] = @info[:desc].gsub(/ +/, ' ')
2121
@info
2222
end
2323

@@ -45,5 +45,15 @@ def module_author
4545
def module_date
4646
@info[:date]
4747
end
48+
49+
# @return [Boolean] true if the description is preformatted.
50+
def module_description_preformatted
51+
@info[:desc_preformatted]
52+
end
53+
54+
# Emits any information that the user should be aware of before using the module.
55+
def emit_usage_info
56+
nil
57+
end
4858
end
4959
end

lib/wpxf/core/payload.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ def initialize
1616
default: true
1717
)
1818
])
19+
20+
self.queued_commands = []
1921
end
2022

2123
# @return an encoded version of the payload.
@@ -74,12 +76,14 @@ def post_exploit(mod)
7476

7577
# Cleanup any allocated resource to the payload.
7678
def cleanup
79+
nil
7780
end
7881

7982
# Run checks to raise warnings to the user of any issues or noteworthy
8083
# points in regards to the payload being used with the current module.
8184
# @param mod [Module] the module using the payload.
8285
def check(mod)
86+
nil
8387
end
8488

8589
# @return [Hash] a hash of constants that should be injected at the
@@ -104,9 +108,19 @@ def php_preamble
104108
preamble
105109
end
106110

111+
# Enqueue a command to be executed on the target system, if the
112+
# payload supports queued commands.
113+
# @param cmd [String] the command to execute when the payload is executed.
114+
def enqueue_command(cmd)
115+
queued_commands.push(cmd)
116+
end
117+
107118
# @return the payload in its raw format.
108119
attr_accessor :raw
109120

121+
# @return [Array] the commands queued to be executed on the target.
122+
attr_accessor :queued_commands
123+
110124
private
111125

112126
def raw_payload_with_random_var_names

lib/wpxf/utility/reference_inflater.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def format_strings
2222
{
2323
'WPVDB' => 'https://wpvulndb.com/vulnerabilities/%s',
2424
'OSVDB' => 'http://www.osvdb.org/%s',
25-
'CVE' => 'http://www.cvedetails.com/cve/%s',
25+
'CVE' => 'http://www.cvedetails.com/cve/CVE-%s',
2626
'EDB' => 'https://www.exploit-db.com/exploits/%s',
2727
'URL' => '%s'
2828
}

lib/wpxf/wordpress/file_download.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,19 @@ def validate_content(content)
7676
true
7777
end
7878

79+
# A task to run before the download starts.
80+
# @return [Boolean] true if pre-download operations were successful.
81+
def before_download
82+
true
83+
end
84+
7985
# Run the module.
8086
# @return [Boolean] true if successful.
8187
def run
8288
validate_implementation
8389

8490
return false unless super
91+
return false unless before_download
8592

8693
res = request_file
8794
return false unless validate_result(res) && validate_content(res.body)

lib/wpxf/wordpress/fingerprint.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def check_version_from_custom_file(url, regex, fixed = nil, introduced = nil)
7979

8080
private
8181

82-
WORDPRESS_VERSION_PATTERN = '([^\r\n"\']+\.[^\r\n"\']+)'
82+
WORDPRESS_VERSION_PATTERN = '(\d+\.\d+(?:\.\d+)*)'
8383

8484
WORDPRESS_GENERATOR_VERSION_PATTERN = %r{<meta\sname="generator"\s
8585
content="WordPress\s#{WORDPRESS_VERSION_PATTERN}"\s\/>}xi

lib/wpxf/wordpress/shell_upload.rb

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,17 @@ def payload_name
3636

3737
# @return [String] the URL of the file used to upload the payload.
3838
def uploader_url
39+
nil
3940
end
4041

4142
# @return [BodyBuilder] the {Wpxf::Utility::BodyBuilder} used to generate the uploader form.
4243
def payload_body_builder
44+
nil
4345
end
4446

4547
# @return [String] the URL of the payload after it is uploaded to the target.
4648
def uploaded_payload_location
49+
nil
4750
end
4851

4952
# Called prior to preparing and uploading the payload.
@@ -52,21 +55,37 @@ def before_upload
5255
true
5356
end
5457

58+
# @return [Integer] the response code to expect from a successful upload operation.
59+
def expected_upload_response_code
60+
200
61+
end
62+
63+
# @return [Hash] the query string parameters to use when submitting the upload request.
64+
def upload_request_params
65+
nil
66+
end
67+
68+
# @return [String] the extension type to use when generating the payload name.
69+
def payload_name_extension
70+
'php'
71+
end
72+
5573
# Run the module.
5674
# @return [Boolean] true if successful.
5775
def run
5876
return false unless super
5977
return false unless before_upload
6078

6179
emit_info 'Preparing payload...'
62-
@payload_name = "#{Utility::Text.rand_alpha(payload_name_length)}.php"
80+
@payload_name = "#{Utility::Text.rand_alpha(payload_name_length)}.#{payload_name_extension}"
6381
builder = payload_body_builder
6482
return false unless builder
6583

6684
emit_info 'Uploading payload...'
6785
return false unless upload_payload(builder)
6886

6987
payload_url = uploaded_payload_location
88+
return false unless payload_url
7089
emit_success "Uploaded the payload to #{payload_url}", true
7190

7291
emit_info 'Executing the payload...'
@@ -75,6 +94,11 @@ def run
7594
true
7695
end
7796

97+
# @return [Boolean] true if the result of the upload operation is valid.
98+
def validate_upload_result
99+
true
100+
end
101+
78102
# Execute the payload at the specified address.
79103
# @param payload_url [String] the payload URL to access.
80104
def execute_payload(payload_url)
@@ -90,21 +114,21 @@ def payload_name_length
90114

91115
def upload_payload(builder)
92116
builder.create do |body|
93-
@upload_result = execute_post_request(url: uploader_url, body: body, cookie: @session_cookie)
117+
@upload_result = execute_post_request(url: uploader_url, params: upload_request_params, body: body, cookie: @session_cookie)
94118
end
95119

96120
if @upload_result.nil? || @upload_result.timed_out?
97121
emit_error 'No response from the target'
98122
return false
99123
end
100124

101-
if @upload_result.code != 200
125+
if @upload_result.code != expected_upload_response_code
102126
emit_info "Response code: #{@upload_result.code}", true
103127
emit_info "Response body: #{@upload_result.body}", true
104128
emit_error 'Failed to upload payload'
105129
return false
106130
end
107131

108-
true
132+
validate_upload_result
109133
end
110134
end

lib/wpxf/wordpress/urls.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,9 @@ def wordpress_url_uploads
111111
def wordpress_url_admin_profile
112112
normalize_uri(wordpress_url_admin, 'profile.php')
113113
end
114+
115+
# @return [String] the base path of the REST API introduced in WordPress 4.7.0.
116+
def wordpress_url_rest_api
117+
normalize_uri(full_uri, 'wp-json')
118+
end
114119
end

0 commit comments

Comments
 (0)