Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scenario 4 Updates #7

Merged
merged 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cobra.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ def parse_arguments():
parser.add_argument("cloud_provider", choices=["aws", "azure", "gcp"], help="Cloud provider (aws, azure, gcp)")
parser.add_argument("action", choices=["launch", "status", "destroy"], help="Action to perform (launch, status, destroy)")
parser.add_argument("--simulation", action="store_true", help="Enable simulation mode")
parser.add_argument("--scenario", choices=["scenario-1", "scenario-2", "scenario-3"], default="scenario-1", help="Scenario selection")
parser.add_argument("--scenario", choices=["scenario-1", "scenario-2", "scenario-3", "scenario-4"], default="scenario-1", help="Scenario selection")
return parser.parse_args()

def main_function(cloud_provider, action, simulation, scenario):
Expand Down
15 changes: 15 additions & 0 deletions core/aws-scenario-3-output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"AMI ID": "ami-0eb9e8e8004581f2f",
"Attacker Server Instance ID": "i-0877167aef7ac90db",
"Attacker Server Public IP": "65.2.31.80",
"Attacker Server Role": "my-instance-profile-d35bf34",
"Key Pair Name": "my-key-pair-bd293a2",
"Region": "ap-south-1",
"Subnet ID": "subnet-0f82b1bdf83f603e7",
"Victim Server Instance ID": "i-0eee1fd0b30ce2200",
"Victim Server Public IP": "13.233.140.205",
"Victim Server Role": "my-instance-profile-2-b681d68",
"policy_name": "ec2-role-policy-afede80",
"role_name": "ec2-role-0860d0b",
"security_group_name": "web-sg-c2e6010"
}
21 changes: 15 additions & 6 deletions core/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from scenarios.scenario_2.scenario_2 import scenario_2_execute
from scenarios.scenario_2.scenario_2 import scenario_2_destroy
from scenarios.scenario_3.scenario_3 import scenario_3_execute
from scenarios.scenario_4.scenario_4 import scenario_4_execute

def loading_animation():
chars = "/—\\|"
Expand All @@ -32,9 +33,9 @@ def select_cloud_provider():
print(colored("3. GCP", color="green"))
while True:
try:
choice = int(input(colored("Enter your choice (1/2/3): ", color="yellow")))
if choice not in [1, 2, 3]:
raise ValueError(colored("Invalid choice. Please enter 1, 2, or 3.", color="red"))
choice = int(input(colored("Enter your choice (1/2/3/4): ", color="yellow")))
if choice not in [1, 2, 3, 4]:
raise ValueError(colored("Invalid choice. Please enter 1, 2, 3, or 4.", color="red"))
return choice
except ValueError as e:
print(e)
Expand All @@ -44,11 +45,12 @@ def select_attack_scenario(cloud_provider):
print(colored("1. Exploit Vulnerable Application, EC2 takeover, Credential Exfiltration & Anomalous Compute Provisioning", color="green"))
print(colored("2. Rest API exploit - command injection, credential exfiltration from backend lambda and privilige escalation, rogue identity creation & persistence", color="green"))
print(colored("3. Compromising a web app living inside a GKE Pod, access pod secret, escalate privilege, take over the cluster", color="green"))
print(colored("4. Exfiltrate EC2 role credentials using IMDSv2 with least privileged access", color="green"))
while True:
try:
choice = int(input(colored("Enter your choice: ", color="yellow")))
if choice not in [1, 2, 3]:
raise ValueError(colored("Invalid choice. Please enter 1 or 2.", color="red"))
if choice not in [1, 2, 3, 4]:
raise ValueError(colored("Invalid choice. Please enter 1, 2, 3 or 4.", color="red"))
return choice
except ValueError as e:
print(e)
Expand All @@ -75,6 +77,8 @@ def execute_scenario(x):
scenario_2_execute()
elif x == 3:
scenario_3_execute()
elif x == 4:
scenario_4_execute()
else:
print("Invalid Scenario Selected")
print(colored("Scenario executed successfully!", color="green"))
Expand All @@ -96,6 +100,8 @@ def main(cloud_provider, action, simulation, scenario):
execute_scenario(2)
elif scenario_choice == 3:
execute_scenario(3)
elif scenario_choice == 4:
execute_scenario(4)
#print(colored("Scenario coming soon!", color="yellow"))
elif action == 'status' and scenario == "scenario-1":
subprocess.call("cd ./scenarios/scenario_1/infra/ && pulumi stack ls", shell=True)
Expand All @@ -106,7 +112,10 @@ def main(cloud_provider, action, simulation, scenario):
elif action == 'destroy' and scenario == "scenario-2":
scenario_2_destroy()
elif action == 'destroy' and scenario == "scenario-3":
subprocess.call("cd ./scenarios/scenario_3/infra && pulumi destroy", shell=True)
subprocess.call("cd ./scenarios/scenario_3/infra && pulumi destroy -s gcp-scenario-1", shell=True)
elif action == 'destroy' and scenario == "scenario-4":
subprocess.call("cd ./scenarios/scenario_4/infra && pulumi destroy -s aws-scenario-3", shell=True)

else:
print('No options provided. --help to know more')

Expand Down
4 changes: 0 additions & 4 deletions scenarios/scenario_1/scenario_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,12 @@ def scenario_1_execute():
print(colored("Bringing up the Vulnerable Application", color="red"))
loading_animation()

# Use tqdm as a context manager to create the progress bar
sleep_duration = 300
with tqdm(total=sleep_duration, desc="Loading") as pbar:
# Loop until sleep_duration is reached
while sleep_duration > 0:
# Sleep for a shorter interval to update the progress bar
sleep_interval = min(1, sleep_duration)
sleep(sleep_interval)

# Update the progress bar with the elapsed time
pbar.update(sleep_interval)
sleep_duration -= sleep_interval

Expand Down
2 changes: 1 addition & 1 deletion scenarios/scenario_2/scenario_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def scenario_2_execute():
pbar.update(sleep_interval)
sleep_duration -= sleep_interval

subprocess.call("curl '"+API_GW_URL+"?query=ping'", shell=True)
#subprocess.call("curl '"+API_GW_URL+"?query=ping'", shell=True)

#Backdoor IAM User
print(colored("Creating a Backdoor User which can be used by the attacker", color="red"))
Expand Down
12 changes: 12 additions & 0 deletions scenarios/scenario_4/infra/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: infra
runtime:
name: python
options:
virtualenv: ../../../venv
description: A minimal AWS Python Pulumi program
config:
pulumi:tags:
value:
pulumi:template: ""


189 changes: 189 additions & 0 deletions scenarios/scenario_4/infra/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import pulumi
import pulumi_aws as aws
import os
import sys
import subprocess

def read_public_key(pub_key_path):
with open(pub_key_path, "r") as f:
public_key = f.read().strip()

return public_key

current = aws.get_region()

key_pair = aws.ec2.KeyPair("my-key-pair", public_key=read_public_key("../../../id_rsa.pub"))

ubuntu_ami = aws.ec2.get_ami(
filters=[
aws.ec2.GetAmiFilterArgs(
name="name",
values=["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"],
),
aws.ec2.GetAmiFilterArgs(
name="virtualization-type",
values=["hvm"],
),
],
owners=["099720109477"],
most_recent=True,

)

# Create an IAM role for EC2 instance
role = aws.iam.Role("ec2-role",
assume_role_policy="""{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
}"""
)

# Attach a policy to the role allowing necessary permissions
policy = aws.iam.RolePolicy("ec2-role-policy",
role=role.name,
policy="""{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:ModifyInstanceAttribute"

],
"Resource": "*"
}
]
}"""
)

role_2 = aws.iam.Role("ec2-role-imdsv2",
assume_role_policy="""{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
}"""
)

policy_2 = aws.iam.RolePolicy("server-role-policy",
role=role_2.name,
policy="""{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:*",
"s3:*"
],
"Resource": "*"
}
]
}"""
)

sg = aws.ec2.SecurityGroup("web-sg",
ingress=[
{
"protocol": "tcp",
"fromPort": 8080,
"toPort": 8080,
"cidrBlocks": ["0.0.0.0/0"]
},
{
"protocol": "tcp",
"fromPort": 22,
"toPort": 22,
"cidrBlocks": ["0.0.0.0/0"]
}
],
egress=[{
"protocol": "-1",
"fromPort": 0,
"toPort": 0,
"cidrBlocks": ["0.0.0.0/0"]
}]
)

# User data script to be executed when the instance starts
user_data_script = """
IyEvYmluL2Jhc2gKc3VkbyBhcHQgaW5zdGFsbCBhd3NjbGkgLXkKd2dldCAtUCAvaG9tZS91YnVudHUvIGh0dHBzOi8vbGFiLWZpbGVzLTAwZmZhYWJjYy5zMy5hbWF6b25hd3MuY29tL3VlYmEtbGFiL3NlcnZlci5weQpzdWRvIGNob3duIHVidW50dTp1YnVudHUgL2hvbWUvdWJ1bnR1L3NlcnZlci5weQp3Z2V0IC1QIC9ob21lL3VidW50dS8gaHR0cHM6Ly9sYWItZmlsZXMtMDBmZmFhYmNjLnMzLmFtYXpvbmF3cy5jb20vdWViYS1sYWIvdXNlcmRhdGEudHh0Cg==
"""

instance_profile = aws.iam.InstanceProfile("my-instance-profile",
role=role.name
)

instance_profile_2 = aws.iam.InstanceProfile("my-instance-profile-2",
role=role_2.name
)

# Create an EC2 instance with user data
instance = aws.ec2.Instance("attacker",
instance_type="t2.micro",
ami=ubuntu_ami.id,
iam_instance_profile=instance_profile.name,
security_groups=[sg.name],
user_data=user_data_script,
key_name=key_pair.key_name
)

instance1 = aws.ec2.Instance("imds-machine",
instance_type="t2.micro",
ami=ubuntu_ami.id,
iam_instance_profile=instance_profile_2.name,
security_groups=[sg.name],
key_name=key_pair.key_name)


# Export the public IP of the EC2 instance
print("Attacker Server Public IP")
pulumi.export("Attacker Server Public IP", instance.public_ip)

print("Victim Server Public IP")
pulumi.export("Victim Server Public IP", instance1.public_ip)

pulumi.export("role_name", role.name)

# Export the policy name
pulumi.export("policy_name", policy.name)

# Export the security group name
pulumi.export("security_group_name", sg.name)

# Export the instance profile name
print("Attacker Server Role")
pulumi.export("Attacker Server Role", instance_profile.name)

print("Victim Server Role")
pulumi.export("Victim Server Role", instance_profile_2.name)

# Export the instance ID
print("Attacker Server Instance ID")
pulumi.export("Attacker Server Instance ID", instance.id)

print("Victim Server Instance ID")
pulumi.export("Victim Server Instance ID", instance1.id)

pulumi.export("AMI ID", ubuntu_ami.id)

pulumi.export("Subnet ID", instance.subnet_id)

pulumi.export("Key Pair Name", key_pair.key_name)

pulumi.export("Region", current.name)

59 changes: 59 additions & 0 deletions scenarios/scenario_4/scenario_4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import os
import pyfiglet
import time
import subprocess
import json
from tqdm import tqdm
from time import sleep
from termcolor import colored
from core.helpers import generate_ssh_key
from core.helpers import loading_animation
from core.helpers import generate_ssh_key

def scenario_4_execute():
print("-"*30)
print(colored("Executing Scenraio 4 : Exfiltrate EC2 role credentials using IMDSv2 with least privileged access", color="red"))
loading_animation()
print("-"*30)

print(colored("Rolling out Infra", color="red"))
loading_animation()
print("-"*30)

file_path = "./core/aws-scenario-3-output.json"
if os.path.exists(file_path):
os.remove(file_path)
print("File '{}' found and deleted.".format(file_path))
else:
print("File '{}' not found.".format(file_path))

generate_ssh_key()

subprocess.call("cd ./scenarios/scenario_4/infra/ && pulumi up -s aws-scenario-3 -y", shell=True)
subprocess.call("cd ./scenarios/scenario_4/infra/ && pulumi stack -s aws-scenario-3 output --json >> ../../../core/aws-scenario-3-output.json", shell=True)

with open("./core/aws-scenario-3-output.json", "r") as file:
data = json.load(file)

ATTACKER_SERVER_PUBLIC_IP = data["Attacker Server Public IP"]
VICTIM_SERVER_PUBLIC_IP = data["Victim Server Public IP"]
ATTACKER_SERVER_INSTANCE_ID = data["Attacker Server Instance ID"]
VICTIM_SERVER_INSTANCE_ID = data["Victim Server Instance ID"]
SUBNET_ID = data["Subnet ID"]
AMI_ID = data["AMI ID"]
KEY_PAIR_NAME = data["Key Pair Name"]
REGION = data["Region"]

sleep_duration = 30
with tqdm(total=sleep_duration, desc="Loading") as pbar:
while sleep_duration > 0:
sleep_interval = min(1, sleep_duration)
sleep(sleep_interval)

pbar.update(sleep_interval)
sleep_duration -= sleep_interval

subprocess.call("ssh -o 'StrictHostKeyChecking accept-new' -i ./id_rsa ubuntu@"+ATTACKER_SERVER_PUBLIC_IP+" 'cat /etc/hostname'", shell=True)