printerxpl-forge 6.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nse/README.md +204 -0
- nse/__init__.py +6 -0
- nse/install_nse.py +412 -0
- nse/lib/printerxpl.lua +238 -0
- nse/scripts/cups-info.nse +74 -0
- nse/scripts/cups-queue-info.nse +43 -0
- nse/scripts/hp-printers-cve-2022-1026.nse +121 -0
- nse/scripts/http-device-mac.nse +107 -0
- nse/scripts/http-hp-ilo-info.nse +121 -0
- nse/scripts/http-info-xerox-enum.nse +101 -0
- nse/scripts/http-vuln-cve2022-1026.nse +158 -0
- nse/scripts/lexmark-config.nse +89 -0
- nse/scripts/pjl-ready-message.nse +106 -0
- nse/scripts/printer-banner.nse +217 -0
- nse/scripts/printer-cups-rce.nse +189 -0
- nse/scripts/printer-cve-detect.nse +279 -0
- nse/scripts/printer-discover.nse +205 -0
- nse/scripts/printer-firmware-exposed.nse +219 -0
- nse/scripts/printer-hp-pjl.nse +192 -0
- nse/scripts/printer-http-ews.nse +293 -0
- nse/scripts/printer-ipp-info.nse +235 -0
- nse/scripts/printer-lexmark-ipp.nse +203 -0
- nse/scripts/printer-passback.nse +204 -0
- nse/scripts/printer-pjl-info.nse +146 -0
- nse/scripts/printer-printnightmare.nse +211 -0
- nse/scripts/printer-snmp-info.nse +176 -0
- nse/scripts/printer-vuln-check.nse +256 -0
- nse/scripts/snmp-device-mac.nse +93 -0
- nse/scripts/snmp-info.nse +146 -0
- nse/scripts/snmp-sysdescr.nse +70 -0
- printerxpl_forge-6.2.0.dist-info/METADATA +919 -0
- printerxpl_forge-6.2.0.dist-info/RECORD +97 -0
- printerxpl_forge-6.2.0.dist-info/WHEEL +5 -0
- printerxpl_forge-6.2.0.dist-info/entry_points.txt +4 -0
- printerxpl_forge-6.2.0.dist-info/licenses/LICENSE +21 -0
- printerxpl_forge-6.2.0.dist-info/top_level.txt +4 -0
- src/assets/fonts/gunplay.pfa +1671 -0
- src/assets/fonts/kshandwrt.pfa +315 -0
- src/assets/fonts/laksoner.pfa +2402 -0
- src/assets/fonts/paintcans.pfa +9699 -0
- src/assets/fonts/stencilod.pfa +4076 -0
- src/assets/fonts/takecover.pfa +26138 -0
- src/assets/fonts/topsecret.pfa +6652 -0
- src/assets/fonts/whoa.pfa +773 -0
- src/assets/mibs/HOST-RESOURCES-MIB +1540 -0
- src/assets/mibs/Printer-MIB +4389 -0
- src/assets/mibs/README.md +9 -0
- src/assets/mibs/SNMPv2-MIB +854 -0
- src/assets/overlays/hacker.eps +596 -0
- src/assets/overlays/smiley.eps +214 -0
- src/assets/overlays/smiley2.eps +240 -0
- src/core/attack_orchestrator.py +1025 -0
- src/core/capabilities.py +323 -0
- src/core/destructive_audit.py +430 -0
- src/core/discovery.py +488 -0
- src/core/osdetect.py +74 -0
- src/core/poly_runner.py +579 -0
- src/core/printer.py +1426 -0
- src/main.py +2134 -0
- src/modules/install_printer.py +318 -0
- src/modules/login_bruteforce.py +852 -0
- src/modules/pcl.py +506 -0
- src/modules/pjl.py +3575 -0
- src/modules/print_job.py +1290 -0
- src/modules/ps.py +1102 -0
- src/payloads/__init__.py +98 -0
- src/payloads/assets/overlays/notice.eps +9 -0
- src/protocols/__init__.py +19 -0
- src/protocols/firmware.py +738 -0
- src/protocols/ipp.py +216 -0
- src/protocols/ipp_attacks.py +609 -0
- src/protocols/lpd.py +141 -0
- src/protocols/network_map.py +1004 -0
- src/protocols/raw.py +173 -0
- src/protocols/smb.py +359 -0
- src/protocols/ssrf_pivot.py +427 -0
- src/protocols/storage.py +587 -0
- src/ui/__init__.py +6 -0
- src/ui/interactive.py +742 -0
- src/ui/spinner.py +112 -0
- src/ui/tables.py +132 -0
- src/utils/banner_grabber.py +852 -0
- src/utils/codebook.py +456 -0
- src/utils/config.py +522 -0
- src/utils/cve_loader.py +158 -0
- src/utils/default_creds.py +134 -0
- src/utils/discovery_online.py +1327 -0
- src/utils/exploit_manager.py +805 -0
- src/utils/fuzzer.py +220 -0
- src/utils/helper.py +732 -0
- src/utils/local_printers.py +307 -0
- src/utils/ml_engine.py +491 -0
- src/utils/operators.py +474 -0
- src/utils/ports.py +234 -0
- src/utils/vuln_scanner.py +823 -0
- src/utils/wordlist_loader.py +412 -0
- src/version.py +36 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-discover — High-speed multi-protocol printer discovery and fingerprinting.
|
|
3
|
+
|
|
4
|
+
Performs rapid identification of printers and MFPs on a network segment by probing:
|
|
5
|
+
9100/tcp JetDirect/RAW (PJL)
|
|
6
|
+
631/tcp IPP / CUPS
|
|
7
|
+
80,443/tcp HTTP EWS (Embedded Web Server)
|
|
8
|
+
515/tcp LPD (Line Printer Daemon)
|
|
9
|
+
161/udp SNMP
|
|
10
|
+
23/tcp Telnet (legacy printers)
|
|
11
|
+
3702/udp WSD (Windows printers)
|
|
12
|
+
|
|
13
|
+
Outputs: device type, vendor, model, firmware, open ports, and a quick verdict on
|
|
14
|
+
whether PrinterXPL-Forge or EmbedXPL-Forge should be used for complete testing.
|
|
15
|
+
|
|
16
|
+
Designed for quick network-wide sweeps:
|
|
17
|
+
nmap -sV --open -p 9100,631,80,443,515,161,23 --script printer-discover <CIDR>
|
|
18
|
+
|
|
19
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
20
|
+
]]
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
-- @usage
|
|
24
|
+
-- nmap -p 9100,631,80 --script printer-discover <target>
|
|
25
|
+
-- nmap -sV --open -p 9100,631,80,443 --script printer-discover 192.168.1.0/24
|
|
26
|
+
-- @output
|
|
27
|
+
-- 192.168.1.100:
|
|
28
|
+
-- | printer-discover:
|
|
29
|
+
-- | Device-Type : Network Printer / MFP
|
|
30
|
+
-- | Vendor : HP
|
|
31
|
+
-- | Model : LaserJet Enterprise M606dn
|
|
32
|
+
-- | Open-Ports : 9100 (PJL), 631 (IPP), 80 (EWS)
|
|
33
|
+
-- | CVEs : 5 potential (CRITICAL: CVE-2025-26506, CVE-2017-2741)
|
|
34
|
+
-- | Tool : printerxpl-forge (pip install printerxpl-forge)
|
|
35
|
+
-- |_ Quick-Scan : printerxpl-forge scan --target 192.168.1.100
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
categories = { "discovery", "safe", "default" }
|
|
39
|
+
author = "André Henrique (@mrhenrike) | União Geek"
|
|
40
|
+
license = "Same as Nmap -- See https://nmap.org/book/man-legal.html"
|
|
41
|
+
|
|
42
|
+
local stdnse = require "stdnse"
|
|
43
|
+
local shortport = require "shortport"
|
|
44
|
+
local comm = require "comm"
|
|
45
|
+
local http = require "http"
|
|
46
|
+
local string = require "string"
|
|
47
|
+
local table = require "table"
|
|
48
|
+
local nmap = require "nmap"
|
|
49
|
+
|
|
50
|
+
portrule = shortport.port_or_service(
|
|
51
|
+
{ 9100, 631, 80, 443, 515, 23 },
|
|
52
|
+
{ "jetdirect", "ipp", "http", "https", "printer", "telnet" },
|
|
53
|
+
"tcp"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
local UEL = "\x1b%-12345X"
|
|
57
|
+
|
|
58
|
+
-- Vendor patterns (ordered by specificity)
|
|
59
|
+
local VENDOR_PATTERNS = {
|
|
60
|
+
{ pat = "laserjet|pagewide|officejet|jetdirect|futuresmart|hp print",
|
|
61
|
+
vendor = "HP", tool = "printerxpl-forge", cve_count_est = 5 },
|
|
62
|
+
{ pat = "imagerunner|imageclass|imagepress|pixma|lbp[%s%-]?%d",
|
|
63
|
+
vendor = "Canon", tool = "printerxpl-forge", cve_count_est = 3 },
|
|
64
|
+
{ pat = "lexmark",
|
|
65
|
+
vendor = "Lexmark", tool = "printerxpl-forge", cve_count_est = 6 },
|
|
66
|
+
{ pat = "versalink|workcentre|altalink|phaser|colorqube",
|
|
67
|
+
vendor = "Xerox", tool = "printerxpl-forge", cve_count_est = 3 },
|
|
68
|
+
{ pat = "aficio|ricoh mp|ricoh sp|ricoh im",
|
|
69
|
+
vendor = "Ricoh", tool = "printerxpl-forge", cve_count_est = 3 },
|
|
70
|
+
{ pat = "brother mfc|brother dcp|brother hl",
|
|
71
|
+
vendor = "Brother", tool = "printerxpl-forge", cve_count_est = 2 },
|
|
72
|
+
{ pat = "bizhub|konica minolta|konicaminolta",
|
|
73
|
+
vendor = "Konica Minolta", tool = "printerxpl-forge", cve_count_est = 2 },
|
|
74
|
+
{ pat = "ecosys|taskalfa|kyocera",
|
|
75
|
+
vendor = "Kyocera", tool = "printerxpl-forge", cve_count_est = 1 },
|
|
76
|
+
{ pat = "sharp mx|sharp ar|sharp bp",
|
|
77
|
+
vendor = "Sharp", tool = "printerxpl-forge", cve_count_est = 1 },
|
|
78
|
+
{ pat = "e.studio|topaccess|toshiba",
|
|
79
|
+
vendor = "Toshiba", tool = "printerxpl-forge", cve_count_est = 1 },
|
|
80
|
+
{ pat = "workforce|ecotank|epson stylus",
|
|
81
|
+
vendor = "Epson", tool = "printerxpl-forge", cve_count_est = 1 },
|
|
82
|
+
{ pat = "samsung scx|samsung clx|samsung ml",
|
|
83
|
+
vendor = "Samsung", tool = "printerxpl-forge", cve_count_est = 2 },
|
|
84
|
+
{ pat = "cups",
|
|
85
|
+
vendor = "Linux/CUPS", tool = "printerxpl-forge", cve_count_est = 4 },
|
|
86
|
+
{ pat = "zebra|zpl ii",
|
|
87
|
+
vendor = "Zebra", tool = "embedxpl-forge", cve_count_est = 1 },
|
|
88
|
+
{ pat = "oki data|okidata",
|
|
89
|
+
vendor = "OKI", tool = "printerxpl-forge", cve_count_est = 1 },
|
|
90
|
+
{ pat = "dell.*print|dell.*laser",
|
|
91
|
+
vendor = "Dell", tool = "printerxpl-forge", cve_count_est = 1 },
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
local function detect_vendor(text)
|
|
95
|
+
if not text then return nil, nil, nil end
|
|
96
|
+
local t = text:lower()
|
|
97
|
+
for _, v in ipairs(VENDOR_PATTERNS) do
|
|
98
|
+
if t:match(v.pat) then
|
|
99
|
+
return v.vendor, v.tool, v.cve_count_est
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
return nil, nil, nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
local function extract_model(text)
|
|
106
|
+
if not text then return nil end
|
|
107
|
+
local model = text:match('[Dd][Ee][Ss][Cc][Rr][Ii][Pp][Tt][Ii][Oo][Nn]%s*=%s*"([^"]+)"')
|
|
108
|
+
or text:match("<title>([^<]+)</title>")
|
|
109
|
+
or text:match("[Mm]odel[^:]*:%s*([^\r\n]+)")
|
|
110
|
+
or text:match("[Pp]roduct[^:]*:%s*([^\r\n]+)")
|
|
111
|
+
if model then return model:match("^%s*(.-)%s*$"):sub(1, 80) end
|
|
112
|
+
return nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
local function quick_pjl(host, port)
|
|
116
|
+
local ok, r = comm.exchange(host, port,
|
|
117
|
+
UEL .. "@PJL\r\n@PJL INFO ID\r\n" .. UEL,
|
|
118
|
+
{ timeout = 3000, bytes = 1024 })
|
|
119
|
+
if ok then return r end
|
|
120
|
+
return nil
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
local function quick_http(host, port)
|
|
124
|
+
local r = http.get(host, port.number, "/", { timeout = 3000 })
|
|
125
|
+
if r and r.status == 200 and r.body then return r.body end
|
|
126
|
+
return nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
local function quick_ipp(host, port)
|
|
130
|
+
local r = http.post(host, port.number, "/printers/",
|
|
131
|
+
{ header = { ["Content-Type"] = "application/ipp" }, timeout = 3000 },
|
|
132
|
+
nil,
|
|
133
|
+
"\x02\x00\x00\x0b\x00\x00\x00\x01\x01" ..
|
|
134
|
+
"\x47\x00\x12attributes-charset\x00\x05utf-8" ..
|
|
135
|
+
"\x48\x00\x1battributes-natural-language\x00\x05en-us" ..
|
|
136
|
+
"\x03")
|
|
137
|
+
if r and r.body then return r.body end
|
|
138
|
+
return nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
action = function(host, port)
|
|
142
|
+
local out = stdnse.output_table()
|
|
143
|
+
|
|
144
|
+
-- Collect banner based on port
|
|
145
|
+
local banner = nil
|
|
146
|
+
local portnum = port.number
|
|
147
|
+
|
|
148
|
+
if portnum == 9100 then
|
|
149
|
+
banner = quick_pjl(host, port)
|
|
150
|
+
if banner then out["Source"] = "PJL (port 9100)" end
|
|
151
|
+
elseif portnum == 631 then
|
|
152
|
+
banner = quick_ipp(host, port)
|
|
153
|
+
if banner then out["Source"] = "IPP (port 631)" end
|
|
154
|
+
elseif portnum == 80 or portnum == 443 then
|
|
155
|
+
banner = quick_http(host, port)
|
|
156
|
+
if banner then out["Source"] = "HTTP EWS (port " .. portnum .. ")" end
|
|
157
|
+
elseif portnum == 515 then
|
|
158
|
+
local ok, r = comm.exchange(host, port, "\x01\n", { timeout = 2000, bytes = 256 })
|
|
159
|
+
if ok then banner = r; out["Source"] = "LPD (port 515)" end
|
|
160
|
+
elseif portnum == 23 then
|
|
161
|
+
local ok, r = comm.exchange(host, port, "\n", { timeout = 2000, bytes = 256 })
|
|
162
|
+
if ok then banner = r; out["Source"] = "Telnet (port 23)" end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
if not banner or #banner < 5 then
|
|
166
|
+
return "No banner received — port may be filtered or device requires special probe"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
-- Detect vendor
|
|
170
|
+
local vendor, tool, cve_est = detect_vendor(banner)
|
|
171
|
+
local model = extract_model(banner)
|
|
172
|
+
|
|
173
|
+
-- Store for other scripts
|
|
174
|
+
if vendor then
|
|
175
|
+
stdnse.registry_set({ host.ip, "printer-vendor" }, vendor)
|
|
176
|
+
stdnse.registry_set({ host.ip, "printer-banner" }, banner:sub(1, 2048))
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
out["Device-Type"] = "Network Printer / MFP"
|
|
180
|
+
out["Vendor"] = vendor or "Unknown (banner detected — check printer-banner)"
|
|
181
|
+
|
|
182
|
+
if model then out["Model"] = model end
|
|
183
|
+
|
|
184
|
+
local fw = banner:match("DATECODE%s*=%s*([%w%.%-]+)")
|
|
185
|
+
or banner:match("[Ff]irmware[Vv]er%s*=%s*([%w%.%-]+)")
|
|
186
|
+
if fw then out["Firmware"] = fw end
|
|
187
|
+
|
|
188
|
+
-- CVE count hint
|
|
189
|
+
if vendor and cve_est and cve_est > 0 then
|
|
190
|
+
out["CVE-Potential"] = string.format(
|
|
191
|
+
"~%d CVEs for %s in PrinterXPL-Forge DB (run printer-cve-detect for details)",
|
|
192
|
+
cve_est, vendor)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
-- Tool recommendation
|
|
196
|
+
out["Tool"] = (tool or "printerxpl-forge") ..
|
|
197
|
+
" → pip install " .. (tool or "printerxpl-forge")
|
|
198
|
+
out["Quick-Scan"] = string.format(
|
|
199
|
+
"printerxpl-forge scan --target %s", host.ip)
|
|
200
|
+
out["Full-Check"] = string.format(
|
|
201
|
+
"nmap -p 9100,631,80,443,427,445 --script printer-cve-detect,printer-vuln-check %s",
|
|
202
|
+
host.ip)
|
|
203
|
+
|
|
204
|
+
return out
|
|
205
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-firmware-exposed — Firmware update endpoint exposure detection.
|
|
3
|
+
|
|
4
|
+
Probes vendor-specific HTTP endpoints commonly used for firmware updates to detect:
|
|
5
|
+
- Unauthenticated firmware upload interfaces
|
|
6
|
+
- Exposed firmware management APIs
|
|
7
|
+
- Insecure firmware download endpoints (prone to MitM firmware replacement)
|
|
8
|
+
|
|
9
|
+
Covers: HP, Ricoh, Konica Minolta, Brother, Epson, Kyocera, Lexmark, Canon,
|
|
10
|
+
Toshiba, Xerox, Sharp, Samsung, OKI.
|
|
11
|
+
|
|
12
|
+
CVE coverage: CVE-2023-6018, FIRMWARE-RICOH-001, FIRMWARE-KONICA-001,
|
|
13
|
+
FIRMWARE-BROTHER-001, FIRMWARE-EPSON-001, CVE-2023-50738
|
|
14
|
+
|
|
15
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
16
|
+
]]
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
-- @usage
|
|
20
|
+
-- nmap -p 80,443,8080 --script printer-firmware-exposed <target>
|
|
21
|
+
-- @output
|
|
22
|
+
-- PORT STATE SERVICE
|
|
23
|
+
-- 80/tcp open http
|
|
24
|
+
-- | printer-firmware-exposed:
|
|
25
|
+
-- | Exposed Endpoints (3):
|
|
26
|
+
-- | /hp/device/FirmwareUpdate (HTTP 200 — no auth required)
|
|
27
|
+
-- | /cgi-bin/fw_update (HTTP 200 — no auth required)
|
|
28
|
+
-- | /firmware (HTTP 302 → /firmware/upload)
|
|
29
|
+
-- | [CRITICAL] CVE-2023-6018 — HP firmware auth bypass → arbitrary .RFU upload
|
|
30
|
+
-- | [HIGH] FIRMWARE-RICOH-001 — Ricoh Aficio unsigned firmware upload
|
|
31
|
+
-- | Verdict : VULNERABLE — 3 unprotected firmware endpoints found
|
|
32
|
+
-- |_ Suggest : printerxpl-forge run --module xpl/research/research-hp-fw-bypass
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
categories = { "vuln", "safe", "discovery" }
|
|
36
|
+
author = "André Henrique (@mrhenrike) | União Geek"
|
|
37
|
+
license = "Same as Nmap -- See https://nmap.org/book/man-legal.html"
|
|
38
|
+
|
|
39
|
+
local stdnse = require "stdnse"
|
|
40
|
+
local shortport = require "shortport"
|
|
41
|
+
local http = require "http"
|
|
42
|
+
local string = require "string"
|
|
43
|
+
local table = require "table"
|
|
44
|
+
|
|
45
|
+
portrule = shortport.http
|
|
46
|
+
|
|
47
|
+
-- Firmware endpoint database: { path, vendor, method, cve, sev, cvss, xpl, desc }
|
|
48
|
+
local FW_ENDPOINTS = {
|
|
49
|
+
-- HP
|
|
50
|
+
{ path="/hp/device/FirmwareUpdate", vendor="HP", method="GET", auth_check=true,
|
|
51
|
+
cve="CVE-2023-6018", sev="CRITICAL", cvss=9.8,
|
|
52
|
+
desc="HP FutureSmart firmware auth bypass → arbitrary .RFU upload",
|
|
53
|
+
xpl="xpl/research/research-hp-fw-bypass" },
|
|
54
|
+
{ path="/hp/device/info_firmwareupdate.htm", vendor="HP", method="GET", auth_check=true,
|
|
55
|
+
cve="CVE-2023-6018", sev="CRITICAL", cvss=9.8,
|
|
56
|
+
desc="HP firmware update info page (EWS unauthenticated)",
|
|
57
|
+
xpl="xpl/research/research-hp-fw-bypass" },
|
|
58
|
+
-- Ricoh
|
|
59
|
+
{ path="/cgi-bin/fw_update", vendor="Ricoh", method="GET", auth_check=true,
|
|
60
|
+
cve="FIRMWARE-RICOH-001", sev="HIGH", cvss=8.0,
|
|
61
|
+
desc="Ricoh Aficio unsigned firmware upload via /cgi-bin/fw_update",
|
|
62
|
+
xpl="xpl/research/research-ricoh-fw-unsigned" },
|
|
63
|
+
{ path="/web/entry.cgi?id=fw", vendor="Ricoh", method="GET", auth_check=true,
|
|
64
|
+
cve="FIRMWARE-RICOH-001", sev="HIGH", cvss=8.0,
|
|
65
|
+
desc="Ricoh web firmware management entry (no signature check)",
|
|
66
|
+
xpl="xpl/research/research-ricoh-fw-unsigned" },
|
|
67
|
+
-- Konica Minolta
|
|
68
|
+
{ path="/wcd/fwdl.cgi", vendor="Konica", method="GET", auth_check=true,
|
|
69
|
+
cve="FIRMWARE-KONICA-001", sev="HIGH", cvss=8.5,
|
|
70
|
+
desc="Konica bizhub firmware download/upload via /wcd/fwdl.cgi",
|
|
71
|
+
xpl="xpl/research/research-konica-fw-upload" },
|
|
72
|
+
{ path="/wcd/index.html", vendor="Konica", method="GET", auth_check=false,
|
|
73
|
+
cve="FIRMWARE-KONICA-001", sev="HIGH", cvss=8.5,
|
|
74
|
+
desc="Konica bizhub admin page (check for firmware menu)",
|
|
75
|
+
xpl="xpl/research/research-konica-fw-upload" },
|
|
76
|
+
-- Brother
|
|
77
|
+
{ path="/firmware", vendor="Brother", method="GET", auth_check=true,
|
|
78
|
+
cve="FIRMWARE-BROTHER-001", sev="HIGH", cvss=8.0,
|
|
79
|
+
desc="Brother MFC/DCP firmware upload endpoint",
|
|
80
|
+
xpl="xpl/research/research-brother-fw-upload" },
|
|
81
|
+
{ path="/general/firmware_update.html", vendor="Brother", method="GET", auth_check=true,
|
|
82
|
+
cve="FIRMWARE-BROTHER-001", sev="HIGH", cvss=8.0,
|
|
83
|
+
desc="Brother firmware update page (chain with CVE-2024-51978)",
|
|
84
|
+
xpl="xpl/research/research-brother-fw-upload" },
|
|
85
|
+
-- Epson
|
|
86
|
+
{ path="/PRESENTATION/ADVANCED/FIRMWARE_UPDATE/TOP", vendor="Epson", method="GET", auth_check=true,
|
|
87
|
+
cve="FIRMWARE-EPSON-001", sev="HIGH", cvss=7.5,
|
|
88
|
+
desc="Epson firmware update presentation layer (WorkForce/EcoTank)",
|
|
89
|
+
xpl="xpl/research/research-epson-fw-update" },
|
|
90
|
+
-- Canon
|
|
91
|
+
{ path="/portal_top.html", vendor="Canon", method="GET", auth_check=false,
|
|
92
|
+
cve=nil, sev="INFO", cvss=0,
|
|
93
|
+
desc="Canon imageRUNNER admin portal (check for firmware menu)",
|
|
94
|
+
xpl=nil },
|
|
95
|
+
{ path="/cgi-bin/fwdl.cgi", vendor="Canon", method="GET", auth_check=true,
|
|
96
|
+
cve="CVE-2025-14235", sev="CRITICAL", cvss=9.8,
|
|
97
|
+
desc="Canon imageCLASS firmware download endpoint",
|
|
98
|
+
xpl="xpl/research/research-canon-pcl-bof" },
|
|
99
|
+
-- Kyocera
|
|
100
|
+
{ path="/js/jssrc/model/system/FirmwareUpdate.js", vendor="Kyocera", method="GET", auth_check=false,
|
|
101
|
+
cve=nil, sev="INFO", cvss=0,
|
|
102
|
+
desc="Kyocera ECOSYS firmware update JS source detected",
|
|
103
|
+
xpl=nil },
|
|
104
|
+
-- Lexmark
|
|
105
|
+
{ path="/cgi-bin/dynamic/printer/PrinterStatus.html", vendor="Lexmark", method="GET", auth_check=false,
|
|
106
|
+
cve="CVE-2023-50738", sev="HIGH", cvss=7.2,
|
|
107
|
+
desc="Lexmark firmware downgrade bypass surface",
|
|
108
|
+
xpl="xpl/research/research-lexmark-fw-decrypt" },
|
|
109
|
+
-- Toshiba
|
|
110
|
+
{ path="/TopAccess/Admin/FirmwareUpdate", vendor="Toshiba", method="GET", auth_check=true,
|
|
111
|
+
cve="CVE-2024-21911", sev="HIGH", cvss=8.8,
|
|
112
|
+
desc="Toshiba e-STUDIO TopAccess firmware update (auth bypass CVE-2024-21911)",
|
|
113
|
+
xpl="xpl/research/research-toshiba-auth-bypass" },
|
|
114
|
+
-- Xerox
|
|
115
|
+
{ path="/cgi-bin/dynamic/index.html", vendor="Xerox", method="GET", auth_check=false,
|
|
116
|
+
cve=nil, sev="INFO", cvss=0,
|
|
117
|
+
desc="Xerox EWS dynamic page",
|
|
118
|
+
xpl=nil },
|
|
119
|
+
-- Generic
|
|
120
|
+
{ path="/firmware/update", vendor="Generic", method="GET", auth_check=true,
|
|
121
|
+
cve=nil, sev="MEDIUM", cvss=5.0,
|
|
122
|
+
desc="Generic firmware update endpoint",
|
|
123
|
+
xpl="xpl/research/research-generic-fw-upload" },
|
|
124
|
+
{ path="/upgrade", vendor="Generic", method="GET", auth_check=true,
|
|
125
|
+
cve=nil, sev="MEDIUM", cvss=5.0,
|
|
126
|
+
desc="Generic upgrade endpoint",
|
|
127
|
+
xpl=nil },
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
action = function(host, port)
|
|
131
|
+
local out = stdnse.output_table()
|
|
132
|
+
|
|
133
|
+
-- Quick root probe to confirm HTTP service
|
|
134
|
+
local root = http.get(host, port.number, "/", { timeout = 4000 })
|
|
135
|
+
if not root then return "HTTP not responding" end
|
|
136
|
+
|
|
137
|
+
local exposed = {}
|
|
138
|
+
local cves_found = {}
|
|
139
|
+
local seen_cves = {}
|
|
140
|
+
|
|
141
|
+
for _, ep in ipairs(FW_ENDPOINTS) do
|
|
142
|
+
local resp = http.get(host, port.number, ep.path,
|
|
143
|
+
{ timeout = 4000, redirect_ok = false })
|
|
144
|
+
if resp then
|
|
145
|
+
local interesting = (resp.status == 200) or
|
|
146
|
+
(resp.status == 302) or
|
|
147
|
+
(resp.status == 401) -- 401 = exists but needs auth
|
|
148
|
+
if interesting then
|
|
149
|
+
local status_str = tostring(resp.status)
|
|
150
|
+
local needs_auth = (resp.status == 401 or resp.status == 403)
|
|
151
|
+
|
|
152
|
+
if resp.status == 200 and ep.auth_check then
|
|
153
|
+
-- Unprotected! High severity
|
|
154
|
+
table.insert(exposed, string.format("%s (HTTP %s — no auth required) [%s]",
|
|
155
|
+
ep.path, status_str, ep.vendor))
|
|
156
|
+
if ep.cve and not seen_cves[ep.cve] then
|
|
157
|
+
seen_cves[ep.cve] = true
|
|
158
|
+
table.insert(cves_found, ep)
|
|
159
|
+
end
|
|
160
|
+
elseif resp.status == 200 then
|
|
161
|
+
table.insert(exposed, string.format("%s (HTTP %s) [%s]",
|
|
162
|
+
ep.path, status_str, ep.vendor))
|
|
163
|
+
elseif needs_auth then
|
|
164
|
+
table.insert(exposed, string.format("%s (HTTP %s — auth required, endpoint EXISTS) [%s]",
|
|
165
|
+
ep.path, status_str, ep.vendor))
|
|
166
|
+
elseif resp.status == 302 then
|
|
167
|
+
local loc = resp.header and resp.header["location"] or "?"
|
|
168
|
+
table.insert(exposed, string.format("%s → %s (HTTP %s) [%s]",
|
|
169
|
+
ep.path, loc, status_str, ep.vendor))
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
if #exposed == 0 then
|
|
176
|
+
return "No firmware endpoints found — device may be patched or use non-standard paths"
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
out["Exposed-Endpoints"] = exposed
|
|
180
|
+
|
|
181
|
+
-- Build CVE output
|
|
182
|
+
if #cves_found > 0 then
|
|
183
|
+
local cve_lines = {}
|
|
184
|
+
for _, c in ipairs(cves_found) do
|
|
185
|
+
if c.cve then
|
|
186
|
+
table.insert(cve_lines, string.format("[%s] %s (CVSS %.1f) — %s",
|
|
187
|
+
c.sev, c.cve, c.cvss, c.desc))
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
out["CVEs"] = cve_lines
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
-- Verdict
|
|
194
|
+
local unauth_count = 0
|
|
195
|
+
for _, ep_str in ipairs(exposed) do
|
|
196
|
+
if ep_str:match("no auth required") then unauth_count = unauth_count + 1 end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
if unauth_count > 0 then
|
|
200
|
+
out["Verdict"] = string.format(
|
|
201
|
+
"VULNERABLE — %d unprotected firmware endpoint(s) found", unauth_count)
|
|
202
|
+
if cves_found[1] and cves_found[1].xpl then
|
|
203
|
+
out["Suggest"] = "printerxpl-forge run --module " .. cves_found[1].xpl .. " --target " .. host.ip
|
|
204
|
+
end
|
|
205
|
+
elseif #exposed > 0 then
|
|
206
|
+
out["Verdict"] = string.format(
|
|
207
|
+
"POSSIBLY VULNERABLE — %d firmware endpoint(s) detected (auth required)", #exposed)
|
|
208
|
+
if cves_found[1] and cves_found[1].xpl then
|
|
209
|
+
out["Suggest"] = "printerxpl-forge run --module " .. cves_found[1].xpl ..
|
|
210
|
+
" --dry-run --target " .. host.ip
|
|
211
|
+
end
|
|
212
|
+
else
|
|
213
|
+
out["Verdict"] = "NOT VULNERABLE — no firmware endpoints accessible"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
out["Full-Scan"] = "pip install printerxpl-forge | printerxpl-forge scan --target " .. host.ip
|
|
217
|
+
|
|
218
|
+
return out
|
|
219
|
+
end
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-hp-pjl — HP-specific PJL deep enumeration and vulnerability assessment.
|
|
3
|
+
|
|
4
|
+
Targets HP LaserJet, OfficeJet, and PageWide devices via port 9100.
|
|
5
|
+
Performs: model + firmware version extraction, HP NVRAM variable dump,
|
|
6
|
+
filesystem directory listing (PJL FSDIRLIST), and direct CVE checks for:
|
|
7
|
+
|
|
8
|
+
- CVE-2025-26506: HP LaserJet Enterprise PostScript RCE (unauthenticated)
|
|
9
|
+
- CVE-2017-2741: HP PageWide PJL path traversal to arbitrary file read/write → RCE
|
|
10
|
+
- CVE-2018-5924: Faxploit — fax stack overflow (HP / Samsung)
|
|
11
|
+
- CVE-2023-6018: HP FutureSmart firmware auth bypass → arbitrary firmware upload
|
|
12
|
+
- CVE-2023-1707: HP FutureSmart 5.6 scan job data disclosure
|
|
13
|
+
|
|
14
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
15
|
+
]]
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
-- @usage
|
|
19
|
+
-- nmap -p 9100 --script printer-hp-pjl <target>
|
|
20
|
+
-- @output
|
|
21
|
+
-- PORT STATE SERVICE
|
|
22
|
+
-- 9100/tcp open jetdirect
|
|
23
|
+
-- | printer-hp-pjl:
|
|
24
|
+
-- | Vendor : HP
|
|
25
|
+
-- | Model : HP LaserJet Enterprise M606dn
|
|
26
|
+
-- | Firmware : FY5L.04.A.00.00 (FutureSmart 5.6)
|
|
27
|
+
-- | Serial : JPGD12345
|
|
28
|
+
-- | Filesystem : 0:\ (HDD) — FSDIRLIST accessible
|
|
29
|
+
-- | [CRITICAL] CVE-2025-26506 (CVSS 9.8) — PS RCE → printerxpl run --module xpl/edb-cve-2025-26506
|
|
30
|
+
-- | [CRITICAL] CVE-2017-2741 (CVSS 9.8) — PJL path traversal RCE → printerxpl run --module xpl/edb-cve-2017-2741
|
|
31
|
+
-- | Verdict : POSSIBLY VULNERABLE (HP LaserJet on 9100 — version fingerprint not sufficient)
|
|
32
|
+
-- |_ Suggest : printerxpl-forge run --module xpl/edb-cve-2025-26506 --target <IP>
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
categories = { "vuln", "safe", "discovery" }
|
|
36
|
+
author = "André Henrique (@mrhenrike) | União Geek"
|
|
37
|
+
license = "Same as Nmap -- See https://nmap.org/book/man-legal.html"
|
|
38
|
+
|
|
39
|
+
local stdnse = require "stdnse"
|
|
40
|
+
local shortport = require "shortport"
|
|
41
|
+
local comm = require "comm"
|
|
42
|
+
local http = require "http"
|
|
43
|
+
local string = require "string"
|
|
44
|
+
local table = require "table"
|
|
45
|
+
|
|
46
|
+
portrule = shortport.port_or_service(9100, "jetdirect", "tcp")
|
|
47
|
+
|
|
48
|
+
local UEL = "\x1b%-12345X"
|
|
49
|
+
|
|
50
|
+
local function pjl_exchange(host, port, cmd, bytes)
|
|
51
|
+
bytes = bytes or 8192
|
|
52
|
+
local payload = UEL .. "@PJL\r\n" .. cmd .. "\r\n" .. UEL
|
|
53
|
+
local ok, resp = comm.exchange(host, port, payload,
|
|
54
|
+
{ timeout = 7000, bytes = bytes })
|
|
55
|
+
if ok then return resp end
|
|
56
|
+
return nil
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
local function extract(text, pattern)
|
|
60
|
+
if not text then return nil end
|
|
61
|
+
local v = text:match(pattern)
|
|
62
|
+
if v then return v:match("^%s*(.-)%s*$") end
|
|
63
|
+
return nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
action = function(host, port)
|
|
67
|
+
local out = stdnse.output_table()
|
|
68
|
+
|
|
69
|
+
-- Batch query: ID + CONFIG + FILESYS + VARIABLES + PRODINFO
|
|
70
|
+
local batch = "@PJL INFO ID\r\n@PJL INFO CONFIG\r\n@PJL INFO FILESYS\r\n@PJL INFO VARIABLES\r\n@PJL INFO PRODINFO\r\n"
|
|
71
|
+
local resp = pjl_exchange(host, port, batch, 16384)
|
|
72
|
+
if not resp then
|
|
73
|
+
return "No PJL response — port may be filtered or device is not HP JetDirect-compatible"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
-- Vendor confirmation
|
|
77
|
+
local is_hp = resp:lower():match("hp") or resp:lower():match("laserjet") or
|
|
78
|
+
resp:lower():match("officejet") or resp:lower():match("futuresmart")
|
|
79
|
+
if not is_hp then
|
|
80
|
+
return "HP device not confirmed via PJL (use printer-pjl-info for generic PJL)"
|
|
81
|
+
end
|
|
82
|
+
out["Vendor"] = "HP"
|
|
83
|
+
|
|
84
|
+
-- Model
|
|
85
|
+
local model = extract(resp, 'DESCRIPTION%s*=%s*"([^"]+)"')
|
|
86
|
+
or extract(resp, "[Mm]odel%s*:%s*([^\r\n]+)")
|
|
87
|
+
or extract(resp, "@PJL INFO ID%s*\r?\n([^\r\n]+)")
|
|
88
|
+
if model then out["Model"] = model end
|
|
89
|
+
|
|
90
|
+
-- Firmware
|
|
91
|
+
local fw = extract(resp, "DATECODE%s*=%s*([%w%.%-]+)")
|
|
92
|
+
or extract(resp, "FW%s*=%s*([%w%.%-]+)")
|
|
93
|
+
or extract(resp, "[Ff]irmware[Vv]er%s*=%s*([%w%.%-]+)")
|
|
94
|
+
if fw then out["Firmware"] = fw end
|
|
95
|
+
|
|
96
|
+
-- FutureSmart version
|
|
97
|
+
local fs_ver = extract(resp, "[Ff]uture[Ss]mart%s+([%d%.]+)")
|
|
98
|
+
if fs_ver then out["FutureSmart"] = fs_ver end
|
|
99
|
+
|
|
100
|
+
-- Serial
|
|
101
|
+
local sn = extract(resp, "SERIALNUMBER%s*=%s*([%w%-]+)")
|
|
102
|
+
if sn then out["Serial"] = sn end
|
|
103
|
+
|
|
104
|
+
-- Memory
|
|
105
|
+
local mem = extract(resp, "TOTALMEMORY%s*=%s*(%d+)")
|
|
106
|
+
if mem then out["Memory-KB"] = mem end
|
|
107
|
+
|
|
108
|
+
-- Filesystem
|
|
109
|
+
local fs_listing = extract(resp, "VOLUME%s*=%s*([^\r\n]+)")
|
|
110
|
+
if fs_listing then
|
|
111
|
+
out["Filesystem"] = fs_listing
|
|
112
|
+
-- Try FSDIRLIST
|
|
113
|
+
local dir_resp = pjl_exchange(host, port,
|
|
114
|
+
'@PJL FSDIRLIST NAME="0:\\" ENTRY=1 COUNT=20\r\n', 4096)
|
|
115
|
+
if dir_resp and dir_resp:match("TYPE") then
|
|
116
|
+
out["FS-Directory"] = dir_resp:match("(.-)%x1b"):match("^%s*(.-)%s*$") or "(see raw)"
|
|
117
|
+
out["FS-Access"] = "FSDIRLIST accessible — filesystem enumeration possible"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
-- CVE assessment
|
|
122
|
+
local cves = {}
|
|
123
|
+
-- CVE-2025-26506: HP LaserJet Enterprise PS RCE — affects most modern HP enterprise printers
|
|
124
|
+
table.insert(cves, {
|
|
125
|
+
id="CVE-2025-26506", cvss=9.8, sev="CRITICAL",
|
|
126
|
+
desc="HP LaserJet Enterprise PostScript unauthenticated RCE — malformed PS job triggers code exec",
|
|
127
|
+
xpl="xpl/edb-cve-2025-26506",
|
|
128
|
+
check = true, -- HP + port 9100 is sufficient surface
|
|
129
|
+
})
|
|
130
|
+
-- CVE-2017-2741: HP PageWide PJL path traversal
|
|
131
|
+
table.insert(cves, {
|
|
132
|
+
id="CVE-2017-2741", cvss=9.8, sev="CRITICAL",
|
|
133
|
+
desc="HP PageWide/OfficeJet PJL FSUPLOAD path traversal to arbitrary file read → RCE chain",
|
|
134
|
+
xpl="xpl/edb-cve-2017-2741",
|
|
135
|
+
check = fs_listing ~= nil, -- more likely if filesystem accessible
|
|
136
|
+
})
|
|
137
|
+
-- CVE-2018-5924: Faxploit
|
|
138
|
+
table.insert(cves, {
|
|
139
|
+
id="CVE-2018-5924", cvss=9.8, sev="CRITICAL",
|
|
140
|
+
desc="HP fax stack overflow via malformed TIFF/G3 fax data — unauthenticated RCE on fax-capable models",
|
|
141
|
+
xpl="xpl/edb-cve-2018-5924",
|
|
142
|
+
check = resp:lower():match("fax") ~= nil,
|
|
143
|
+
})
|
|
144
|
+
-- CVE-2023-6018: FutureSmart firmware bypass
|
|
145
|
+
local fw_bypass_check = (fs_ver ~= nil) and (fs_ver >= "5.0")
|
|
146
|
+
table.insert(cves, {
|
|
147
|
+
id="CVE-2023-6018", cvss=9.8, sev="CRITICAL",
|
|
148
|
+
desc="HP FutureSmart firmware auth bypass → arbitrary .RFU firmware upload / implant",
|
|
149
|
+
xpl="xpl/research/research-hp-fw-bypass",
|
|
150
|
+
check = fw_bypass_check or (fw ~= nil),
|
|
151
|
+
})
|
|
152
|
+
-- CVE-2023-1707
|
|
153
|
+
table.insert(cves, {
|
|
154
|
+
id="CVE-2023-1707", cvss=9.1, sev="CRITICAL",
|
|
155
|
+
desc="HP FutureSmart 5.6 scan-to-email data disclosure when IPsec enabled",
|
|
156
|
+
xpl="xpl/research/research-hp-futuresmart-leak",
|
|
157
|
+
check = fs_ver == "5.6" or resp:lower():match("5%.6") ~= nil,
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
local matched = {}
|
|
161
|
+
for _, c in ipairs(cves) do
|
|
162
|
+
if c.check then table.insert(matched, c) end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
if #matched == 0 then
|
|
166
|
+
-- Still include top 2 as possible (HP on 9100 is strong signal)
|
|
167
|
+
matched = { cves[1], cves[2] }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
-- Output CVEs
|
|
171
|
+
local cve_lines = {}
|
|
172
|
+
for _, c in ipairs(matched) do
|
|
173
|
+
table.insert(cve_lines, string.format("[%s] %s (CVSS %.1f) — %s\n module: %s",
|
|
174
|
+
c.sev, c.id, c.cvss, c.desc, c.xpl))
|
|
175
|
+
end
|
|
176
|
+
out["CVEs"] = cve_lines
|
|
177
|
+
|
|
178
|
+
-- Verdict
|
|
179
|
+
local is_vuln = resp:lower():match("futuresmart") or fs_listing ~= nil
|
|
180
|
+
local is_possible = true -- HP + port 9100 is always at least possible
|
|
181
|
+
|
|
182
|
+
if is_vuln then
|
|
183
|
+
out["Verdict"] = "POSSIBLY VULNERABLE — HP " .. (model or "LaserJet") .. " with exposed PJL/FS"
|
|
184
|
+
else
|
|
185
|
+
out["Verdict"] = "POSSIBLY VULNERABLE — HP device detected; version confirmation needed"
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
out["Suggest"] = "printerxpl-forge run --module " .. matched[1].xpl .. " --target " .. host.ip
|
|
189
|
+
out["Full-Scan"] = "pip install printerxpl-forge | printerxpl-forge scan --target " .. host.ip
|
|
190
|
+
|
|
191
|
+
return out
|
|
192
|
+
end
|