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/metasploit/framework/aws/client.rb
Views: 11784
1
require 'openssl'
2
3
module Metasploit
4
module Framework
5
module Aws
6
module Client
7
USER_AGENT = "aws-sdk-ruby2/2.6.27 ruby/2.3.2 x86_64-darwin15"
8
include Msf::Exploit::Remote::HttpClient
9
10
# because Post modules require these to be defined when including HttpClient
11
def register_autofilter_ports(ports=[]); end
12
def register_autofilter_hosts(ports=[]); end
13
def register_autofilter_services(services=[]); end
14
15
def hexdigest(value)
16
if value.nil? || !value.instance_of?(String)
17
print_error "Unexpected value format"
18
return nil
19
end
20
digest = OpenSSL::Digest.new('SHA256')
21
if value.respond_to?(:read)
22
chunk = nil
23
chunk_size = 1024 * 1024 # 1 megabyte
24
digest.update(chunk) while chunk = value.read(chunk_size)
25
value.rewind
26
else
27
digest.update(value)
28
end
29
digest.hexdigest
30
end
31
32
def hmac(key, value)
33
if key.nil? || !key.instance_of?(String) || value.nil? || !value.instance_of?(String)
34
print_error "Unexpected key/value format"
35
return nil
36
end
37
OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, value)
38
end
39
40
def hexhmac(key, value)
41
if key.nil? || !key.instance_of?(String) || value.nil? || !value.instance_of?(String)
42
print_error "Unexpected key/value format"
43
return nil
44
end
45
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, value)
46
end
47
48
def request_to_sign(headers, body_digest)
49
if headers.nil? || !headers.instance_of?(Hash) || body_digest.nil? || !body_digest.instance_of?(String)
50
return nil, nil
51
end
52
headers_block = headers.sort_by(&:first).map do |k, v|
53
v = "#{v},#{v}" if k == 'Host'
54
"#{k.downcase}:#{v}"
55
end.join("\n")
56
headers_list = headers.keys.sort.map(&:downcase).join(';')
57
flat_request = [ "POST", "/", '', headers_block + "\n", headers_list, body_digest].join("\n")
58
[headers_list, flat_request]
59
end
60
61
def sign(creds, service, headers, body_digest, now)
62
date_mac = hmac("AWS4" + creds.fetch('SecretAccessKey'), now[0, 8])
63
region_mac = hmac(date_mac, datastore['Region'])
64
service_mac = hmac(region_mac, service)
65
credentials_mac = hmac(service_mac, 'aws4_request')
66
headers_list, flat_request = request_to_sign(headers, body_digest)
67
doc = "AWS4-HMAC-SHA256\n#{now}\n#{now[0, 8]}/#{datastore['Region']}/#{service}/aws4_request\n#{hexdigest(flat_request)}"
68
69
signature = hexhmac(credentials_mac, doc)
70
[headers_list, signature]
71
end
72
73
def auth(creds, service, headers, body_digest, now)
74
headers_list, signature = sign(creds, service, headers, body_digest, now)
75
"AWS4-HMAC-SHA256 Credential=#{creds.fetch('AccessKeyId')}/#{now[0, 8]}/#{datastore['Region']}/#{service}/aws4_request, SignedHeaders=#{headers_list}, Signature=#{signature}"
76
end
77
78
def body(vars_post)
79
pstr = ""
80
vars_post.each_pair do |var, val|
81
pstr << '&' unless pstr.empty?
82
pstr << var
83
pstr << '='
84
pstr << val
85
end
86
pstr
87
end
88
89
def headers(creds, service, body_digest, now = nil)
90
now = Time.now.utc.strftime("%Y%m%dT%H%M%SZ") if now.nil?
91
headers = {
92
'Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8',
93
'Accept-Encoding' => '',
94
'User-Agent' => USER_AGENT,
95
'X-Amz-Date' => now,
96
'Host' => datastore['RHOST'],
97
'X-Amz-Content-Sha256' => body_digest,
98
'Accept' => '*/*'
99
}
100
headers['X-Amz-Security-Token'] = creds['Token'] if creds['Token']
101
sign_headers = ['Content-Type', 'Host', 'User-Agent', 'X-Amz-Content-Sha256', 'X-Amz-Date']
102
auth_headers = headers.select { |k, _| sign_headers.include?(k) }
103
headers['Authorization'] = auth(creds, service, auth_headers, body_digest, now)
104
headers
105
end
106
107
def print_hsh(hsh)
108
return if hsh.nil? || !hsh.instance_of?(Hash)
109
hsh.each do |key, value|
110
vprint_status "#{key}: #{value}"
111
end
112
end
113
114
def print_results(doc, action)
115
response = "#{action}Response"
116
result = "#{action}Result"
117
resource = /[A-Z][a-z]+([A-Za-z]+)/.match(action)[1]
118
119
if doc["ErrorResponse"] && doc["ErrorResponse"]["Error"]
120
print_error doc["ErrorResponse"]["Error"]["Message"]
121
return nil
122
end
123
124
idoc = doc.fetch(response)
125
if idoc.nil? || !idoc.instance_of?(Hash)
126
print_error "Unexpected response structure"
127
return {}
128
end
129
idoc = idoc[result] if idoc[result]
130
idoc = idoc[resource] if idoc[resource]
131
132
if idoc["member"]
133
idoc["member"].each do |x|
134
print_hsh x
135
end
136
else
137
print_hsh idoc
138
end
139
idoc
140
end
141
142
def call_api(creds, service, api_params)
143
vprint_status("Connecting (#{datastore['RHOST']})...")
144
body = body(api_params)
145
body_length = body.length
146
body_digest = hexdigest(body)
147
begin
148
res = send_request_raw(
149
'method' => 'POST',
150
'data' => body,
151
'headers' => headers(creds, service, body_digest)
152
)
153
if res.nil?
154
print_error "#{peer} did not respond"
155
else
156
Hash.from_xml(res.body)
157
end
158
rescue => e
159
print_error e.message
160
end
161
end
162
163
def call_iam(creds, api_params)
164
api_params['Version'] = '2010-05-08' unless api_params['Version']
165
call_api(creds, 'iam', api_params)
166
end
167
168
def call_ec2(creds, api_params)
169
api_params['Version'] = '2015-10-01' unless api_params['Version']
170
call_api(creds, 'ec2', api_params)
171
end
172
173
def call_sts(creds, api_params)
174
api_params['Version'] = '2011-06-15' unless api_params['Version']
175
call_api(creds, 'sts', api_params)
176
end
177
end
178
end
179
end
180
end
181
182