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

Reply via email to