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.
Files changed (97) hide show
  1. nse/README.md +204 -0
  2. nse/__init__.py +6 -0
  3. nse/install_nse.py +412 -0
  4. nse/lib/printerxpl.lua +238 -0
  5. nse/scripts/cups-info.nse +74 -0
  6. nse/scripts/cups-queue-info.nse +43 -0
  7. nse/scripts/hp-printers-cve-2022-1026.nse +121 -0
  8. nse/scripts/http-device-mac.nse +107 -0
  9. nse/scripts/http-hp-ilo-info.nse +121 -0
  10. nse/scripts/http-info-xerox-enum.nse +101 -0
  11. nse/scripts/http-vuln-cve2022-1026.nse +158 -0
  12. nse/scripts/lexmark-config.nse +89 -0
  13. nse/scripts/pjl-ready-message.nse +106 -0
  14. nse/scripts/printer-banner.nse +217 -0
  15. nse/scripts/printer-cups-rce.nse +189 -0
  16. nse/scripts/printer-cve-detect.nse +279 -0
  17. nse/scripts/printer-discover.nse +205 -0
  18. nse/scripts/printer-firmware-exposed.nse +219 -0
  19. nse/scripts/printer-hp-pjl.nse +192 -0
  20. nse/scripts/printer-http-ews.nse +293 -0
  21. nse/scripts/printer-ipp-info.nse +235 -0
  22. nse/scripts/printer-lexmark-ipp.nse +203 -0
  23. nse/scripts/printer-passback.nse +204 -0
  24. nse/scripts/printer-pjl-info.nse +146 -0
  25. nse/scripts/printer-printnightmare.nse +211 -0
  26. nse/scripts/printer-snmp-info.nse +176 -0
  27. nse/scripts/printer-vuln-check.nse +256 -0
  28. nse/scripts/snmp-device-mac.nse +93 -0
  29. nse/scripts/snmp-info.nse +146 -0
  30. nse/scripts/snmp-sysdescr.nse +70 -0
  31. printerxpl_forge-6.2.0.dist-info/METADATA +919 -0
  32. printerxpl_forge-6.2.0.dist-info/RECORD +97 -0
  33. printerxpl_forge-6.2.0.dist-info/WHEEL +5 -0
  34. printerxpl_forge-6.2.0.dist-info/entry_points.txt +4 -0
  35. printerxpl_forge-6.2.0.dist-info/licenses/LICENSE +21 -0
  36. printerxpl_forge-6.2.0.dist-info/top_level.txt +4 -0
  37. src/assets/fonts/gunplay.pfa +1671 -0
  38. src/assets/fonts/kshandwrt.pfa +315 -0
  39. src/assets/fonts/laksoner.pfa +2402 -0
  40. src/assets/fonts/paintcans.pfa +9699 -0
  41. src/assets/fonts/stencilod.pfa +4076 -0
  42. src/assets/fonts/takecover.pfa +26138 -0
  43. src/assets/fonts/topsecret.pfa +6652 -0
  44. src/assets/fonts/whoa.pfa +773 -0
  45. src/assets/mibs/HOST-RESOURCES-MIB +1540 -0
  46. src/assets/mibs/Printer-MIB +4389 -0
  47. src/assets/mibs/README.md +9 -0
  48. src/assets/mibs/SNMPv2-MIB +854 -0
  49. src/assets/overlays/hacker.eps +596 -0
  50. src/assets/overlays/smiley.eps +214 -0
  51. src/assets/overlays/smiley2.eps +240 -0
  52. src/core/attack_orchestrator.py +1025 -0
  53. src/core/capabilities.py +323 -0
  54. src/core/destructive_audit.py +430 -0
  55. src/core/discovery.py +488 -0
  56. src/core/osdetect.py +74 -0
  57. src/core/poly_runner.py +579 -0
  58. src/core/printer.py +1426 -0
  59. src/main.py +2134 -0
  60. src/modules/install_printer.py +318 -0
  61. src/modules/login_bruteforce.py +852 -0
  62. src/modules/pcl.py +506 -0
  63. src/modules/pjl.py +3575 -0
  64. src/modules/print_job.py +1290 -0
  65. src/modules/ps.py +1102 -0
  66. src/payloads/__init__.py +98 -0
  67. src/payloads/assets/overlays/notice.eps +9 -0
  68. src/protocols/__init__.py +19 -0
  69. src/protocols/firmware.py +738 -0
  70. src/protocols/ipp.py +216 -0
  71. src/protocols/ipp_attacks.py +609 -0
  72. src/protocols/lpd.py +141 -0
  73. src/protocols/network_map.py +1004 -0
  74. src/protocols/raw.py +173 -0
  75. src/protocols/smb.py +359 -0
  76. src/protocols/ssrf_pivot.py +427 -0
  77. src/protocols/storage.py +587 -0
  78. src/ui/__init__.py +6 -0
  79. src/ui/interactive.py +742 -0
  80. src/ui/spinner.py +112 -0
  81. src/ui/tables.py +132 -0
  82. src/utils/banner_grabber.py +852 -0
  83. src/utils/codebook.py +456 -0
  84. src/utils/config.py +522 -0
  85. src/utils/cve_loader.py +158 -0
  86. src/utils/default_creds.py +134 -0
  87. src/utils/discovery_online.py +1327 -0
  88. src/utils/exploit_manager.py +805 -0
  89. src/utils/fuzzer.py +220 -0
  90. src/utils/helper.py +732 -0
  91. src/utils/local_printers.py +307 -0
  92. src/utils/ml_engine.py +491 -0
  93. src/utils/operators.py +474 -0
  94. src/utils/ports.py +234 -0
  95. src/utils/vuln_scanner.py +823 -0
  96. src/utils/wordlist_loader.py +412 -0
  97. 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