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,203 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-lexmark-ipp — Lexmark-specific IPP vulnerability assessment.
|
|
3
|
+
|
|
4
|
+
Targets Lexmark MFPs and printers via port 631 (IPP) and port 80 (EWS).
|
|
5
|
+
Detects and validates:
|
|
6
|
+
- CVE-2023-50739: IPP heap buffer overflow → RCE (100+ models, ZDI-CAN-22549)
|
|
7
|
+
- CVE-2023-23560: Lexmark Web Services SSRF → RCE (Pwn2Own Toronto 2022)
|
|
8
|
+
- CVE-2023-26067: Post-auth RCE via device configuration API
|
|
9
|
+
- CVE-2023-50733: EWS SSRF — printer as lateral movement pivot
|
|
10
|
+
- CVE-2023-50738: Firmware downgrade bypass
|
|
11
|
+
- CVE-2022-24935: Lexmark authentication bypass
|
|
12
|
+
|
|
13
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
14
|
+
]]
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
-- @usage
|
|
18
|
+
-- nmap -p 631,80 --script printer-lexmark-ipp <target>
|
|
19
|
+
-- @output
|
|
20
|
+
-- PORT STATE SERVICE
|
|
21
|
+
-- 631/tcp open ipp
|
|
22
|
+
-- | printer-lexmark-ipp:
|
|
23
|
+
-- | Vendor : Lexmark
|
|
24
|
+
-- | Model : CX622 (detected from IPP attributes)
|
|
25
|
+
-- | [CRITICAL] CVE-2023-23560 (CVSS 9.0) — Lexmark SSRF-to-RCE
|
|
26
|
+
-- | [HIGH] CVE-2023-50739 (CVSS 8.8) — Lexmark IPP heap BOF (100+ models)
|
|
27
|
+
-- | [MEDIUM] CVE-2023-50733 (CVSS 6.5) — Lexmark EWS SSRF pivot
|
|
28
|
+
-- | Verdict : POSSIBLY VULNERABLE
|
|
29
|
+
-- |_ Suggest : printerxpl-forge run --module xpl/edb-51928 --target <IP>
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
categories = { "vuln", "safe" }
|
|
33
|
+
author = "André Henrique (@mrhenrike) | União Geek"
|
|
34
|
+
license = "Same as Nmap -- See https://nmap.org/book/man-legal.html"
|
|
35
|
+
|
|
36
|
+
local stdnse = require "stdnse"
|
|
37
|
+
local shortport = require "shortport"
|
|
38
|
+
local http = require "http"
|
|
39
|
+
local string = require "string"
|
|
40
|
+
local table = require "table"
|
|
41
|
+
|
|
42
|
+
portrule = shortport.port_or_service({ 631, 80, 443 }, { "ipp", "http", "https" }, "tcp")
|
|
43
|
+
|
|
44
|
+
local LEXMARK_CVES = {
|
|
45
|
+
{ id="CVE-2023-23560", cvss=9.0, sev="CRITICAL",
|
|
46
|
+
desc="Lexmark SSRF-to-RCE via Web Services interface — unauthenticated (Pwn2Own Toronto 2022)",
|
|
47
|
+
xpl="xpl/edb-51928",
|
|
48
|
+
port_check=80 },
|
|
49
|
+
{ id="CVE-2023-26067", cvss=9.1, sev="CRITICAL",
|
|
50
|
+
desc="Lexmark post-auth RCE via device configuration API",
|
|
51
|
+
xpl="xpl/edb-cve-2023-26067",
|
|
52
|
+
port_check=80 },
|
|
53
|
+
{ id="CVE-2023-50739", cvss=8.8, sev="HIGH",
|
|
54
|
+
desc="Lexmark IPP heap buffer overflow → RCE (100+ models, ZDI-CAN-22549)",
|
|
55
|
+
xpl="xpl/edb-cve-2023-50739",
|
|
56
|
+
port_check=631 },
|
|
57
|
+
{ id="CVE-2023-50733", cvss=6.5, sev="MEDIUM",
|
|
58
|
+
desc="Lexmark EWS SSRF — pivot to internal network resources",
|
|
59
|
+
xpl="xpl/edb-cve-2023-50733",
|
|
60
|
+
port_check=80 },
|
|
61
|
+
{ id="CVE-2023-50738", cvss=7.2, sev="HIGH",
|
|
62
|
+
desc="Lexmark firmware downgrade bypass → re-expose historical vulnerabilities",
|
|
63
|
+
xpl="xpl/research/research-lexmark-fw-decrypt",
|
|
64
|
+
port_check=80 },
|
|
65
|
+
{ id="CVE-2022-24935", cvss=8.0, sev="HIGH",
|
|
66
|
+
desc="Lexmark authentication bypass via crafted POST to NPS endpoint",
|
|
67
|
+
xpl="xpl/research/research-lexmark-auth",
|
|
68
|
+
port_check=80 },
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
-- IPP probe to detect Lexmark
|
|
72
|
+
local function ipp_fingerprint(host, port)
|
|
73
|
+
if port.number ~= 631 then return nil end
|
|
74
|
+
local req =
|
|
75
|
+
"\x02\x00\x00\x0b\x00\x00\x00\x01\x01" ..
|
|
76
|
+
"\x47\x00\x12attributes-charset\x00\x05utf-8" ..
|
|
77
|
+
"\x48\x00\x1battributes-natural-language\x00\x05en-us" ..
|
|
78
|
+
"\x45\x00\x0bprinter-uri\x00\x0cipp://0.0.0.0/" ..
|
|
79
|
+
"\x03"
|
|
80
|
+
local resp = http.post(host, 631, "/printers/",
|
|
81
|
+
{ header = { ["Content-Type"] = "application/ipp" }, timeout = 5000 },
|
|
82
|
+
nil, req)
|
|
83
|
+
if resp and resp.body then return resp.body end
|
|
84
|
+
return nil
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
-- HTTP EWS probe to detect Lexmark
|
|
88
|
+
local function http_fingerprint(host, port)
|
|
89
|
+
local paths = { "/", "/cgi-bin/dynamic/", "/lexmark/", "/printer.html" }
|
|
90
|
+
for _, p in ipairs(paths) do
|
|
91
|
+
local r = http.get(host, port.number, p, { timeout = 5000 })
|
|
92
|
+
if r and r.status == 200 and r.body then
|
|
93
|
+
if r.body:lower():match("lexmark") then
|
|
94
|
+
return r.body
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
return nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
-- Test CVE-2023-23560 SSRF surface
|
|
102
|
+
local function check_ssrf_surface(host, port)
|
|
103
|
+
local r = http.get(host, port.number, "/cgi-bin/dynamic/config/webservices.html",
|
|
104
|
+
{ timeout = 4000 })
|
|
105
|
+
if r and (r.status == 200 or r.status == 302) then
|
|
106
|
+
return true
|
|
107
|
+
end
|
|
108
|
+
-- Try alternate
|
|
109
|
+
r = http.get(host, port.number, "/webservices", { timeout = 3000 })
|
|
110
|
+
if r and (r.status == 200 or r.status == 302) then return true end
|
|
111
|
+
return false
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
-- Test CVE-2022-24935 auth bypass surface
|
|
115
|
+
local function check_nps_bypass(host, port)
|
|
116
|
+
local r = http.post(host, port.number, "/cgi-bin/dynamic/nps",
|
|
117
|
+
{ header = { ["Content-Type"] = "application/x-www-form-urlencoded" },
|
|
118
|
+
timeout = 4000 },
|
|
119
|
+
nil, "auth=probe")
|
|
120
|
+
if r and r.status == 200 then
|
|
121
|
+
if r.body and r.body:lower():match("lexmark") then
|
|
122
|
+
return true
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
return false
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
action = function(host, port)
|
|
129
|
+
local out = stdnse.output_table()
|
|
130
|
+
|
|
131
|
+
-- Fingerprint
|
|
132
|
+
local banner = nil
|
|
133
|
+
local is_lexmark = false
|
|
134
|
+
|
|
135
|
+
if port.number == 631 then
|
|
136
|
+
banner = ipp_fingerprint(host, port)
|
|
137
|
+
if banner and banner:lower():match("lexmark") then is_lexmark = true end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if not is_lexmark then
|
|
141
|
+
banner = http_fingerprint(host, port)
|
|
142
|
+
if banner then is_lexmark = true end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
if not is_lexmark then
|
|
146
|
+
return "Lexmark not detected on port " .. port.number ..
|
|
147
|
+
" — use printer-cve-detect for generic CVE matching"
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
out["Vendor"] = "Lexmark"
|
|
151
|
+
|
|
152
|
+
-- Extract model if possible
|
|
153
|
+
if banner then
|
|
154
|
+
local model = banner:match("[Ll]exmark%s+([%w%-]+%s*[%w%-]*)")
|
|
155
|
+
if model then out["Model"] = model:match("^%s*(.-)%s*$"):sub(1, 60) end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
-- Active surface checks
|
|
159
|
+
local ssrf_exposed = check_ssrf_surface(host, port)
|
|
160
|
+
local nps_exposed = check_nps_bypass(host, port)
|
|
161
|
+
|
|
162
|
+
if ssrf_exposed then out["SSRF-Surface"] = "Web Services config accessible — CVE-2023-23560" end
|
|
163
|
+
if nps_exposed then out["NPS-Surface"] = "NPS endpoint responding — CVE-2022-24935" end
|
|
164
|
+
|
|
165
|
+
-- Match CVEs
|
|
166
|
+
local matched = {}
|
|
167
|
+
for _, cve in ipairs(LEXMARK_CVES) do
|
|
168
|
+
local relevant = (not cve.port_check) or (cve.port_check == port.number)
|
|
169
|
+
if not relevant then
|
|
170
|
+
relevant = cve.cvss >= 9.0 -- always include critical
|
|
171
|
+
end
|
|
172
|
+
local triggered = relevant
|
|
173
|
+
if cve.id == "CVE-2023-23560" and not ssrf_exposed then triggered = relevant end
|
|
174
|
+
if cve.id == "CVE-2022-24935" and not nps_exposed then triggered = relevant end
|
|
175
|
+
if triggered then table.insert(matched, cve) end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
-- Sort by CVSS
|
|
179
|
+
table.sort(matched, function(a, b) return a.cvss > b.cvss end)
|
|
180
|
+
|
|
181
|
+
if #matched > 0 then
|
|
182
|
+
local cve_lines = {}
|
|
183
|
+
for _, c in ipairs(matched) do
|
|
184
|
+
table.insert(cve_lines, string.format("[%s] %s (CVSS %.1f) — %s\n module: %s",
|
|
185
|
+
c.sev, c.id, c.cvss, c.desc, c.xpl))
|
|
186
|
+
end
|
|
187
|
+
out["CVEs"] = cve_lines
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
-- Verdict
|
|
191
|
+
local is_vuln = ssrf_exposed or nps_exposed
|
|
192
|
+
if is_vuln then
|
|
193
|
+
out["Verdict"] = "POSSIBLY VULNERABLE — active surface exposure confirmed"
|
|
194
|
+
out["Suggest"] = "printerxpl-forge run --module " .. matched[1].xpl .. " --target " .. host.ip
|
|
195
|
+
else
|
|
196
|
+
out["Verdict"] = "POSSIBLY VULNERABLE — Lexmark confirmed but active surfaces not exposed"
|
|
197
|
+
out["Suggest"] = "printerxpl-forge run --module " .. matched[1].xpl .. " --dry-run --target " .. host.ip
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
out["Full-Scan"] = "pip install printerxpl-forge | printerxpl-forge scan --target " .. host.ip
|
|
201
|
+
|
|
202
|
+
return out
|
|
203
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-passback — LDAP/SMB credential passback attack surface detection.
|
|
3
|
+
|
|
4
|
+
Modern multifunction printers (MFPs) support scan-to-email, scan-to-folder
|
|
5
|
+
(SMB), LDAP authentication, and network scanning. If these features are
|
|
6
|
+
configured, a rogue LDAP/SMB server can capture domain credentials when the
|
|
7
|
+
printer initiates a connection ("passback attack").
|
|
8
|
+
|
|
9
|
+
This script detects:
|
|
10
|
+
- Accessible LDAP configuration pages (scan-to-email / address book LDAP)
|
|
11
|
+
- SMB scan destination configuration pages
|
|
12
|
+
- Network credential test endpoints
|
|
13
|
+
- Active Directory integration surfaces
|
|
14
|
+
|
|
15
|
+
CVE coverage:
|
|
16
|
+
- CVE-2022-23968: Xerox FutureSmart LDAP passback
|
|
17
|
+
- CVE-2022-23969: Xerox FutureSmart SMB passback
|
|
18
|
+
- Generic passback surface detection for HP, Ricoh, Konica, Canon, etc.
|
|
19
|
+
|
|
20
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
21
|
+
]]
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
-- @usage
|
|
25
|
+
-- nmap -p 80,443 --script printer-passback <target>
|
|
26
|
+
-- @output
|
|
27
|
+
-- PORT STATE SERVICE
|
|
28
|
+
-- 80/tcp open http
|
|
29
|
+
-- | printer-passback:
|
|
30
|
+
-- | LDAP-Config : /hp/device/LdapConfiguration.htm (HTTP 200 — accessible)
|
|
31
|
+
-- | SMB-Config : /hp/device/SmbConfiguration.htm (HTTP 200 — accessible)
|
|
32
|
+
-- | [HIGH] CVE-2022-23968 — Xerox LDAP passback → domain credential capture
|
|
33
|
+
-- | [HIGH] CVE-2022-23969 — Xerox SMB passback → domain credential capture
|
|
34
|
+
-- | Verdict : POSSIBLY VULNERABLE — LDAP/SMB config pages accessible
|
|
35
|
+
-- |_ Suggest : printerxpl-forge run --module xpl/research/research-passback-ldap
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
categories = { "vuln", "safe", "discovery" }
|
|
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 http = require "http"
|
|
45
|
+
local string = require "string"
|
|
46
|
+
local table = require "table"
|
|
47
|
+
|
|
48
|
+
portrule = shortport.http
|
|
49
|
+
|
|
50
|
+
-- Passback surface endpoints per vendor
|
|
51
|
+
local PASSBACK_ENDPOINTS = {
|
|
52
|
+
-- HP
|
|
53
|
+
{ path="/hp/device/LdapConfiguration.htm", vendor="HP", proto="LDAP",
|
|
54
|
+
cve="CVE-2022-23968-HP", desc="HP EWS LDAP directory config" },
|
|
55
|
+
{ path="/hp/device/NetworkFolderConfiguration.htm", vendor="HP", proto="SMB",
|
|
56
|
+
cve="CVE-2022-23969-HP", desc="HP EWS SMB scan-to-folder config" },
|
|
57
|
+
{ path="/hp/device/EmailConfiguration.htm", vendor="HP", proto="SMTP",
|
|
58
|
+
cve=nil, desc="HP EWS email config (SMTP credentials)" },
|
|
59
|
+
-- Xerox
|
|
60
|
+
{ path="/cgi-bin/dynamic/userprefs/LdapConfiguration.html", vendor="Xerox", proto="LDAP",
|
|
61
|
+
cve="CVE-2022-23968", desc="Xerox FutureSmart LDAP config (CISA alert)" },
|
|
62
|
+
{ path="/cgi-bin/dynamic/userprefs/SmtpConfiguration.html", vendor="Xerox", proto="SMTP",
|
|
63
|
+
cve=nil, desc="Xerox SMTP config (email credentials)" },
|
|
64
|
+
{ path="/cgi-bin/dynamic/userprefs/SmbConfiguration.html", vendor="Xerox", proto="SMB",
|
|
65
|
+
cve="CVE-2022-23969", desc="Xerox FutureSmart SMB config (CISA alert)" },
|
|
66
|
+
{ path="/web/entry.cgi?id=ldap", vendor="Ricoh", proto="LDAP",
|
|
67
|
+
cve=nil, desc="Ricoh LDAP config" },
|
|
68
|
+
{ path="/web/entry.cgi?id=scan_folder", vendor="Ricoh", proto="SMB",
|
|
69
|
+
cve=nil, desc="Ricoh scan-to-folder SMB config" },
|
|
70
|
+
-- Konica
|
|
71
|
+
{ path="/wcd/ldap.html", vendor="Konica", proto="LDAP",
|
|
72
|
+
cve=nil, desc="Konica bizhub LDAP config" },
|
|
73
|
+
{ path="/wcd/smb_setting.html", vendor="Konica", proto="SMB",
|
|
74
|
+
cve=nil, desc="Konica bizhub SMB scan config" },
|
|
75
|
+
-- Toshiba
|
|
76
|
+
{ path="/TopAccess/Admin/Setting/E-MAIL/LDAPSetting.htm", vendor="Toshiba", proto="LDAP",
|
|
77
|
+
cve=nil, desc="Toshiba TopAccess LDAP auth config" },
|
|
78
|
+
-- Canon
|
|
79
|
+
{ path="/cgi-bin/dynamicconfig", vendor="Canon", proto="LDAP",
|
|
80
|
+
cve=nil, desc="Canon dynamic config (may include LDAP)" },
|
|
81
|
+
-- Brother
|
|
82
|
+
{ path="/network/network.html", vendor="Brother", proto="SMTP",
|
|
83
|
+
cve=nil, desc="Brother network config (SMTP credentials)" },
|
|
84
|
+
-- Kyocera
|
|
85
|
+
{ path="/js/jssrc/model/system/LdapSetting.js", vendor="Kyocera", proto="LDAP",
|
|
86
|
+
cve=nil, desc="Kyocera LDAP setting JS (check for config exposure)" },
|
|
87
|
+
-- Generic
|
|
88
|
+
{ path="/cgi-bin/dynamic/config/ldapconfig.html", vendor="Generic", proto="LDAP",
|
|
89
|
+
cve=nil, desc="Generic LDAP config endpoint" },
|
|
90
|
+
{ path="/ldap", vendor="Generic", proto="LDAP",
|
|
91
|
+
cve=nil, desc="Generic /ldap path" },
|
|
92
|
+
{ path="/scan/smb", vendor="Generic", proto="SMB",
|
|
93
|
+
cve=nil, desc="Generic scan-to-SMB endpoint" },
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
-- Known passback CVEs for detailed output
|
|
97
|
+
local PASSBACK_CVES = {
|
|
98
|
+
["CVE-2022-23968"] = {
|
|
99
|
+
sev="HIGH", cvss=7.5,
|
|
100
|
+
desc="Xerox FutureSmart LDAP passback — printer sends LDAP bind to rogue server capturing domain creds",
|
|
101
|
+
xpl="xpl/research/research-passback-ldap",
|
|
102
|
+
},
|
|
103
|
+
["CVE-2022-23969"] = {
|
|
104
|
+
sev="HIGH", cvss=7.5,
|
|
105
|
+
desc="Xerox FutureSmart SMB passback — printer authenticates to rogue SMB share capturing NTLM hashes",
|
|
106
|
+
xpl="xpl/research/research-passback-smb",
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
action = function(host, port)
|
|
111
|
+
local out = stdnse.output_table()
|
|
112
|
+
|
|
113
|
+
-- Quick root check
|
|
114
|
+
local root = http.get(host, port.number, "/", { timeout = 4000 })
|
|
115
|
+
if not root then return "HTTP not responding" end
|
|
116
|
+
|
|
117
|
+
local accessible = {}
|
|
118
|
+
local cves_triggered = {}
|
|
119
|
+
local seen = {}
|
|
120
|
+
local protos_found = {}
|
|
121
|
+
|
|
122
|
+
for _, ep in ipairs(PASSBACK_ENDPOINTS) do
|
|
123
|
+
local resp = http.get(host, port.number, ep.path,
|
|
124
|
+
{ timeout = 4000, redirect_ok = false })
|
|
125
|
+
if resp and (resp.status == 200 or resp.status == 302) then
|
|
126
|
+
local entry = string.format("%s (%s, HTTP %d) [%s — %s]",
|
|
127
|
+
ep.path, ep.proto, resp.status, ep.vendor, ep.desc)
|
|
128
|
+
table.insert(accessible, entry)
|
|
129
|
+
protos_found[ep.proto] = true
|
|
130
|
+
|
|
131
|
+
if ep.cve and not seen[ep.cve] then
|
|
132
|
+
seen[ep.cve] = true
|
|
133
|
+
local cve_detail = PASSBACK_CVES[ep.cve]
|
|
134
|
+
if cve_detail then
|
|
135
|
+
table.insert(cves_triggered, {
|
|
136
|
+
id = ep.cve,
|
|
137
|
+
sev = cve_detail.sev,
|
|
138
|
+
cvss = cve_detail.cvss,
|
|
139
|
+
desc = cve_detail.desc,
|
|
140
|
+
xpl = cve_detail.xpl,
|
|
141
|
+
})
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
if #accessible == 0 then
|
|
148
|
+
return "No LDAP/SMB passback surfaces detected (admin pages may require auth or non-standard paths)"
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
-- Protocol summary
|
|
152
|
+
local proto_list = {}
|
|
153
|
+
for p, _ in pairs(protos_found) do table.insert(proto_list, p) end
|
|
154
|
+
out["Protocols"] = table.concat(proto_list, ", ")
|
|
155
|
+
|
|
156
|
+
out["Config-Pages-Found"] = accessible
|
|
157
|
+
|
|
158
|
+
-- CVE output
|
|
159
|
+
if #cves_triggered > 0 then
|
|
160
|
+
local cve_lines = {}
|
|
161
|
+
for _, c in ipairs(cves_triggered) do
|
|
162
|
+
table.insert(cve_lines, string.format("[%s] %s (CVSS %.1f) — %s",
|
|
163
|
+
c.sev, c.id, c.cvss, c.desc))
|
|
164
|
+
end
|
|
165
|
+
out["CVEs"] = cve_lines
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
-- Passback explanation
|
|
169
|
+
local has_ldap = protos_found["LDAP"]
|
|
170
|
+
local has_smb = protos_found["SMB"]
|
|
171
|
+
|
|
172
|
+
local attack_desc = {}
|
|
173
|
+
if has_ldap then
|
|
174
|
+
table.insert(attack_desc,
|
|
175
|
+
"LDAP passback: redirect printer LDAP queries to rogue server → capture domain credentials")
|
|
176
|
+
end
|
|
177
|
+
if has_smb then
|
|
178
|
+
table.insert(attack_desc,
|
|
179
|
+
"SMB passback: redirect printer SMB auth to Responder/rogue share → capture NTLM hashes")
|
|
180
|
+
end
|
|
181
|
+
if #attack_desc > 0 then
|
|
182
|
+
out["Attack-Vector"] = attack_desc
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
-- Verdict
|
|
186
|
+
out["Verdict"] = "POSSIBLY VULNERABLE — " ..
|
|
187
|
+
#accessible .. " config page(s) accessible; passback attack feasible if admin credentials known"
|
|
188
|
+
|
|
189
|
+
if #cves_triggered > 0 then
|
|
190
|
+
out["Suggest"] = "printerxpl-forge run --module " .. cves_triggered[1].xpl .. " --target " .. host.ip
|
|
191
|
+
else
|
|
192
|
+
out["Suggest"] = "printerxpl-forge run --module xpl/research/research-passback-ldap --target " .. host.ip
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
out["Manual-Steps"] =
|
|
196
|
+
"1. Set up rogue LDAP/SMB server (Responder or custom) " ..
|
|
197
|
+
"2. Modify printer LDAP/SMB server IP to attacker IP " ..
|
|
198
|
+
"3. Trigger auth test from printer web UI " ..
|
|
199
|
+
"4. Capture credentials on rogue server"
|
|
200
|
+
|
|
201
|
+
out["Full-Scan"] = "pip install printerxpl-forge | printerxpl-forge scan --target " .. host.ip
|
|
202
|
+
|
|
203
|
+
return out
|
|
204
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-pjl-info — Deep PJL enumeration via port 9100.
|
|
3
|
+
|
|
4
|
+
Sends multiple @PJL INFO commands to extract: device identity (ID, MODEL),
|
|
5
|
+
environment variables (TIMEOUT, RESOLUTION, COPIES), filesystem listing (FSLIST),
|
|
6
|
+
status (JOB status, READY state), and network configuration.
|
|
7
|
+
|
|
8
|
+
Also tests for PJL password protection — unprotected devices are flagged.
|
|
9
|
+
|
|
10
|
+
References:
|
|
11
|
+
https://github.com/mrhenrike/PrinterXPL-Forge
|
|
12
|
+
https://h20195.www2.hp.com/v2/GetDocument.aspx?docname=bpl13208 (HP PJL Tech Ref)
|
|
13
|
+
|
|
14
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
15
|
+
]]
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
-- @usage
|
|
19
|
+
-- nmap -p 9100 --script printer-pjl-info <target>
|
|
20
|
+
-- @output
|
|
21
|
+
-- PORT STATE SERVICE
|
|
22
|
+
-- 9100/tcp open jetdirect
|
|
23
|
+
-- | printer-pjl-info:
|
|
24
|
+
-- | ID : HP LASERJET M606
|
|
25
|
+
-- | Model : HP LaserJet Enterprise M606dn
|
|
26
|
+
-- | Firmware : FY5L.04.A.00.00
|
|
27
|
+
-- | Serial : JPGD12345
|
|
28
|
+
-- | Resolution : 1200
|
|
29
|
+
-- | Timeout : 15
|
|
30
|
+
-- | Filesystem : 0:\ [HDD 40GB]
|
|
31
|
+
-- | PJL-Password : NOT SET (unprotected — PJL job injection possible)
|
|
32
|
+
-- |_ Verdict : POSSIBLY VULNERABLE (unrestricted PJL access)
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
categories = { "discovery", "safe", "vuln" }
|
|
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 string = require "string"
|
|
43
|
+
|
|
44
|
+
portrule = shortport.port_or_service(9100, "jetdirect", "tcp")
|
|
45
|
+
|
|
46
|
+
local UEL = "\x1b%-12345X"
|
|
47
|
+
|
|
48
|
+
local PJL_QUERIES = {
|
|
49
|
+
{ cmd = "@PJL INFO ID\r\n", key = "ID" },
|
|
50
|
+
{ cmd = "@PJL INFO CONFIG\r\n", key = "Config" },
|
|
51
|
+
{ cmd = "@PJL INFO VARIABLES\r\n", key = "Variables" },
|
|
52
|
+
{ cmd = "@PJL INFO FILESYS\r\n", key = "Filesystem" },
|
|
53
|
+
{ cmd = "@PJL INFO STATUS\r\n", key = "Status" },
|
|
54
|
+
{ cmd = "@PJL INFO PRODINFO\r\n", key = "ProductInfo" },
|
|
55
|
+
{ cmd = "@PJL INFO MEMORY\r\n", key = "Memory" },
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
local function pjl_send(host, port, cmds)
|
|
59
|
+
local payload = UEL .. "@PJL\r\n"
|
|
60
|
+
for _, q in ipairs(cmds) do
|
|
61
|
+
payload = payload .. q.cmd
|
|
62
|
+
end
|
|
63
|
+
payload = payload .. UEL
|
|
64
|
+
|
|
65
|
+
local status, resp = comm.exchange(host, port, payload,
|
|
66
|
+
{ timeout = 7000, bytes = 16384 })
|
|
67
|
+
if status then return resp end
|
|
68
|
+
return nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
local function extract_field(text, pattern)
|
|
72
|
+
local val = text:match(pattern)
|
|
73
|
+
if val then return val:match("^%s*(.-)%s*$") end
|
|
74
|
+
return nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
action = function(host, port)
|
|
78
|
+
local resp = pjl_send(host, port, PJL_QUERIES)
|
|
79
|
+
if not resp then
|
|
80
|
+
return "No PJL response — port may be closed or require credentials"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
local out = stdnse.output_table()
|
|
84
|
+
|
|
85
|
+
-- Basic identity
|
|
86
|
+
local id_val = extract_field(resp, "@PJL INFO ID%s*\r?\n(.-)%r?\n%s*@PJL")
|
|
87
|
+
or extract_field(resp, 'ID%s*=%s*"([^"]+)"')
|
|
88
|
+
or extract_field(resp, "ID%s*\r?\n%s*([^\r\n]+)")
|
|
89
|
+
if id_val then out["ID"] = id_val end
|
|
90
|
+
|
|
91
|
+
-- Firmware
|
|
92
|
+
local fw = extract_field(resp, "DATECODE%s*=%s*([%w%.%-]+)")
|
|
93
|
+
or extract_field(resp, "[Ff]irmware[Vv]ersion%s*=%s*([%w%.%-]+)")
|
|
94
|
+
if fw then out["Firmware"] = fw end
|
|
95
|
+
|
|
96
|
+
-- Serial
|
|
97
|
+
local sn = extract_field(resp, "SERIALNUMBER%s*=%s*([%w%-]+)")
|
|
98
|
+
or extract_field(resp, "SERIAL%s*=%s*([%w%-]+)")
|
|
99
|
+
if sn then out["Serial"] = sn end
|
|
100
|
+
|
|
101
|
+
-- Resolution
|
|
102
|
+
local res = extract_field(resp, "RESOLUTION%s*=%s*(%d+)")
|
|
103
|
+
if res then out["Resolution-DPI"] = res end
|
|
104
|
+
|
|
105
|
+
-- Memory
|
|
106
|
+
local mem = extract_field(resp, "TOTALMEMORY%s*=%s*(%d+)")
|
|
107
|
+
if mem then out["Memory-KB"] = mem end
|
|
108
|
+
|
|
109
|
+
-- Filesystem
|
|
110
|
+
local fs = extract_field(resp, "VOLUME%s*=%s*([^\r\n]+)")
|
|
111
|
+
or extract_field(resp, "NAME%s*=%s*\"([^\"]+)\"")
|
|
112
|
+
if fs then out["Filesystem"] = fs end
|
|
113
|
+
|
|
114
|
+
-- Job count / status
|
|
115
|
+
local jobs = extract_field(resp, "JOBNAME%s*=%s*\"([^\"]+)\"")
|
|
116
|
+
if jobs then out["Last-Job"] = jobs end
|
|
117
|
+
|
|
118
|
+
-- PJL Password check: try @PJL DEFAULT PASSWORD test
|
|
119
|
+
local pwd_check = UEL .. "@PJL\r\n@PJL DEFAULT PASSWORD=0\r\n" .. UEL
|
|
120
|
+
local ps, pr = comm.exchange(host, port, pwd_check, { timeout = 3000, bytes = 512 })
|
|
121
|
+
local pwd_set = false
|
|
122
|
+
if ps and pr and (pr:match("PASSWORD") or pr:match("ACCESS")) then
|
|
123
|
+
pwd_set = true
|
|
124
|
+
out["PJL-Password"] = "SET (access control enabled)"
|
|
125
|
+
else
|
|
126
|
+
out["PJL-Password"] = "NOT SET — PJL unrestricted (job injection / NVRAM access possible)"
|
|
127
|
+
pwd_set = false
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
-- Verdict
|
|
131
|
+
local is_vuln = not pwd_set
|
|
132
|
+
local is_possible = resp and resp:match("READY") ~= nil
|
|
133
|
+
|
|
134
|
+
if is_vuln then
|
|
135
|
+
out["Verdict"] = "POSSIBLY VULNERABLE — unrestricted PJL: use printer-vuln-check for deep scan"
|
|
136
|
+
out["Suggest"] = "printerxpl-forge run --module xpl/edb-cve-2017-2741 --target " .. host.ip
|
|
137
|
+
elseif is_possible then
|
|
138
|
+
out["Verdict"] = "NOT VULNERABLE to unrestricted PJL (password set)"
|
|
139
|
+
else
|
|
140
|
+
out["Verdict"] = "UNKNOWN — partial PJL response"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
out["Full-Exploitation"] = "pip install printerxpl-forge | printerxpl-forge run --target " .. host.ip
|
|
144
|
+
|
|
145
|
+
return out
|
|
146
|
+
end
|