README.md
Rendering markdown...
#!/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 <target_ip> [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()