#!/usr/bin/env python3 """ CVE-2026-32746 - Non-destructive telnetd version detection ============================================================ Connects to a telnet service, negotiates options, and checks whether the service supports LINEMODE (indicating GNU InetUtils telnetd, which is affected by CVE-2026-32746). Does NOT send any exploit payload. Safe for production scanning. Usage: python3 detect.py [port] """ import argparse import socket import sys import time IAC = 0xFF DO = 0xFD WILL = 0xFB WONT = 0xFC DONT = 0xFE SB = 0xFA SE = 0xF0 OPT_TTYPE = 0x18 OPT_TSPEED = 0x20 OPT_LINEMODE = 0x22 def recv_all(s, timeout=2): """Receive all available data with a timeout.""" s.settimeout(timeout) chunks = [] try: while True: chunk = s.recv(4096) if not chunk: break chunks.append(chunk) except socket.timeout: pass return b''.join(chunks) def detect(host, port, timeout): """Check if target appears to run vulnerable telnetd.""" print(f"[*] Checking {host}:{port} for GNU InetUtils telnetd") print(f" (CVE-2026-32746 - LINEMODE SLC Buffer Overflow)\n") try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(timeout) s.connect((host, port)) except (socket.error, OSError) as e: print(f"[-] Connection failed: {e}") return # Round 1: Read initial negotiation time.sleep(1) data = recv_all(s) if not data: print("[-] No data received") s.close() return # Parse initial options options = [] i = 0 while i < len(data) - 2: if data[i] == IAC: cmd = data[i + 1] opt = data[i + 2] if cmd in (DO, WILL): options.append((cmd, opt)) i += 3 else: i += 1 print(f" Port {port}/tcp open") print(f" Initial options: {len(options)}") # Respond to everything + offer WILL LINEMODE resp = bytearray() for cmd, opt in options: if cmd == DO: resp.extend([IAC, WILL, opt]) elif cmd == WILL: resp.extend([IAC, DO, opt]) # Proactively offer LINEMODE resp.extend([IAC, WILL, OPT_LINEMODE]) # Send TTYPE and TSPEED suboptions (server expects these) resp.extend([IAC, SB, OPT_TTYPE, 0x00]) resp.extend(b'xterm') resp.extend([IAC, SE]) resp.extend([IAC, SB, OPT_TSPEED, 0x00]) resp.extend(b'38400,38400') resp.extend([IAC, SE]) s.send(resp) # Round 2: Check for DO LINEMODE time.sleep(1) data2 = recv_all(s) got_linemode = False got_slc = False if data2: i = 0 while i < len(data2) - 2: if data2[i] == IAC: if data2[i + 1] == DO and data2[i + 2] == OPT_LINEMODE: got_linemode = True elif data2[i + 1] == SB and i + 3 < len(data2) and data2[i + 2] == OPT_LINEMODE: if i + 4 < len(data2) and data2[i + 3] == 0x03: # LM_SLC got_slc = True i += 3 if data2[i + 1] not in (SB,) else 1 else: i += 1 print(f" LINEMODE accepted: {'Yes' if got_linemode else 'No'}") print(f" SLC negotiation: {'Yes' if got_slc else 'No'}") if got_linemode: print(f"\n[!] LIKELY VULNERABLE to CVE-2026-32746") print(f" Server supports LINEMODE with SLC negotiation") print(f" GNU InetUtils telnetd through 2.7 is affected") print(f" CVSS: 9.8 (Critical) | No patch available") print(f"\n Run exploit.py to confirm (crashes the service)") else: print(f"\n[*] LINEMODE not accepted") print(f" Likely not GNU InetUtils telnetd, or LINEMODE disabled") # Clean disconnect s.close() def main(): parser = argparse.ArgumentParser( description="CVE-2026-32746 - Non-destructive telnetd detection", epilog="Safe for production use. Does not send exploit payload." ) parser.add_argument("host", help="Target IP address") parser.add_argument("port", type=int, nargs="?", default=23, help="Target port (default: 23)") parser.add_argument("-t", "--timeout", type=int, default=10, help="Socket timeout in seconds (default: 10)") args = parser.parse_args() detect(args.host, args.port, args.timeout) if __name__ == "__main__": main()