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,307 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Local Installed Printer Discovery
5
+ ==================================
6
+ Enumerates printers installed on the local host across all platforms.
7
+
8
+ Supports:
9
+ - Windows : Get-Printer + Get-PrinterPort (PowerShell)
10
+ - Linux : CUPS via lpstat or /etc/cups/printers.conf
11
+ - macOS : CUPS via lpstat
12
+ - Android : CUPS via lpstat (if installed)
13
+ - BSD : CUPS via lpstat
14
+ """
15
+
16
+ # Author : Andre Henrique (@mrhenrike)
17
+ # GitHub : https://github.com/mrhenrike
18
+ # LinkedIn : https://linkedin.com/in/mrhenrike
19
+ # X/Twitter : https://x.com/mrhenrike
20
+
21
+ from __future__ import annotations
22
+
23
+ import re
24
+ import shutil
25
+ import subprocess
26
+ import socket
27
+ from typing import Dict, List, Optional
28
+
29
+ from core.osdetect import get_os
30
+ from utils.helper import output
31
+
32
+
33
+ # ── dataclass-like dict keys ─────────────────────────────────────────────────
34
+ # Each printer entry dict has these keys (all strings):
35
+ # name, driver, port, ip, protocol, status, shared, source
36
+
37
+
38
+ def discover_local_installed() -> List[Dict[str, str]]:
39
+ """
40
+ Enumerate all printers installed on this host.
41
+
42
+ Returns a list of dicts with printer info.
43
+ Each dict has: name, driver, port, ip, protocol, status, shared, source.
44
+ """
45
+ os_type = get_os()
46
+
47
+ if os_type in ('windows', 'wsl'):
48
+ return _windows_printers()
49
+ elif os_type in ('linux', 'darwin', 'bsd', 'android', 'wsl'):
50
+ return _cups_printers()
51
+ else:
52
+ output().warning("Local printer discovery not supported on this OS.")
53
+ return []
54
+
55
+
56
+ # ── Windows ──────────────────────────────────────────────────────────────────
57
+
58
+ def _windows_printers() -> List[Dict[str, str]]:
59
+ """Use PowerShell Get-Printer to list installed printers, then resolve IPs."""
60
+ pwsh = shutil.which('powershell.exe') or shutil.which('pwsh.exe')
61
+ if not pwsh:
62
+ output().warning("PowerShell not found; cannot enumerate local printers.")
63
+ return []
64
+
65
+ script = (
66
+ "Get-Printer | Select-Object Name,DriverName,PortName,PrinterStatus,Shared"
67
+ " | ConvertTo-Json -Compress"
68
+ )
69
+ try:
70
+ raw = subprocess.check_output(
71
+ [pwsh, '-NoProfile', '-Command', script],
72
+ text=True, timeout=20, stderr=subprocess.DEVNULL,
73
+ )
74
+ except Exception as e:
75
+ output().warning(f"Get-Printer failed: {e}")
76
+ return []
77
+
78
+ import json
79
+ try:
80
+ data = json.loads(raw) if raw.strip() else []
81
+ except json.JSONDecodeError:
82
+ output().warning("Could not parse Get-Printer JSON output.")
83
+ return []
84
+
85
+ if isinstance(data, dict):
86
+ data = [data]
87
+
88
+ results = []
89
+ for p in data:
90
+ name = str(p.get('Name', ''))
91
+ driver = str(p.get('DriverName', ''))
92
+ port = str(p.get('PortName', ''))
93
+ status = _win_status(str(p.get('PrinterStatus', '0')))
94
+ shared = 'yes' if p.get('Shared') else 'no'
95
+
96
+ ip, proto = _resolve_win_printer(port, name)
97
+
98
+ results.append({
99
+ 'name': name,
100
+ 'driver': driver,
101
+ 'port': port,
102
+ 'ip': ip,
103
+ 'protocol': proto,
104
+ 'status': status,
105
+ 'shared': shared,
106
+ 'source': 'windows-local',
107
+ })
108
+ return results
109
+
110
+
111
+ def _resolve_win_printer(port_name: str, printer_name: str) -> tuple:
112
+ """
113
+ Resolve IP and protocol from a Windows printer port name and printer name.
114
+
115
+ Returns (ip_str, protocol_str).
116
+ """
117
+ # Pattern: IP_x.x.x.x or IP_x.x.x.x_N (standard TCP/IP port monitor)
118
+ m = re.match(r'IP_(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', port_name)
119
+ if m:
120
+ return m.group(1), 'TCP/9100'
121
+
122
+ # Pattern: direct IP in port name (e.g. '192.168.1.5:9100')
123
+ m = re.match(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?::(\d+))?', port_name)
124
+ if m:
125
+ return m.group(1), f"TCP/{m.group(2) or '9100'}"
126
+
127
+ # Pattern: Epson port like EP3F9F9C:L3250 SERIES
128
+ m = re.match(r'EP([0-9A-Fa-f]{4,8})', port_name, re.I)
129
+ if m:
130
+ hostname = 'EPSON' + m.group(1).upper()
131
+ ip = _try_resolve(hostname) or ''
132
+ return ip, 'WSD/IPP'
133
+
134
+ # WSD port: try to resolve from the printer name
135
+ # Windows WSD printer names often start with the device hostname
136
+ # e.g. "EPSON3F9F9C (L3250 Series)", "PROMETHEUSPRINTER (HP DeskJet 2700 series)"
137
+ if port_name.startswith('WSD-') or port_name.startswith('wsd-'):
138
+ hostname = printer_name.split(' ')[0].strip()
139
+ ip = _try_resolve(hostname) or ''
140
+ proto = 'WSD/IPP'
141
+ # Fallback: try extracting hostname from parentheses if first word fails
142
+ if not ip:
143
+ m2 = re.search(r'\(([^)]+)\)', printer_name)
144
+ if m2:
145
+ ip = _try_resolve(m2.group(1).strip()) or ''
146
+ return ip, proto
147
+
148
+ # Virtual/local ports
149
+ virtual = ('nul:', 'PORTPROMPT:', 'SHRFAX:', 'USB001', 'FILE:',
150
+ 'LPT', 'COM', 'XPS')
151
+ if any(port_name.startswith(v) for v in virtual):
152
+ return '', 'Virtual'
153
+
154
+ return '', 'WSD'
155
+
156
+
157
+ def _win_status(code: str) -> str:
158
+ """Map Windows PrinterStatus integer to human-readable string."""
159
+ try:
160
+ n = int(code)
161
+ except ValueError:
162
+ return code
163
+ mapping = {
164
+ 0: 'Ready',
165
+ 1: 'Paused',
166
+ 2: 'Error',
167
+ 3: 'Pending Deletion',
168
+ 4: 'Paper Jam',
169
+ 5: 'Paper Out',
170
+ 6: 'Manual Feed',
171
+ 7: 'Paper Problem',
172
+ 8: 'Offline',
173
+ 16: 'Printing',
174
+ 32: 'Output Bin Full',
175
+ 64: 'Paper jam (alt)',
176
+ 128: 'No Toner/Ink',
177
+ 132: 'Offline (no toner)',
178
+ }
179
+ return mapping.get(n, f'Status:{n}')
180
+
181
+
182
+ def _try_resolve(hostname: str) -> Optional[str]:
183
+ """Try to resolve a hostname to an IPv4 address."""
184
+ try:
185
+ return socket.gethostbyname(hostname)
186
+ except socket.gaierror:
187
+ return None
188
+
189
+
190
+ # ── CUPS (Linux / macOS / BSD / Android) ─────────────────────────────────────
191
+
192
+ def _cups_printers() -> List[Dict[str, str]]:
193
+ """Use lpstat -v to enumerate CUPS-managed printers."""
194
+ lpstat = shutil.which('lpstat')
195
+ if not lpstat:
196
+ output().warning("lpstat not found. Install CUPS client tools.")
197
+ return []
198
+
199
+ results = []
200
+ try:
201
+ # lpstat -v: lists printer names and device URIs
202
+ raw_v = subprocess.check_output(
203
+ [lpstat, '-v'], text=True, timeout=10, stderr=subprocess.DEVNULL,
204
+ )
205
+ # lpstat -l -p: detailed printer info (status etc)
206
+ try:
207
+ raw_l = subprocess.check_output(
208
+ [lpstat, '-l', '-p'], text=True, timeout=10,
209
+ stderr=subprocess.DEVNULL,
210
+ )
211
+ except Exception:
212
+ raw_l = ''
213
+
214
+ # Parse "device for <name>: <uri>"
215
+ for line in raw_v.splitlines():
216
+ m = re.match(r'device for (.+?):\s+(.+)', line)
217
+ if not m:
218
+ continue
219
+ name, uri = m.group(1).strip(), m.group(2).strip()
220
+ ip, protocol = _parse_cups_uri(uri)
221
+ status = _cups_status(name, raw_l)
222
+ results.append({
223
+ 'name': name,
224
+ 'driver': '',
225
+ 'port': uri,
226
+ 'ip': ip,
227
+ 'protocol': protocol,
228
+ 'status': status,
229
+ 'shared': '',
230
+ 'source': 'cups-local',
231
+ })
232
+ except Exception as e:
233
+ output().warning(f"lpstat failed: {e}")
234
+
235
+ return results
236
+
237
+
238
+ def _parse_cups_uri(uri: str):
239
+ """Parse a CUPS device URI and return (ip, protocol)."""
240
+ # socket://192.168.1.100:9100
241
+ m = re.match(r'socket://([^:/]+)(?::(\d+))?', uri)
242
+ if m:
243
+ ip = _try_resolve(m.group(1)) or m.group(1)
244
+ port = m.group(2) or '9100'
245
+ return ip, f'RAW/{port}'
246
+
247
+ # ipp://host:port/path or ipps://...
248
+ m = re.match(r'ipps?://([^:/]+)(?::(\d+))?(/.*)?', uri)
249
+ if m:
250
+ ip = _try_resolve(m.group(1)) or m.group(1)
251
+ port = m.group(2) or '631'
252
+ scheme = 'IPP' if uri.startswith('ipp://') else 'IPP/TLS'
253
+ return ip, f'{scheme}/{port}'
254
+
255
+ # lpd://host/queue
256
+ m = re.match(r'lpd://([^/]+)', uri)
257
+ if m:
258
+ ip = _try_resolve(m.group(1)) or m.group(1)
259
+ return ip, 'LPD/515'
260
+
261
+ # usb://Make/Model
262
+ if uri.startswith('usb://'):
263
+ return '', 'USB'
264
+
265
+ # dnssd:// or mdns://
266
+ if 'dnssd' in uri or 'mdns' in uri:
267
+ m = re.search(r'/([^/]+)/queue', uri)
268
+ hostname = m.group(1) if m else ''
269
+ ip = _try_resolve(hostname) or ''
270
+ return ip, 'Bonjour/IPP'
271
+
272
+ return '', uri.split('://')[0].upper()
273
+
274
+
275
+ def _cups_status(name: str, lpstat_output: str) -> str:
276
+ """Extract printer status from lpstat -l -p output."""
277
+ # Look for "printer <name> ..." section
278
+ m = re.search(
279
+ rf'printer {re.escape(name)}\s+(?:is\s+)?(\S+)',
280
+ lpstat_output, re.I,
281
+ )
282
+ if m:
283
+ return m.group(1).replace('-', ' ').title()
284
+ return '?'
285
+
286
+
287
+ # ── Display ──────────────────────────────────────────────────────────────────
288
+
289
+ def print_local_printers(printers: List[Dict[str, str]]) -> None:
290
+ """Pretty-print the list of local installed printers."""
291
+ if not printers:
292
+ print(" [!] No local printers found.")
293
+ return
294
+
295
+ header = f" {'HOST':<18} {'NAME':<35} {'PROTO':<12} {'STATUS':<14} {'DRIVER'}"
296
+ print(f"\033[1;32m{header}\033[0m")
297
+ print(" " + "─" * 98)
298
+
299
+ for p in printers:
300
+ host = (p['ip'] or '-').ljust(18)[:18]
301
+ name = (p['name'] or '-').ljust(35)[:35]
302
+ proto = (p['protocol'] or '-').ljust(12)[:12]
303
+ status = (p['status'] or '-').ljust(14)[:14]
304
+ driver = (p['driver'] or '-')[:30]
305
+ print(f" {host} {name} {proto} {status} {driver}")
306
+
307
+ print()