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,217 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-banner — Multi-protocol printer banner grabber and vendor fingerprinting.
|
|
3
|
+
|
|
4
|
+
Probes ports 9100 (PJL/RAW), 631 (IPP), 80/443 (HTTP EWS), 515 (LPD), and 23 (Telnet)
|
|
5
|
+
to collect the printer's manufacturer, model, firmware version, and serial number.
|
|
6
|
+
Fingerprint results are cross-referenced against the PrinterXPL-Forge CVE database to
|
|
7
|
+
list potentially exploitable vulnerabilities and suggest specific exploit modules.
|
|
8
|
+
|
|
9
|
+
References:
|
|
10
|
+
https://github.com/mrhenrike/PrinterXPL-Forge
|
|
11
|
+
https://github.com/mrhenrike/PrinterXPL-Forge/wiki/NSE
|
|
12
|
+
|
|
13
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
14
|
+
]]
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
-- @usage
|
|
18
|
+
-- nmap -sV -p 9100,631,80,443,515,23 --script printer-banner <target>
|
|
19
|
+
-- @output
|
|
20
|
+
-- PORT STATE SERVICE
|
|
21
|
+
-- 9100/tcp open jetdirect
|
|
22
|
+
-- | printer-banner:
|
|
23
|
+
-- | Vendor : HP
|
|
24
|
+
-- | Family : LaserJet/OfficeJet/PageWide
|
|
25
|
+
-- | Banner : HP LaserJet M606 / FutureSmart 5.6
|
|
26
|
+
-- | Firmware : FY5L.04.A.00.00
|
|
27
|
+
-- | Serial : JPGD12345
|
|
28
|
+
-- | CVEs : 3 matching (run printer-cve-detect for details)
|
|
29
|
+
-- |_ Suggest : printerxpl-forge run --module xpl/edb-cve-2025-26506 --target <IP>
|
|
30
|
+
--
|
|
31
|
+
-- @xmloutput
|
|
32
|
+
-- <table key="banner">...</table>
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
categories = { "discovery", "safe", "default" }
|
|
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 nmap = require "nmap"
|
|
41
|
+
local shortport = require "shortport"
|
|
42
|
+
local comm = require "comm"
|
|
43
|
+
local http = require "http"
|
|
44
|
+
local string = require "string"
|
|
45
|
+
local table = require "table"
|
|
46
|
+
|
|
47
|
+
-- Load shared library (must be placed in nmap/nselib/ or nse/lib/ during install)
|
|
48
|
+
local ok, printerxpl = pcall(require, "printerxpl")
|
|
49
|
+
if not ok then
|
|
50
|
+
printerxpl = nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
-- ── Port rule: fire on any of the typical printer ports ──────────────────────
|
|
54
|
+
portrule = shortport.port_or_service(
|
|
55
|
+
{ 9100, 631, 80, 443, 515, 23 },
|
|
56
|
+
{ "jetdirect", "ipp", "http", "https", "printer", "telnet" },
|
|
57
|
+
"tcp"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
-- ── PJL probe: send @PJL INFO ID and @PJL INFO CONFIG ────────────────────────
|
|
61
|
+
local function pjl_probe(host, port)
|
|
62
|
+
local uel = "\x1b%-12345X"
|
|
63
|
+
local cmd = uel .. "@PJL\r\n@PJL INFO ID\r\n@PJL INFO CONFIG\r\n@PJL INFO STATUS\r\n" .. uel
|
|
64
|
+
local status, result = comm.exchange(host, port, cmd, { timeout = 5000, bytes = 4096 })
|
|
65
|
+
if status then return result end
|
|
66
|
+
return nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
-- ── IPP probe: Get-Printer-Attributes (minimal) ───────────────────────────────
|
|
70
|
+
local function ipp_probe(host, port)
|
|
71
|
+
local resp = http.post(
|
|
72
|
+
host, port, "/ipp/print",
|
|
73
|
+
{ header = { ["Content-Type"] = "application/ipp" } },
|
|
74
|
+
nil,
|
|
75
|
+
-- IPP Get-Printer-Attributes request (version 1.1)
|
|
76
|
+
"\x01\x01\x00\x0b\x00\x00\x00\x01" ..
|
|
77
|
+
"\x01" ..
|
|
78
|
+
"\x47\x00\x12attributes-charset\x00\x05utf-8" ..
|
|
79
|
+
"\x48\x00\x1battributes-natural-language\x00\x05en-us" ..
|
|
80
|
+
"\x03"
|
|
81
|
+
)
|
|
82
|
+
if resp and resp.status then
|
|
83
|
+
return resp.body or ""
|
|
84
|
+
end
|
|
85
|
+
return nil
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
-- ── HTTP EWS probe ───────────────────────────────────────────────────────────
|
|
89
|
+
local function http_probe(host, port)
|
|
90
|
+
local paths = { "/", "/hp/device/", "/cgi-bin/dynamic/", "/TopAccess/", "/web/index.html" }
|
|
91
|
+
for _, path in ipairs(paths) do
|
|
92
|
+
local resp = http.get(host, port, path, { timeout = 5000 })
|
|
93
|
+
if resp and resp.status == 200 and resp.body and #resp.body > 50 then
|
|
94
|
+
return resp.body
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
return nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
-- ── LPD probe: send LPD short-status request ─────────────────────────────────
|
|
101
|
+
local function lpd_probe(host, port)
|
|
102
|
+
local status, result = comm.exchange(host, port, "\x01\n", { timeout = 3000, bytes = 512 })
|
|
103
|
+
if status then return result end
|
|
104
|
+
return nil
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
-- ── Extract fields from combined banner text ──────────────────────────────────
|
|
108
|
+
local function extract_info(text)
|
|
109
|
+
local info = {}
|
|
110
|
+
-- Firmware / version
|
|
111
|
+
local fw = text:match("[Ff]irmware[Vv]ersion[^:]*:%s*([%w%.%-]+)")
|
|
112
|
+
or text:match("DATECODE=%s*([%w%.]+)")
|
|
113
|
+
or text:match("[Vv]ersion[^:]*:%s*([%w%.%-]+)")
|
|
114
|
+
if fw then info.firmware = fw end
|
|
115
|
+
-- Serial
|
|
116
|
+
local sn = text:match("[Ss]erial[Nn]umber[^:]*:%s*([%w%-]+)")
|
|
117
|
+
or text:match("SERIALNUMBER=%s*([%w%-]+)")
|
|
118
|
+
if sn then info.serial = sn end
|
|
119
|
+
-- Model (PJL style: DESCRIPTION="HP LaserJet P3015")
|
|
120
|
+
local model = text:match('[Dd][Ee][Ss][Cc][Rr][Ii][Pp][Tt][Ii][Oo][Nn]="([^"]+)"')
|
|
121
|
+
or text:match("[Mm]odel[^:]*:%s*([^\r\n]+)")
|
|
122
|
+
or text:match("[Pp]roduct:%s*([^\r\n]+)")
|
|
123
|
+
if model then info.model = model:match("^%s*(.-)%s*$") end
|
|
124
|
+
-- IP address (from CUPS / IPP response)
|
|
125
|
+
local ip = text:match("printer%-name%s*=%s*([%w%.%-]+)")
|
|
126
|
+
if ip then info.printer_name = ip end
|
|
127
|
+
return info
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
-- ── Main action ──────────────────────────────────────────────────────────────
|
|
131
|
+
action = function(host, port)
|
|
132
|
+
local portnum = port.number
|
|
133
|
+
local banner_text = ""
|
|
134
|
+
local source = ""
|
|
135
|
+
|
|
136
|
+
if portnum == 9100 then
|
|
137
|
+
local r = pjl_probe(host, port)
|
|
138
|
+
if r then banner_text = r; source = "PJL" end
|
|
139
|
+
elseif portnum == 631 then
|
|
140
|
+
local r = ipp_probe(host, port)
|
|
141
|
+
if r then banner_text = r; source = "IPP" end
|
|
142
|
+
elseif portnum == 80 or portnum == 443 then
|
|
143
|
+
local r = http_probe(host, port)
|
|
144
|
+
if r then banner_text = r; source = "HTTP-EWS" end
|
|
145
|
+
elseif portnum == 515 then
|
|
146
|
+
local r = lpd_probe(host, port)
|
|
147
|
+
if r then banner_text = r; source = "LPD" end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if banner_text == "" then
|
|
151
|
+
return nil
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
-- Vendor detection
|
|
155
|
+
local vendor, family = nil, nil
|
|
156
|
+
if printerxpl then
|
|
157
|
+
vendor, family = printerxpl.detect_vendor(banner_text)
|
|
158
|
+
else
|
|
159
|
+
-- Fallback inline detection
|
|
160
|
+
local b = banner_text:lower()
|
|
161
|
+
if b:match("hp") or b:match("hewlett") or b:match("laserjet") then
|
|
162
|
+
vendor, family = "HP", "LaserJet/OfficeJet"
|
|
163
|
+
elseif b:match("canon") then vendor, family = "Canon", "imageCLASS/imageRUNNER"
|
|
164
|
+
elseif b:match("lexmark") then vendor, family = "Lexmark", "MX/CX series"
|
|
165
|
+
elseif b:match("ricoh") or b:match("aficio") then vendor, family = "Ricoh", "Aficio/MP"
|
|
166
|
+
elseif b:match("xerox") then vendor, family = "Xerox", "WorkCentre/VersaLink"
|
|
167
|
+
elseif b:match("brother") then vendor, family = "Brother", "MFC/DCP"
|
|
168
|
+
elseif b:match("epson") then vendor, family = "Epson", "WorkForce"
|
|
169
|
+
elseif b:match("konica") or b:match("bizhub") then vendor, family = "Konica Minolta", "bizhub"
|
|
170
|
+
elseif b:match("kyocera") then vendor, family = "Kyocera", "ECOSYS"
|
|
171
|
+
elseif b:match("sharp") then vendor, family = "Sharp", "MX"
|
|
172
|
+
elseif b:match("toshiba") then vendor, family = "Toshiba", "e-STUDIO"
|
|
173
|
+
elseif b:match("samsung") then vendor, family = "Samsung", "CLX/SCX"
|
|
174
|
+
elseif b:match("cups") then vendor, family = "Linux/CUPS", "CUPS scheduler"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
local info = extract_info(banner_text)
|
|
179
|
+
|
|
180
|
+
-- Collect CVE count hint
|
|
181
|
+
local cve_count = 0
|
|
182
|
+
local best_xpl = nil
|
|
183
|
+
if printerxpl and vendor then
|
|
184
|
+
local cves = printerxpl.match_cves(vendor)
|
|
185
|
+
cve_count = #cves
|
|
186
|
+
if cve_count > 0 then
|
|
187
|
+
best_xpl = cves[1].xpl
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
-- Build output
|
|
192
|
+
local out = stdnse.output_table()
|
|
193
|
+
out["Protocol"] = source
|
|
194
|
+
if vendor then
|
|
195
|
+
out["Vendor"] = vendor
|
|
196
|
+
out["Family"] = family or "Unknown"
|
|
197
|
+
end
|
|
198
|
+
if info.model then out["Model"] = info.model end
|
|
199
|
+
if info.firmware then out["Firmware"] = info.firmware end
|
|
200
|
+
if info.serial then out["Serial"] = info.serial end
|
|
201
|
+
|
|
202
|
+
-- Banner snippet (first 200 printable chars)
|
|
203
|
+
local snippet = banner_text:gsub("[%c]", " "):sub(1, 200):match("^%s*(.-)%s*$")
|
|
204
|
+
if #snippet > 0 then out["Banner"] = snippet end
|
|
205
|
+
|
|
206
|
+
if cve_count > 0 then
|
|
207
|
+
out["CVE-Count"] = string.format("%d matching CVEs (run printer-cve-detect for details)", cve_count)
|
|
208
|
+
out["Suggest"] = string.format(
|
|
209
|
+
"printerxpl-forge run --module %s --target %s", best_xpl, host.ip)
|
|
210
|
+
elseif vendor then
|
|
211
|
+
out["CVE-Count"] = "0 matching (vendor known but no specific CVE match on this port)"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
out["Full-Scan"] = "pip install printerxpl-forge | printerxpl-forge scan --target " .. host.ip
|
|
215
|
+
|
|
216
|
+
return out
|
|
217
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-cups-rce — CUPS vulnerability detection (CVE-2024-47176 / CVE-2026-34980).
|
|
3
|
+
|
|
4
|
+
Specifically targets CUPS schedulers to detect:
|
|
5
|
+
- CVE-2024-47176: cups-browsed listens on UDP 631 and accepts any browsing packet
|
|
6
|
+
that can redirect to an attacker-controlled IPP server → unauthenticated RCE
|
|
7
|
+
- CVE-2026-34980: CUPS 2.4.16 accepts newline characters in print job-name option,
|
|
8
|
+
allowing injection of PPD: FoomaticRIPCommandLine directives → unauthenticated RCE
|
|
9
|
+
|
|
10
|
+
Detection is passive + version-based; no actual exploit payload is sent.
|
|
11
|
+
|
|
12
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
13
|
+
]]
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
-- @usage
|
|
17
|
+
-- nmap -sU -sT -p U:631,T:631 --script printer-cups-rce <target>
|
|
18
|
+
-- nmap -p 631 --script printer-cups-rce <target>
|
|
19
|
+
-- @output
|
|
20
|
+
-- PORT STATE SERVICE
|
|
21
|
+
-- 631/tcp open ipp
|
|
22
|
+
-- | printer-cups-rce:
|
|
23
|
+
-- | CUPS-Version : 2.4.16
|
|
24
|
+
-- | cups-browsed : DETECTED (listening on UDP 631)
|
|
25
|
+
-- | [CRITICAL] CVE-2026-34980 — CUPS 2.4.16 unauthenticated RCE via PPD injection
|
|
26
|
+
-- | [CRITICAL] CVE-2024-47176 — cups-browsed unauthenticated RCE chain
|
|
27
|
+
-- | Verdict : VULNERABLE
|
|
28
|
+
-- |_ Suggest : printerxpl-forge run --module xpl/research/research-cups-chain-2026
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
categories = { "vuln", "safe" }
|
|
32
|
+
author = "André Henrique (@mrhenrike) | União Geek"
|
|
33
|
+
license = "Same as Nmap -- See https://nmap.org/book/man-legal.html"
|
|
34
|
+
|
|
35
|
+
local stdnse = require "stdnse"
|
|
36
|
+
local shortport = require "shortport"
|
|
37
|
+
local http = require "http"
|
|
38
|
+
local comm = require "comm"
|
|
39
|
+
local string = require "string"
|
|
40
|
+
local table = require "table"
|
|
41
|
+
|
|
42
|
+
portrule = shortport.port_or_service(631, { "ipp", "cups" }, { "tcp", "udp" })
|
|
43
|
+
|
|
44
|
+
local V = { VULN = "VULNERABLE", POSS = "POSSIBLY VULNERABLE", NOT = "NOT VULNERABLE" }
|
|
45
|
+
|
|
46
|
+
-- IPP Get-Printer-Attributes minimal request
|
|
47
|
+
local function ipp_probe(host, port)
|
|
48
|
+
local printer_uri = string.format("ipp://%s:%d/", host.ip, port.number)
|
|
49
|
+
local req =
|
|
50
|
+
"\x02\x00\x00\x0b\x00\x00\x00\x01\x01" ..
|
|
51
|
+
"\x47\x00\x12attributes-charset\x00\x05utf-8" ..
|
|
52
|
+
"\x48\x00\x1battributes-natural-language\x00\x05en-us" ..
|
|
53
|
+
"\x45\x00\x0bprinter-uri" ..
|
|
54
|
+
string.char(0x00, #printer_uri >> 8, #printer_uri & 0xff) .. printer_uri ..
|
|
55
|
+
"\x03"
|
|
56
|
+
local resp = http.post(
|
|
57
|
+
host, port.number, "/printers/",
|
|
58
|
+
{ header = { ["Content-Type"] = "application/ipp" }, timeout = 6000 },
|
|
59
|
+
nil, req
|
|
60
|
+
)
|
|
61
|
+
if resp and resp.body then return resp.body end
|
|
62
|
+
return nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
-- cups-browsed UDP probe: send a CUPS browse packet to port 631 UDP
|
|
66
|
+
local function probe_cups_browsed(host, port)
|
|
67
|
+
-- Legacy CUPS browse packet (type=3 = printer, uri format)
|
|
68
|
+
local browse_pkt = "3 5 http://nmap-probe:631/printers/ ipp://nmap-probe/printers/default\n"
|
|
69
|
+
local ok, resp = comm.exchange(host, { number = 631, protocol = "udp" },
|
|
70
|
+
browse_pkt, { timeout = 3000, bytes = 256 })
|
|
71
|
+
-- If port sends any response or error, cups-browsed likely running
|
|
72
|
+
return ok
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
-- HTTP check on CUPS web interface
|
|
76
|
+
local function get_cups_web(host, port)
|
|
77
|
+
local r = http.get(host, port.number, "/", { timeout = 4000 })
|
|
78
|
+
if r and r.body then return r.body end
|
|
79
|
+
return nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
action = function(host, port)
|
|
83
|
+
local out = stdnse.output_table()
|
|
84
|
+
|
|
85
|
+
-- 1. IPP probe
|
|
86
|
+
local ipp_body = ipp_probe(host, port)
|
|
87
|
+
|
|
88
|
+
-- 2. HTTP CUPS web interface
|
|
89
|
+
local web_body = get_cups_web(host, port)
|
|
90
|
+
|
|
91
|
+
-- Combine for analysis
|
|
92
|
+
local combined = (ipp_body or "") .. (web_body or "")
|
|
93
|
+
|
|
94
|
+
if #combined < 10 then
|
|
95
|
+
return "No IPP/CUPS response on port " .. port.number
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
-- Check if CUPS
|
|
99
|
+
local is_cups = combined:lower():match("cups")
|
|
100
|
+
if not is_cups then
|
|
101
|
+
return "CUPS not detected on port " .. port.number .. " — try printer-ipp-info for generic IPP"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
out["Service"] = "CUPS"
|
|
105
|
+
|
|
106
|
+
-- Extract version
|
|
107
|
+
local version = combined:match("CUPS%s+([%d%.]+)")
|
|
108
|
+
or combined:match("cups%-(%d+%.%d+%.%d+)")
|
|
109
|
+
or combined:match("<version>([%d%.]+)</version>")
|
|
110
|
+
if version then out["CUPS-Version"] = version end
|
|
111
|
+
|
|
112
|
+
-- cups-browsed probe
|
|
113
|
+
local browsed = probe_cups_browsed(host, port)
|
|
114
|
+
out["cups-browsed"] = browsed and "DETECTED (UDP 631 responding)" or "Not detected / filtered"
|
|
115
|
+
|
|
116
|
+
-- CVE determination
|
|
117
|
+
local cves = {}
|
|
118
|
+
|
|
119
|
+
if version == "2.4.16" then
|
|
120
|
+
table.insert(cves, {
|
|
121
|
+
id = "CVE-2026-34980",
|
|
122
|
+
sev = "CRITICAL",
|
|
123
|
+
cvss = 9.1,
|
|
124
|
+
desc = "CUPS 2.4.16 unauthenticated RCE via job-name newline → PPD FoomaticRIPCommandLine injection",
|
|
125
|
+
xpl = "xpl/research/research-cups-chain-2026",
|
|
126
|
+
})
|
|
127
|
+
table.insert(cves, {
|
|
128
|
+
id = "CVE-2026-34990",
|
|
129
|
+
sev = "HIGH",
|
|
130
|
+
cvss = 7.8,
|
|
131
|
+
desc = "CUPS 2.4.16 LPE to root via local printer race condition (chain with CVE-2026-34980)",
|
|
132
|
+
xpl = "xpl/research/research-cups-root-2026",
|
|
133
|
+
})
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
if browsed then
|
|
137
|
+
table.insert(cves, {
|
|
138
|
+
id = "CVE-2024-47176",
|
|
139
|
+
sev = "CRITICAL",
|
|
140
|
+
cvss = 9.9,
|
|
141
|
+
desc = "cups-browsed accepts any UDP browse packet → redirects print jobs to attacker IPP server → RCE chain",
|
|
142
|
+
xpl = "xpl/edb-cve-2024-47176",
|
|
143
|
+
})
|
|
144
|
+
table.insert(cves, {
|
|
145
|
+
id = "CVE-2024-47076",
|
|
146
|
+
sev = "HIGH",
|
|
147
|
+
cvss = 8.6,
|
|
148
|
+
desc = "libcupsfilters cfGetPrinterAttributes5 — no IPP attribute validation → data poisoning",
|
|
149
|
+
xpl = "xpl/edb-cve-2024-47176",
|
|
150
|
+
})
|
|
151
|
+
table.insert(cves, {
|
|
152
|
+
id = "CVE-2024-47175",
|
|
153
|
+
sev = "HIGH",
|
|
154
|
+
cvss = 8.6,
|
|
155
|
+
desc = "libppd ppdCreatePPDFromIPP2 — unsanitized IPP attrs written to PPD → code injection",
|
|
156
|
+
xpl = "xpl/edb-cve-2024-47176",
|
|
157
|
+
})
|
|
158
|
+
table.insert(cves, {
|
|
159
|
+
id = "CVE-2024-47177",
|
|
160
|
+
sev = "CRITICAL",
|
|
161
|
+
cvss = 9.9,
|
|
162
|
+
desc = "cups-filters FoomaticRIPCommandLine arbitrary command execution",
|
|
163
|
+
xpl = "xpl/edb-cve-2024-47176",
|
|
164
|
+
})
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
if #cves > 0 then
|
|
168
|
+
local lines = {}
|
|
169
|
+
for _, c in ipairs(cves) do
|
|
170
|
+
table.insert(lines, string.format("[%s] %s (CVSS %.1f) — %s",
|
|
171
|
+
c.sev, c.id, c.cvss, c.desc))
|
|
172
|
+
end
|
|
173
|
+
out["CVEs"] = lines
|
|
174
|
+
|
|
175
|
+
local is_vuln = version == "2.4.16" or browsed
|
|
176
|
+
if is_vuln then
|
|
177
|
+
out["Verdict"] = V.VULN .. " — " .. cves[1].id
|
|
178
|
+
else
|
|
179
|
+
out["Verdict"] = V.POSS .. " — check printer-vuln-check for confirmation"
|
|
180
|
+
end
|
|
181
|
+
out["Suggest"] = "printerxpl-forge run --module " .. cves[1].xpl .. " --target " .. host.ip
|
|
182
|
+
else
|
|
183
|
+
out["Verdict"] = V.NOT .. " — CUPS version not in vulnerable range and cups-browsed not detected"
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
out["Full-Scan"] = "pip install printerxpl-forge | printerxpl-forge run --module xpl/research/research-cups-chain-2026 --target " .. host.ip
|
|
187
|
+
|
|
188
|
+
return out
|
|
189
|
+
end
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
local description = [[
|
|
2
|
+
printer-cve-detect — Passive CVE detection from fingerprint data.
|
|
3
|
+
|
|
4
|
+
Aggregates banners collected by printer-banner, printer-pjl-info, printer-ipp-info,
|
|
5
|
+
and printer-http-ews scripts (via nmap registry) or performs its own lightweight
|
|
6
|
+
fingerprint if run standalone. Cross-references against the full PrinterXPL-Forge
|
|
7
|
+
CVE database (80+ CVEs) and outputs a ranked list of applicable vulnerabilities.
|
|
8
|
+
|
|
9
|
+
This script does NOT perform active exploitation — it is safe/non-intrusive.
|
|
10
|
+
For active validation use printer-vuln-check.
|
|
11
|
+
|
|
12
|
+
Author: André Henrique (@mrhenrike) | União Geek
|
|
13
|
+
]]
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
-- @usage
|
|
17
|
+
-- nmap -sV -p 9100,631,80 --script printer-banner,printer-cve-detect <target>
|
|
18
|
+
-- nmap -sV -p 9100,631,80 --script printer-cve-detect <target>
|
|
19
|
+
-- @output
|
|
20
|
+
-- PORT STATE SERVICE
|
|
21
|
+
-- 80/tcp open http
|
|
22
|
+
-- | printer-cve-detect:
|
|
23
|
+
-- | Vendor : Lexmark
|
|
24
|
+
-- | Model : CX622 (detected from HTTP EWS)
|
|
25
|
+
-- | CVEs (3 matching):
|
|
26
|
+
-- | [CRITICAL] CVE-2023-23560 CVSS 9.0 — Lexmark SSRF-to-RCE | module: xpl/edb-51928
|
|
27
|
+
-- | [HIGH] CVE-2023-50739 CVSS 8.8 — Lexmark IPP heap BOF | module: xpl/edb-cve-2023-50739
|
|
28
|
+
-- | [MEDIUM] CVE-2023-50733 CVSS 6.5 — Lexmark EWS SSRF | module: xpl/edb-cve-2023-50733
|
|
29
|
+
-- | Top Verdict : POSSIBLY VULNERABLE (CVE-2023-23560 — confirm with printer-vuln-check)
|
|
30
|
+
-- |_ Suggest : printerxpl-forge run --module xpl/edb-51928 --target <IP>
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
categories = { "vuln", "safe", "discovery" }
|
|
34
|
+
author = "André Henrique (@mrhenrike) | União Geek"
|
|
35
|
+
license = "Same as Nmap -- See https://nmap.org/book/man-legal.html"
|
|
36
|
+
|
|
37
|
+
local stdnse = require "stdnse"
|
|
38
|
+
local shortport = require "shortport"
|
|
39
|
+
local http = require "http"
|
|
40
|
+
local comm = require "comm"
|
|
41
|
+
local string = require "string"
|
|
42
|
+
local table = require "table"
|
|
43
|
+
|
|
44
|
+
portrule = shortport.port_or_service(
|
|
45
|
+
{ 9100, 631, 80, 443, 515, 161 },
|
|
46
|
+
{ "jetdirect", "ipp", "http", "https", "printer", "snmp" },
|
|
47
|
+
{ "tcp", "udp" }
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
-- Compact CVE database (VENDOR → list of CVEs)
|
|
51
|
+
local CVE_DB = {
|
|
52
|
+
HP = {
|
|
53
|
+
{ id="CVE-2025-26506", cvss=9.8, sev="CRITICAL", port=9100,
|
|
54
|
+
desc="HP LaserJet Enterprise PostScript unauthenticated RCE",
|
|
55
|
+
xpl="xpl/edb-cve-2025-26506" },
|
|
56
|
+
{ id="CVE-2023-6018", cvss=9.8, sev="CRITICAL", port=443,
|
|
57
|
+
desc="HP LaserJet Enterprise firmware auth bypass → arbitrary upload",
|
|
58
|
+
xpl="xpl/research/research-hp-fw-bypass" },
|
|
59
|
+
{ id="CVE-2023-1707", cvss=9.1, sev="CRITICAL", port=443,
|
|
60
|
+
desc="HP FutureSmart 5.6 scan job data disclosure (IPsec enabled)",
|
|
61
|
+
xpl="xpl/research/research-hp-futuresmart-leak" },
|
|
62
|
+
{ id="CVE-2017-2741", cvss=9.8, sev="CRITICAL", port=9100,
|
|
63
|
+
desc="HP PageWide PJL path traversal to RCE",
|
|
64
|
+
xpl="xpl/edb-cve-2017-2741" },
|
|
65
|
+
{ id="CVE-2018-5924", cvss=9.8, sev="CRITICAL", port=9100,
|
|
66
|
+
desc="Faxploit — HP fax stack overflow via malformed fax data",
|
|
67
|
+
xpl="xpl/edb-cve-2018-5924" },
|
|
68
|
+
},
|
|
69
|
+
Canon = {
|
|
70
|
+
{ id="CVE-2022-24673", cvss=9.8, sev="CRITICAL", port=427,
|
|
71
|
+
desc="Canon imageCLASS SLP pre-auth stack BOF → root RCE (ZDI-22-515)",
|
|
72
|
+
xpl="xpl/edb-cve-2022-24673" },
|
|
73
|
+
{ id="CVE-2025-14235", cvss=9.8, sev="CRITICAL", port=9100,
|
|
74
|
+
desc="Canon imageCLASS PCL buffer overflow → RCE",
|
|
75
|
+
xpl="xpl/research/research-canon-pcl-bof" },
|
|
76
|
+
},
|
|
77
|
+
Lexmark = {
|
|
78
|
+
{ id="CVE-2023-23560", cvss=9.0, sev="CRITICAL", port=80,
|
|
79
|
+
desc="Lexmark SSRF-to-RCE via Web Services interface (Pwn2Own 2022)",
|
|
80
|
+
xpl="xpl/edb-51928" },
|
|
81
|
+
{ id="CVE-2023-26067", cvss=9.1, sev="CRITICAL", port=80,
|
|
82
|
+
desc="Lexmark post-auth RCE via device configuration API",
|
|
83
|
+
xpl="xpl/edb-cve-2023-26067" },
|
|
84
|
+
{ id="CVE-2023-50739", cvss=8.8, sev="HIGH", port=631,
|
|
85
|
+
desc="Lexmark IPP heap buffer overflow → RCE (100+ models, ZDI-CAN-22549)",
|
|
86
|
+
xpl="xpl/edb-cve-2023-50739" },
|
|
87
|
+
{ id="CVE-2023-50733", cvss=6.5, sev="MEDIUM", port=80,
|
|
88
|
+
desc="Lexmark EWS SSRF — printer as lateral movement pivot",
|
|
89
|
+
xpl="xpl/edb-cve-2023-50733" },
|
|
90
|
+
{ id="CVE-2023-50738", cvss=7.2, sev="HIGH", port=80,
|
|
91
|
+
desc="Lexmark firmware downgrade bypass → re-expose historical vulns",
|
|
92
|
+
xpl="xpl/research/research-lexmark-fw-decrypt" },
|
|
93
|
+
},
|
|
94
|
+
Xerox = {
|
|
95
|
+
{ id="CVE-2024-6333", cvss=8.1, sev="HIGH", port=80,
|
|
96
|
+
desc="Xerox VersaLink authenticated OS command injection",
|
|
97
|
+
xpl="xpl/edb-cve-2024-6333" },
|
|
98
|
+
{ id="CVE-2021-27508", cvss=8.0, sev="HIGH", port=80,
|
|
99
|
+
desc="Xerox WorkCentre 78xx OS command injection via clone_group",
|
|
100
|
+
xpl="xpl/research/research-xerox-workcentre-cmdinject" },
|
|
101
|
+
},
|
|
102
|
+
Ricoh = {
|
|
103
|
+
{ id="CVE-2024-34161", cvss=8.8, sev="HIGH", port=80,
|
|
104
|
+
desc="Ricoh MP EWS malformed HTTP → RCE",
|
|
105
|
+
xpl="xpl/research/research-ricoh-ews-rce" },
|
|
106
|
+
{ id="CVE-2021-33945", cvss=7.5, sev="HIGH", port=80,
|
|
107
|
+
desc="Ricoh SP wpa_supplicant stack overflow → DoS/RCE",
|
|
108
|
+
xpl="xpl/research/research-ricoh-wpa-bof" },
|
|
109
|
+
},
|
|
110
|
+
Brother = {
|
|
111
|
+
{ id="CVE-2024-51977", cvss=7.5, sev="HIGH", port=80,
|
|
112
|
+
desc="Brother admin password derivable from serial number (info disc.)",
|
|
113
|
+
xpl="xpl/research/research-brother-serial-pwd" },
|
|
114
|
+
{ id="CVE-2024-51978", cvss=9.1, sev="CRITICAL", port=80,
|
|
115
|
+
desc="Brother auth bypass via serial-derived default credential",
|
|
116
|
+
xpl="xpl/research/research-brother-serial-pwd" },
|
|
117
|
+
},
|
|
118
|
+
Sharp = {
|
|
119
|
+
{ id="CVE-2022-45796", cvss=9.8, sev="CRITICAL", port=80,
|
|
120
|
+
desc="Sharp MX unauthenticated OS command injection",
|
|
121
|
+
xpl="xpl/research/research-sharp-rce" },
|
|
122
|
+
},
|
|
123
|
+
Toshiba = {
|
|
124
|
+
{ id="CVE-2024-21911", cvss=8.8, sev="HIGH", port=80,
|
|
125
|
+
desc="Toshiba e-STUDIO TopAccess authentication bypass → RCE",
|
|
126
|
+
xpl="xpl/research/research-toshiba-auth-bypass" },
|
|
127
|
+
},
|
|
128
|
+
Kyocera = {
|
|
129
|
+
{ id="CVE-2022-1026", cvss=7.5, sev="HIGH", port=9100,
|
|
130
|
+
desc="Kyocera ECOSYS unauthenticated address book credential dump",
|
|
131
|
+
xpl="xpl/edb-cve-2022-1026" },
|
|
132
|
+
},
|
|
133
|
+
Microsoft = {
|
|
134
|
+
{ id="CVE-2021-1675", cvss=9.8, sev="CRITICAL", port=445,
|
|
135
|
+
desc="PrintNightmare — Windows Print Spooler unauthenticated RCE/LPE",
|
|
136
|
+
xpl="xpl/edb-50498" },
|
|
137
|
+
{ id="CVE-2022-38028", cvss=7.8, sev="HIGH", port=445,
|
|
138
|
+
desc="GooseEgg — APT28 Windows Print Spooler LPE (CISA KEV 2024)",
|
|
139
|
+
xpl="xpl/research/research-gooseegg-spooler" },
|
|
140
|
+
{ id="CVE-2020-1048", cvss=7.8, sev="HIGH", port=445,
|
|
141
|
+
desc="PrintDemon — Windows Print Spooler persistent SYSTEM backdoor",
|
|
142
|
+
xpl="xpl/msf-cve-2020-1048-printerdemon" },
|
|
143
|
+
{ id="CVE-2020-1337", cvss=7.8, sev="HIGH", port=445,
|
|
144
|
+
desc="PrintDemon hardlink bypass — arbitrary privileged file write",
|
|
145
|
+
xpl="xpl/edb-cve-2020-1337" },
|
|
146
|
+
},
|
|
147
|
+
["Linux/CUPS"] = {
|
|
148
|
+
{ id="CVE-2024-47176", cvss=9.9, sev="CRITICAL", port=631,
|
|
149
|
+
desc="CUPS cups-browsed unauthenticated RCE chain (2024)",
|
|
150
|
+
xpl="xpl/edb-cve-2024-47176" },
|
|
151
|
+
{ id="CVE-2026-34980", cvss=9.1, sev="CRITICAL", port=631,
|
|
152
|
+
desc="CUPS 2.4.16 unauthenticated RCE via PPD newline injection (2026)",
|
|
153
|
+
xpl="xpl/research/research-cups-chain-2026" },
|
|
154
|
+
{ id="CVE-2026-34990", cvss=7.8, sev="HIGH", port=631,
|
|
155
|
+
desc="CUPS 2.4.16 LPE to root via race condition on local printer",
|
|
156
|
+
xpl="xpl/research/research-cups-root-2026" },
|
|
157
|
+
},
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
-- Grab any banner we can get quickly
|
|
161
|
+
local function quick_fingerprint(host, port)
|
|
162
|
+
local portnum = port.number
|
|
163
|
+
local text = ""
|
|
164
|
+
|
|
165
|
+
if portnum == 9100 then
|
|
166
|
+
local uel = "\x1b%-12345X"
|
|
167
|
+
local ok, r = comm.exchange(host, port,
|
|
168
|
+
uel .. "@PJL\r\n@PJL INFO ID\r\n" .. uel,
|
|
169
|
+
{ timeout = 4000, bytes = 2048 })
|
|
170
|
+
if ok then text = r end
|
|
171
|
+
elseif portnum == 80 or portnum == 443 then
|
|
172
|
+
local r = http.get(host, portnum, "/", { timeout = 4000 })
|
|
173
|
+
if r and r.body then text = r.body:sub(1, 2048) end
|
|
174
|
+
elseif portnum == 631 then
|
|
175
|
+
local r = http.get(host, portnum, "/", { timeout = 4000 })
|
|
176
|
+
if r and r.body then text = r.body:sub(1, 2048) end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
return text
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
-- Detect vendor from text
|
|
183
|
+
local function detect_vendor(text)
|
|
184
|
+
if not text or text == "" then return nil end
|
|
185
|
+
local t = text:lower()
|
|
186
|
+
local checks = {
|
|
187
|
+
{ pat = "hp laserjet|hewlett.packard|jetdirect|futuresmart", v = "HP" },
|
|
188
|
+
{ pat = "canon imagerunner|canon imageclass|canon lbp", v = "Canon" },
|
|
189
|
+
{ pat = "lexmark", v = "Lexmark" },
|
|
190
|
+
{ pat = "xerox versalink|xerox workcentre|xerox altalink|xerox", v = "Xerox" },
|
|
191
|
+
{ pat = "ricoh|aficio", v = "Ricoh" },
|
|
192
|
+
{ pat = "brother mfc|brother dcp", v = "Brother" },
|
|
193
|
+
{ pat = "epson workforce|epson ecotank", v = "Epson" },
|
|
194
|
+
{ pat = "konica|bizhub", v = "Konica Minolta" },
|
|
195
|
+
{ pat = "kyocera ecosys|kyocera taskalfa", v = "Kyocera" },
|
|
196
|
+
{ pat = "sharp mx|sharp ar", v = "Sharp" },
|
|
197
|
+
{ pat = "toshiba e.studio|topaccess", v = "Toshiba" },
|
|
198
|
+
{ pat = "samsung scx|samsung clx", v = "Samsung" },
|
|
199
|
+
{ pat = "cups 2%.4%.16", v = "Linux/CUPS" },
|
|
200
|
+
{ pat = "cups", v = "Linux/CUPS" },
|
|
201
|
+
{ pat = "windows.*spooler|print spooler|printdemon|spoolsv", v = "Microsoft" },
|
|
202
|
+
}
|
|
203
|
+
for _, c in ipairs(checks) do
|
|
204
|
+
if t:match(c.pat) then return c.v end
|
|
205
|
+
end
|
|
206
|
+
return nil
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
action = function(host, port)
|
|
210
|
+
-- Try registry first (from other printer scripts running in same session)
|
|
211
|
+
local banner = stdnse.registry_get({ host.ip, "printer-banner" }) or ""
|
|
212
|
+
local vendor = stdnse.registry_get({ host.ip, "printer-vendor" })
|
|
213
|
+
|
|
214
|
+
if not vendor or vendor == "" then
|
|
215
|
+
local fingerprint = quick_fingerprint(host, port)
|
|
216
|
+
banner = banner .. fingerprint
|
|
217
|
+
vendor = detect_vendor(fingerprint)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
if not vendor then
|
|
221
|
+
return "Cannot determine vendor — combine with printer-banner: " ..
|
|
222
|
+
"nmap --script printer-banner,printer-cve-detect"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
-- Store for other scripts
|
|
226
|
+
stdnse.registry_set({ host.ip, "printer-vendor" }, vendor)
|
|
227
|
+
|
|
228
|
+
local cve_list = CVE_DB[vendor] or {}
|
|
229
|
+
local out = stdnse.output_table()
|
|
230
|
+
out["Vendor"] = vendor
|
|
231
|
+
|
|
232
|
+
if #cve_list == 0 then
|
|
233
|
+
out["CVEs"] = "0 matching CVEs in database for vendor: " .. vendor
|
|
234
|
+
out["Verdict"] = "NOT VULNERABLE (no CVE entries for this vendor)"
|
|
235
|
+
return out
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
-- Filter by port relevance
|
|
239
|
+
local port_num = port.number
|
|
240
|
+
local relevant = {}
|
|
241
|
+
for _, cve in ipairs(cve_list) do
|
|
242
|
+
if (not cve.port) or cve.port == 0 or cve.port == port_num then
|
|
243
|
+
table.insert(relevant, cve)
|
|
244
|
+
else
|
|
245
|
+
-- Include high-severity regardless of port (printer may have other ports open)
|
|
246
|
+
if cve.cvss >= 9.0 then
|
|
247
|
+
table.insert(relevant, cve)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
-- Sort by CVSS descending
|
|
253
|
+
table.sort(relevant, function(a, b) return a.cvss > b.cvss end)
|
|
254
|
+
|
|
255
|
+
out["CVE-Count"] = #relevant .. " matching CVEs for " .. vendor
|
|
256
|
+
|
|
257
|
+
local cve_lines = {}
|
|
258
|
+
for _, c in ipairs(relevant) do
|
|
259
|
+
table.insert(cve_lines, string.format(
|
|
260
|
+
"[%s] %s (CVSS %.1f) — %s\n module: printerxpl-forge run --module %s",
|
|
261
|
+
c.sev, c.id, c.cvss, c.desc, c.xpl))
|
|
262
|
+
end
|
|
263
|
+
out["CVEs"] = cve_lines
|
|
264
|
+
|
|
265
|
+
-- Verdict based on CVSS of top match
|
|
266
|
+
local top = relevant[1]
|
|
267
|
+
if top.cvss >= 9.0 then
|
|
268
|
+
out["Verdict"] = "POSSIBLY VULNERABLE — CRITICAL severity CVE match (confirm with printer-vuln-check)"
|
|
269
|
+
elseif top.cvss >= 7.0 then
|
|
270
|
+
out["Verdict"] = "POSSIBLY VULNERABLE — HIGH severity CVE match"
|
|
271
|
+
else
|
|
272
|
+
out["Verdict"] = "LOW RISK — only MEDIUM/LOW CVEs matched"
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
out["Suggest"] = "printerxpl-forge run --module " .. top.xpl .. " --dry-run --target " .. host.ip
|
|
276
|
+
out["Full-Scan"] = "pip install printerxpl-forge | printerxpl-forge scan --target " .. host.ip
|
|
277
|
+
|
|
278
|
+
return out
|
|
279
|
+
end
|