5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / detect.py PY
#!/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()