@@ -17,183 +17,188 @@ class Metasploit3 < Msf::Auxiliary
17
17
def initialize ( info = { } )
18
18
super ( update_info ( info ,
19
19
'Name' => 'HTTP SOAP Verb/Noun Brute Force Scanner' ,
20
- 'Description' => %q{
20
+ 'Description' => %q(
21
21
This module attempts to brute force SOAP/XML requests to uncover
22
22
hidden methods.
23
- } ,
24
- 'Author' => [ 'patrick' ] ,
23
+ ) ,
24
+ 'Author' => [ 'patrick' ] ,
25
25
'License' => MSF_LICENSE ) )
26
26
27
27
register_options (
28
28
[
29
- OptString . new ( 'PATH' , [ true , " The path to test" , '/' ] ) ,
30
- OptString . new ( 'XMLNAMESPACE' , [ true , " XML Web Service Namespace" , 'http://tempuri.org/' ] ) ,
31
- OptString . new ( 'XMLINSTANCE' , [ true , " XML Schema Instance" , 'http://www.w3.org/2001/XMLSchema-instance' ] ) ,
32
- OptString . new ( 'XMLSCHEMA' , [ true , " XML Schema" , 'http://www.w3.org/2001/XMLSchema' ] ) ,
33
- OptString . new ( 'XMLSOAP' , [ true , " XML SOAP" , 'http://schemas.xmlsoap.org/soap/envelope/' ] ) ,
34
- OptString . new ( 'CONTENTTYPE' , [ true , " The HTTP Content-Type Header" , 'application/x-www-form-urlencoded' ] ) ,
35
- OptInt . new ( 'SLEEP' , [ true , " Sleep this many seconds between requests" , 0 ] ) ,
36
- OptBool . new ( 'DISPLAYHTML' , [ true , " Display HTML response" , false ] ) ,
37
- OptBool . new ( 'SSL' , [ true , " Use SSL" , false ] ) ,
38
- OptBool . new ( 'VERB_DELETE' , [ false , " Enable 'delete' verb" , ' false' ] )
29
+ OptString . new ( 'PATH' , [ true , ' The path to test' , '/' ] ) ,
30
+ OptString . new ( 'XMLNAMESPACE' , [ true , ' XML Web Service Namespace' , 'http://tempuri.org/' ] ) ,
31
+ OptString . new ( 'XMLINSTANCE' , [ true , ' XML Schema Instance' , 'http://www.w3.org/2001/XMLSchema-instance' ] ) ,
32
+ OptString . new ( 'XMLSCHEMA' , [ true , ' XML Schema' , 'http://www.w3.org/2001/XMLSchema' ] ) ,
33
+ OptString . new ( 'XMLSOAP' , [ true , ' XML SOAP' , 'http://schemas.xmlsoap.org/soap/envelope/' ] ) ,
34
+ OptString . new ( 'CONTENTTYPE' , [ true , ' The HTTP Content-Type Header' , 'application/x-www-form-urlencoded' ] ) ,
35
+ OptInt . new ( 'SLEEP' , [ true , ' Sleep this many milliseconds between requests' , 0 ] ) ,
36
+ OptBool . new ( 'DISPLAYHTML' , [ true , ' Display HTML response' , false ] ) ,
37
+ OptBool . new ( 'SSL' , [ true , ' Use SSL' , false ] ) ,
38
+ OptBool . new ( 'VERB_DELETE' , [ false , ' Enable DELETE verb' , false ] )
39
39
] , self . class )
40
40
end
41
41
42
42
# Fingerprint a single host
43
43
def run_host ( ip )
44
+ verbs = %w(
45
+ get
46
+ active
47
+ activate
48
+ create
49
+ change
50
+ set
51
+ put
52
+ do
53
+ go
54
+ resolve
55
+ start
56
+ recover
57
+ initiate
58
+ negotiate
59
+ define
60
+ stop
61
+ begin
62
+ end
63
+ manage
64
+ administer
65
+ modify
66
+ register
67
+ log
68
+ add
69
+ list
70
+ query
71
+ )
44
72
45
- verbs = [
46
- 'get' ,
47
- 'active' ,
48
- 'activate' ,
49
- 'create' ,
50
- 'change' ,
51
- 'set' ,
52
- 'put' ,
53
- 'do' ,
54
- 'go' ,
55
- 'resolve' ,
56
- 'start' ,
57
- 'recover' ,
58
- 'initiate' ,
59
- 'negotiate' ,
60
- 'define' ,
61
- 'stop' ,
62
- 'begin' ,
63
- 'end' ,
64
- 'manage' ,
65
- 'administer' ,
66
- 'modify' ,
67
- 'register' ,
68
- 'log' ,
69
- 'add' ,
70
- 'list' ,
71
- 'query' ,
72
- ]
73
-
74
- if ( datastore [ 'VERB_DELETE' ] )
75
- verbs << 'delete'
76
- end
73
+ verbs << 'delete' if datastore [ 'VERB_DELETE' ]
74
+
75
+ nouns = %w(
76
+ password
77
+ task
78
+ tasks
79
+ pass
80
+ administration
81
+ account
82
+ accounts
83
+ admin
84
+ login
85
+ logins
86
+ token
87
+ tokens
88
+ credential
89
+ credentials
90
+ key
91
+ keys
92
+ guid
93
+ message
94
+ messages
95
+ user
96
+ users
97
+ username
98
+ usernames
99
+ load
100
+ list
101
+ name
102
+ names
103
+ file
104
+ files
105
+ path
106
+ paths
107
+ directory
108
+ directories
109
+ configuration
110
+ configurations
111
+ config
112
+ configs
113
+ setting
114
+ settings
115
+ registry
116
+ on
117
+ off
118
+ )
77
119
78
- nouns = [
79
- 'password' ,
80
- 'task' ,
81
- 'tasks' ,
82
- 'pass' ,
83
- 'administration' ,
84
- 'account' ,
85
- 'accounts' ,
86
- 'admin' ,
87
- 'login' ,
88
- 'logins' ,
89
- 'token' ,
90
- 'tokens' ,
91
- 'credential' ,
92
- 'credentials' ,
93
- 'key' ,
94
- 'keys' ,
95
- 'guid' ,
96
- 'message' ,
97
- 'messages' ,
98
- 'user' ,
99
- 'users' ,
100
- 'username' ,
101
- 'usernames' ,
102
- 'load' ,
103
- 'list' ,
104
- 'name' ,
105
- 'names' ,
106
- 'file' ,
107
- 'files' ,
108
- 'path' ,
109
- 'paths' ,
110
- 'directory' ,
111
- 'directories' ,
112
- 'configuration' ,
113
- 'configurations' ,
114
- 'config' ,
115
- 'configs' ,
116
- 'setting' ,
117
- 'settings' ,
118
- 'registry' ,
119
- 'on' ,
120
- 'off' ,
121
- ]
122
-
123
- target_port = datastore [ 'RPORT' ]
124
120
vhost = datastore [ 'VHOST' ] || wmap_target_host || ip
125
121
126
122
# regular expressions for common rejection messages
127
123
reject_regexen = [ ]
128
- reject_regexen << Regexp . new ( " method \\ S+ is not valid" , true )
129
- reject_regexen << Regexp . new ( " Method \\ S+ not implemented" , true )
130
- reject_regexen << Regexp . new ( " unable to resolve WSDL method name" , true )
124
+ reject_regexen << Regexp . new ( ' method \\S+ is not valid' , true )
125
+ reject_regexen << Regexp . new ( ' Method \\S+ not implemented' , true )
126
+ reject_regexen << Regexp . new ( ' unable to resolve WSDL method name' , true )
131
127
132
- begin
133
- verbs . each do |v |
134
- nouns . each do | n |
128
+ verbs . each do | v |
129
+ nouns . each do |n |
130
+ begin
135
131
data_parts = [ ]
136
- data_parts << " <?xml version=\" 1.0\" encoding=\" utf-8\" ?>"
132
+ data_parts << ' <?xml version=\' 1.0\' encoding=\' utf-8\'?>'
137
133
data_parts << "<soap:Envelope xmlns:xsi=\" #{ datastore [ 'XMLINSTANCE' ] } \" xmlns:xsd=\" #{ datastore [ 'XMLSCHEMA' ] } \" xmlns:soap=\" #{ datastore [ 'XMLSOAP' ] } \" >"
138
- data_parts << " <soap:Body>"
134
+ data_parts << ' <soap:Body>'
139
135
data_parts << "<#{ v } #{ n } xmlns=\" #{ datastore [ 'XMLNAMESPACE' ] } \" >"
140
136
data_parts << "</#{ v } #{ n } >"
141
- data_parts << " </soap:Body>"
142
- data_parts << " </soap:Envelope>"
137
+ data_parts << ' </soap:Body>'
138
+ data_parts << ' </soap:Envelope>'
143
139
data_parts << nil
144
140
data_parts << nil
145
141
data = data_parts . join ( "\r \n " )
146
142
147
143
uri = normalize_uri ( datastore [ 'PATH' ] )
148
- vprint_status ( "Sending request #{ uri } /#{ v } #{ n } to #{ wmap_target_host } :#{ datastore [ 'RPORT' ] } " )
149
-
150
- res = send_request_raw ( {
151
- 'uri' => uri + '/' + v + n ,
152
- 'method' => 'POST' ,
153
- 'vhost' => vhost ,
154
- 'data' => data ,
155
- 'headers' =>
156
- {
157
- 'Content-Length' => data . length ,
158
- 'SOAPAction' => '"' + datastore [ 'XMLNAMESPACE' ] + v + n + '"' ,
159
- 'Expect' => '100-continue' ,
160
- 'Content-Type' => datastore [ 'CONTENTTYPE' ] ,
161
- }
162
- } , 15 )
163
-
164
- if ( res && !( res . body . empty? ) )
165
- if ( ( not reject_regexen . select { |r | res . body =~ r } . empty? ) )
144
+ uri += '/' unless uri =~ /^\/ $/
145
+ uri += v + n
146
+
147
+ vprint_status ( "Sending request #{ uri } #{ wmap_target_host } :#{ datastore [ 'RPORT' ] } " )
148
+
149
+ res = send_request_raw (
150
+ {
151
+ 'uri' => uri ,
152
+ 'method' => 'POST' ,
153
+ 'vhost' => vhost ,
154
+ 'data' => data ,
155
+ 'headers' =>
156
+ {
157
+ 'Content-Length' => data . length ,
158
+ 'SOAPAction' => '"' + datastore [ 'XMLNAMESPACE' ] + v + n + '"' ,
159
+ 'Expect' => '100-continue' ,
160
+ 'Content-Type' => datastore [ 'CONTENTTYPE' ]
161
+ }
162
+ } , 15 )
163
+
164
+ if res && !( res . body . empty? )
165
+ if reject_regexen . any? { |r | res . body =~ r }
166
166
print_status ( "Server #{ wmap_target_host } :#{ datastore [ 'RPORT' ] } rejected SOAPAction: #{ v } #{ n } with HTTP: #{ res . code } #{ res . message } ." )
167
- elsif ( res . message =~ /Cannot process the message because the content type/ )
167
+ elsif res . message =~ /Cannot process the message because the content type/
168
168
print_status ( "Server #{ wmap_target_host } :#{ datastore [ 'RPORT' ] } rejected CONTENTTYPE: HTTP: #{ res . code } #{ res . message } ." )
169
169
res . message =~ /was not the expected type\s \' ([^']+)'/
170
170
print_status ( "Set CONTENTTYPE to \" #{ $1} \" " )
171
171
return false
172
- elsif ( res . code == 404 )
172
+ elsif res . code == 404
173
173
print_status ( "Server #{ wmap_target_host } :#{ datastore [ 'RPORT' ] } returned HTTP 404 for #{ datastore [ 'PATH' ] } . Use a different one." )
174
174
return false
175
175
else
176
176
print_status ( "Server #{ wmap_target_host } :#{ datastore [ 'RPORT' ] } responded to SOAPAction: #{ v } #{ n } with HTTP: #{ res . code } #{ res . message } ." )
177
177
## Add Report
178
178
report_note (
179
- : host => ip ,
180
- : proto => 'tcp' ,
181
- : sname => ( ssl ? 'https' : 'http' ) ,
182
- : port => rport ,
183
- : type => "SOAPAction: #{ v } #{ n } " ,
184
- : data => "SOAPAction: #{ v } #{ n } with HTTP: #{ res . code } #{ res . message } ."
179
+ host : ip ,
180
+ proto : 'tcp' ,
181
+ sname : ( ssl ? 'https' : 'http' ) ,
182
+ port : rport ,
183
+ type : "SOAPAction: #{ v } #{ n } " ,
184
+ data : "SOAPAction: #{ v } #{ n } with HTTP: #{ res . code } #{ res . message } ."
185
185
)
186
186
if datastore [ 'DISPLAYHTML' ]
187
- print_status ( " The HTML content follows:" )
187
+ print_status ( ' The HTML content follows:' )
188
188
print_status ( res . body + "\r \n " )
189
189
end
190
190
end
191
191
end
192
- select ( nil , nil , nil , datastore [ 'SLEEP' ] ) if ( datastore [ 'SLEEP' ] > 0 )
192
+ rescue ::Rex ::ConnectionRefused , ::Rex ::HostUnreachable , ::Rex ::ConnectionTimeout , ::Timeout ::Error , ::Errno ::EPIPE => e
193
+ print_error ( e . message )
194
+ ensure
195
+ Rex . sleep ( sleep_time )
193
196
end
194
197
end
195
- rescue ::Rex ::ConnectionRefused , ::Rex ::HostUnreachable , ::Rex ::ConnectionTimeout , ::Timeout ::Error , ::Errno ::EPIPE => e
196
- vprint_error ( e . message )
197
198
end
198
199
end
200
+
201
+ def sleep_time
202
+ datastore [ 'SLEEP' ] / 1000.0
203
+ end
199
204
end
0 commit comments