11require 'metasploit/framework/login_scanner/http'
2- require 'nokogiri'
32
43module Metasploit
54 module Framework
@@ -8,143 +7,74 @@ module LoginScanner
87 # Wordpress XML RPC login scanner
98 class WordpressRPC < HTTP
109
11- # @!attribute passwords
12- # @return [Array]
13- attr_accessor :passwords
14-
15- # @!attribute chunk_size
16- # @return [Fixnum]
17- attr_accessor :chunk_size
18-
19- # @!attribute block_wait
20- # @return [Fixnum]
21- attr_accessor :block_wait
22-
23- # @!attribute base_uri
24- # @return [String]
25- attr_accessor :base_uri
26-
27- # @!attribute wordpress_url_xmlrpc
28- # @return [String]
29- attr_accessor :wordpress_url_xmlrpc
30-
31- def set_default
32- self . wordpress_url_xmlrpc = 'xmlrpc.php'
33- self . block_wait = 6
34- self . base_uri = '/'
35- self . chunk_size = 1800
36- end
10+ # (see Base#attempt_login)
11+ def attempt_login ( credential )
12+ http_client = Rex ::Proto ::Http ::Client . new (
13+ host , port , { 'Msf' => framework , 'MsfExploit' => framework_module } , ssl , ssl_version , proxies
14+ )
15+ configure_http_client ( http_client )
3716
38- # Returns the XML data that is used for the login.
39- #
40- # @param user [String] username
41- # @return [Array]
42- def generate_xml ( user )
43- xml_payloads = [ ]
17+ result_opts = {
18+ credential : credential ,
19+ host : host ,
20+ port : port ,
21+ protocol : 'tcp'
22+ }
23+ if ssl
24+ result_opts [ :service_name ] = 'https'
25+ else
26+ result_opts [ :service_name ] = 'http'
27+ end
4428
45- # Evil XML | Limit number of log-ins to CHUNKSIZE/request due
46- # Wordpress limitation which is 1700 maximum.
47- passwords . each_slice ( chunk_size ) do |pass_group |
48- document = Nokogiri ::XML ::Builder . new do |xml |
49- xml . methodCall {
50- xml . methodName ( "system.multicall" )
51- xml . params {
52- xml . param {
53- xml . value {
54- xml . array {
55- xml . data {
56- pass_group . each do |pass |
57- xml . value {
58- xml . struct {
59- xml . member {
60- xml . name ( "methodName" )
61- xml . value { xml . string ( "wp.getUsersBlogs" ) } }
62- xml . member {
63- xml . name ( "params" )
64- xml . value {
65- xml . array {
66- xml . data {
67- xml . value {
68- xml . array {
69- xml . data {
70- xml . value { xml . string ( user ) }
71- xml . value { xml . string ( pass ) }
72- } } } } } } } } }
73- end
74- } } } } } }
29+ begin
30+ http_client . connect
31+
32+ request = http_client . request_cgi (
33+ 'uri' => uri ,
34+ 'method' => method ,
35+ 'data' => generate_xml_request ( credential . public , credential . private ) ,
36+ )
37+ response = http_client . send_recv ( request )
38+
39+ if response && response . code == 200 && response . body =~ /<value><int>401<\/ int><\/ value>/ || response . body =~ /<name>user_id<\/ name>/
40+ result_opts . merge! ( status : Metasploit ::Model ::Login ::Status ::SUCCESSFUL , proof : response )
41+ elsif response . body =~ /<value><int>-32601<\/ int><\/ value>/
42+ result_opts . merge! ( status : Metasploit ::Model ::Login ::Status ::UNABLE_TO_CONNECT )
43+ else
44+ result_opts . merge! ( status : Metasploit ::Model ::Login ::Status ::INCORRECT , proof : response )
7545 end
76- xml_payloads << document . to_xml
46+ rescue ::EOFError , Rex ::ConnectionError , ::Timeout ::Error => e
47+ result_opts . merge! ( status : Metasploit ::Model ::Login ::Status ::UNABLE_TO_CONNECT , proof : e )
7748 end
7849
79- xml_payloads
80- end
81-
82- # Sends an HTTP request to Wordpress.
83- #
84- # @param xml [String] XML data.
85- # @return [void]
86- def send_wp_request ( xml )
87- opts =
88- {
89- 'method' => 'POST' ,
90- 'uri' => normalize_uri ( "#{ base_uri } /#{ wordpress_url_xmlrpc } " ) ,
91- 'data' => xml ,
92- 'ctype' => 'text/xml'
93- }
50+ Result . new ( result_opts )
9451
95- client = Rex ::Proto ::Http ::Client . new ( rhost )
96- client . connect
97- req = client . request_cgi ( opts )
98- res = client . send_recv ( req )
99-
100- if res && res . code != 200
101- sleep ( block_wait * 60 )
102- end
103-
104- @res = res
10552 end
10653
54+ # This method generates the XML data for the RPC login request
55+ # @param user [String] the username to authenticate with
56+ # @param pass [String] the password to authenticate with
57+ # @return [String] the generated XML body for the request
58+ def generate_xml_request ( user , pass )
59+ xml = "<?xml version=\" 1.0\" encoding=\" iso-8859-1\" ?>"
60+ xml << '<methodCall>'
61+ xml << '<methodName>wp.getUsers</methodName>'
62+ xml << '<params><param><value>1</value></param>'
63+ xml << "<param><value>#{ user } </value></param>"
64+ xml << "<param><value>#{ pass } </value></param>"
65+ xml << '</params>'
66+ xml << '</methodCall>'
67+ xml
68+ end
10769
108- # Attempts to login.
109- #
110- # @param credential [Metasploit::Framework::Credential]
111- # @return [Metasploit::Framework::LoginScanner::Result]
112- def attempt_login ( credential )
113- generate_xml ( credential . public ) . each do |xml |
114- send_wp_request ( xml )
115- req_xml = Nokogiri ::Slop ( xml )
116- res_xml = Nokogiri ::Slop ( @res . to_s . scan ( /<.*>/ ) . join )
117- res_xml . search ( "methodResponse/params/param/value/array/data/value" ) . each_with_index do |value , i |
118- result = value . at ( "struct/member/value/int" )
119- if result . nil?
120- pass = req_xml . search ( "data/value/array/data" ) [ i ] . value [ 1 ] . text . strip
121- credential . private = pass
122- result_opts = {
123- credential : credential ,
124- host : host ,
125- port : port ,
126- protocol : 'tcp'
127- }
128- result_opts . merge! ( status : Metasploit ::Model ::Login ::Status ::SUCCESSFUL )
129- return Result . new ( result_opts )
130- end
131- end
132- end
133-
134- result_opts = {
135- credential : credential ,
136- host : host ,
137- port : port ,
138- protocol : 'tcp'
139- }
140-
141- result_opts . merge! ( status : Metasploit ::Model ::Login ::Status ::INCORRECT )
142- return Result . new ( result_opts )
70+ # (see Base#set_sane_defaults)
71+ def set_sane_defaults
72+ @method = "POST" . freeze
73+ super
14374 end
14475
14576 end
14677 end
14778 end
14879end
14980
150-
0 commit comments