
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]
| 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 |
인증으로 모든 카테고리 실행:```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