Skip to content

Commit

Permalink
Merge pull request #7 from PaloAltoNetworks/scen-4
Browse files Browse the repository at this point in the history
Scenario 4 Updates
  • Loading branch information
hkoushik authored Jun 25, 2024
2 parents a41866c + 5869965 commit 99ba82d
Show file tree
Hide file tree
Showing 8 changed files with 292 additions and 12 deletions.
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)



0 comments on commit 99ba82d

Please sign in to comment.