zhangph <[email protected]> writes: > 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.
This was already identified [1] and fixed in inetutils-2.8 [2]. Collin [1] https://lists.gnu.org/archive/html/bug-inetutils/2026-03/msg00031.html [2] https://codeberg.org/inetutils/inetutils/commit/6864598a29b652a6b69a958f5cd1318aa2b258af
