To: [email protected]
Subject: [Security] telnetd slcbuf global buffer overflow in add_slc() —
pre-auth, remotely triggerable
Hi,
Ran into a buffer overflow in telnetd's SLC handling while fuzzing inetutils
2.2.
The add_slc() function in telnetd/slc.c writes triplets into a fixed-size global
buffer without any bounds check, so a client can overflow it by sending too many
SLC entries in a single LINEMODE suboption.
The vulnerable code (slc.c:166-178):
static unsigned char slcbuf[NSLC * 6]; /* 108 bytes */
static unsigned char *slcptr;
void add_slc(register char func, register char flag, register cc_t val)
{
if ((*slcptr++ = (unsigned char) func) == 0xff)
*slcptr++;
if ((*slcptr++ = (unsigned char) flag) == 0xff)
*slcptr++;
if ((*slcptr++ = (unsigned char) val) == 0xff)
*slcptr++;
}
start_slc() sets slcptr = slcbuf + 4 (4-byte header). Each triplet adds 3 bytes.
No length check anywhere. Once do_opt_slc() processes more than 34 triplets from
a single suboption, slcptr goes past the end of slcbuf.
To trigger, a client sends:
FF FB 22 # IAC WILL LINEMODE
FF FA 22 03 (13 03 01 × 35) FF F0 # IAC SB LINEMODE SLC + 35 triplets + SE
With ASAN:
global-buffer-overflow on address 0x000000dd822c
WRITE of size 1 at 0x000000dd822c
#0 add_slc slc.c:175
#1 process_slc slc.c:268
#2 do_opt_slc slc.c:498
#3 suboption state.c:1263
0x000000dd822c is located 0 bytes to the right of 'slcbuf' (size 108)
Boundary is sharp: 34 triplets = 106 bytes, no crash. 36 triplets = 112 bytes,
crash. Max overflow is ~400 bytes (limited by subbuffer[512]).
This happens during option negotiation, before any authentication.
The adjacent global is def_slcbuf (a pointer), so if the overflow reaches it,
deferslc() would later use a corrupted pointer in a memmove. Whether that's
practically exploitable depends on linker layout — in my build there's a 52-byte
gap (ASAN redzone), but production builds may place things differently.
Suggested fix — add a bounds check in add_slc():
void add_slc(register char func, register char flag, register cc_t val)
{
if (slcptr + 6 > slcbuf + sizeof(slcbuf))
return;
/* ... existing code ... */
}
And a similar check before the sprintf in end_slc() at line 240.
Happy to send over the full PoC, ASAN logs, and analysis if useful. This code
goes back to 4.4BSD so other telnetd implementations may have the same issue.
Thanks