Re: [Security] telnetd slcbuf global buffer overflow in add_slc() — pre-auth, remotely triggerable

Collin Funk <[email protected]> Thu, 11 Jun 2026 19:53:05 -0700
Newsgroups gmane.comp.gnu.inetutils.bugs
Message-ID <[email protected]>
zhangph <[email protected]> writes:

> Ran into a buffer overflow in telnetd's SLC handling while fuzzing inetut=
ils 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 t=
oo 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 va=
l)
>     {
>       if ((*slcptr++ =3D (unsigned char) func) =3D=3D 0xff)
>         *slcptr++;
>       if ((*slcptr++ =3D (unsigned char) flag) =3D=3D 0xff)
>         *slcptr++;
>       if ((*slcptr++ =3D (unsigned char) val) =3D=3D 0xff)
>         *slcptr++;
>     }
>
>
> start_slc() sets slcptr =3D slcbuf + 4 (4-byte header). Each triplet adds=
 3 bytes.
> No length check anywhere. Once do_opt_slc() processes more than 34 triple=
ts 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 =C3=97 35) FF F0 # IAC SB LINEMODE SLC + 35 tri=
plets + 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 =3D 106 bytes, no crash. 36 triplets =3D 1=
12 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 =E2=80=94 in my build th=
ere's a 52-byte
> gap (ASAN redzone), but production builds may place things differently.
>
>
> Suggested fix =E2=80=94 add a bounds check in add_slc():
>
>
>     void add_slc(register char func, register char flag, register cc_t va=
l)
>     {
>       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 is=
sue.

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/6864598a29b652a6b69a958=
f5cd1318aa2b258af