CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/net/winrm/rex_http_transport.rb
Views: 11780
1
require 'winrm'
2
3
module Net
4
module MsfWinRM
5
# Transport for communicating to a WinRM service, using Rex sockets
6
class RexHttpTransport < WinRM::HTTP::HttpTransport
7
# rubocop:disable Lint/
8
def initialize(opts)
9
if opts[:kerberos_authenticator]
10
self.http_client = opts.fetch(:http_client) { Rex::Proto::Http::Client.new(opts[:host], opts[:port], {}, opts[:ssl], opts[:ssl_version], opts[:proxies], kerberos_authenticator: opts[:kerberos_authenticator]) }
11
self.preferred_auth = 'Kerberos'
12
else
13
self.http_client = opts.fetch(:http_client) { Rex::Proto::Http::Client.new(opts[:host], opts[:port], {}, opts[:ssl], opts[:ssl_version], opts[:proxies], opts[:user], opts[:password]) }
14
self.preferred_auth = 'Negotiate'
15
end
16
self.timeout = opts[:timeout]
17
@mutex = Mutex.new
18
self.uri = opts[:uri]
19
self.vhost = opts[:vhost]
20
if opts[:realm]
21
http_client.set_config('domain' => opts[:realm])
22
end
23
end
24
25
def peerinfo
26
if http_client && http_client.conn
27
http_client.conn.peerinfo
28
end
29
end
30
31
def localinfo
32
if http_client && http_client.conn
33
http_client.conn.localinfo
34
end
35
end
36
37
def krb_transform_response(encryptor, response)
38
# OMI server doesn't always respond to encrypted messages with encrypted responses over SSL
39
return unless response
40
return if response.headers['Content-Type'] && response.headers['Content-Type'].first =~ (%r{\Aapplication/soap\+xml}i)
41
return if response.body.empty?
42
43
str = response.body.force_encoding('BINARY')
44
str.sub!(%r{^.*Content-Type: application/octet-stream\r\n(.*)--Encrypted.*$}m, '\1')
45
str.sub!(%r{^.*Content-Type: application/octet-stream\r\n(.*)-- Encrypted.*$}m, '\1')
46
47
# Strip off the "encrypted message header length" token
48
str = str[4, str.length-4]
49
begin
50
plaintext = encryptor.decrypt_and_verify(str)
51
rescue Rex::Proto::Kerberos::Model::Error::KerberosError => exception
52
raise WinRM::WinRMHTTPTransportError, "Could not decrypt Kerberos message (#{exception})"
53
end
54
response.body = plaintext
55
end
56
57
def krb_transform_request(encryptor, req)
58
return req if !req.opts['data']
59
opts = req.opts.dup
60
61
body_type = 'application/HTTP-Kerberos-session-encrypted'
62
opts['ctype'] = 'multipart/encrypted;protocol="' + body_type + '";boundary="Encrypted Boundary"'
63
data = opts['data']
64
emessage, header_length, pad_length = encryptor.encrypt_and_increment(data)
65
emessage = [header_length].pack('V') + emessage
66
67
opts['data'] = body(emessage, data.length + pad_length, body_type)
68
Rex::Proto::Http::ClientRequest.new(opts)
69
end
70
71
# Performs decryption of the stream coming from the HTTP client
72
def ntlm_transform_response(ntlm_client, response)
73
# OMI server doesn't always respond to encrypted messages with encrypted responses over SSL
74
return unless response
75
return if response.headers['Content-Type'] && response.headers['Content-Type'].first =~ (%r{\Aapplication/soap\+xml}i)
76
return if response.body.empty?
77
78
str = response.body.force_encoding('BINARY')
79
str.sub!(%r{^.*Content-Type: application/octet-stream\r\n(.*)--Encrypted.*$}m, '\1')
80
81
signature = str[4..19]
82
message = ntlm_client.session.unseal_message(str[20..-1])
83
if ntlm_client.session.verify_signature(signature, message)
84
response.body = message
85
return
86
else
87
raise WinRM::WinRMHTTPTransportError, 'Could not decrypt NTLM message.'
88
end
89
end
90
91
# Performs encryption of the stream being sent to the HTTP client
92
def ntlm_transform_request(ntlm_client, req)
93
return req if !req.opts['data']
94
opts = req.opts.dup
95
96
opts['ctype'] = 'multipart/encrypted;protocol="application/HTTP-SPNEGO-session-encrypted";boundary="Encrypted Boundary"'
97
data = opts['data']
98
emessage = ntlm_client.session.seal_message(data)
99
signature = ntlm_client.session.sign_message(data)
100
edata = "\x10\x00\x00\x00#{signature}#{emessage}"
101
102
opts['data'] = body(edata, data.bytesize)
103
Rex::Proto::Http::ClientRequest.new(opts)
104
end
105
106
def _send_request(message)
107
@mutex.synchronize do
108
opts = {
109
'uri' => uri,
110
'method' => 'POST',
111
'agent' => 'Microsoft WinRM Client',
112
'ctype' => 'application/soap+xml;charset=UTF-8',
113
'no_body_for_auth' => true,
114
'preferred_auth' => self.preferred_auth,
115
}
116
117
opts.merge!('vhost' => self.vhost) if self.vhost
118
119
if message
120
opts['data'] = message
121
opts['krb_transform_request'] = method(:krb_transform_request)
122
opts['krb_transform_response'] = method(:krb_transform_response)
123
opts['ntlm_transform_request'] = method(:ntlm_transform_request)
124
opts['ntlm_transform_response'] = method(:ntlm_transform_response)
125
end
126
request = http_client.request_cgi(opts)
127
response = http_client.send_recv(request, timeout, true)
128
if response
129
WinRM::ResponseHandler.new(response.body, response.code).parse_to_xml
130
else
131
raise WinRM::WinRMHTTPTransportError, 'No response'
132
end
133
end
134
end
135
136
def send_request(message)
137
_send_request(message)
138
end
139
140
protected
141
142
attr_accessor :http_client, :uri, :timeout, :preferred_auth, :vhost
143
end
144
end
145
end
146
147