On Thu, Mar 12, 2026 at 08:24:42PM +0200, Justin Swartz wrote:
> I would like to draw the community's attention to the following
> vulnerability [1], summarized as "Remote Pre-Auth Buffer Overflow
> in GNU Inetutils telnetd (LINEMODE SLC)", which was reported to
> the bug-inetutils mailing list recently.
>
> I am not affiliated with the researchers, inetutils, nor GNU/FSF.
>
> Regards,
> Justin
>
> ---
>
> [1]
> https://lists.gnu.org/archive/html/bug-inetutils/2026-03/msg00031.html
Thank you, Justin!
In cases like this, we should be bringing the entire report to
oss-security, not just a link. So I'll include it below.
Further in the above thread, there's a link to a fix pull request by
Collin Funk. I didn't review it in full context, but even within the
patch context it fails my review:
add_slc (char func, char flag, cc_t val)
{
/* Do nothing if the entire triplet cannot fit in the buffer. */
if (slcbuf + sizeof slcbuf <= slcptr + 6)
return;
if ((*slcptr++ = (unsigned char) func) == 0xff)
*slcptr++ = 0xff;
if ((*slcptr++ = (unsigned char) flag) == 0xff)
*slcptr++ = 0xff;
if ((*slcptr++ = (unsigned char) val) == 0xff)
*slcptr++ = 0xff;
} /* end of add_slc */
In "slcptr + 6", it appears to rely on pointer math working outside of
the object, but that's UB in C. If the C compiler concludes that the
"if" condition cannot be true within defined behavior, it is free to
optimize the entire "if" and "return" out. A proper check may be:
if (slcbuf + sizeof slcbuf - 6 <= slcptr)
or perhaps with "<" in place of "<=", unless we need an extra element
for some reason.
For comparison, here's the function from netkit-telnet-0.17/telnetd/slc.c:
static unsigned char slcbuf[NSLC*6]; /* buffer for slc negotiation */
static void add_slcbuf_raw_char(unsigned char ch) {
if (slcoff < sizeof(slcbuf)) {
slcbuf[slcoff++] = ch;
}
}
static void add_slcbuf_char(unsigned char ch) {
add_slcbuf_raw_char(ch);
if (ch==0xff) {
add_slcbuf_raw_char(0xff);
}
}
#ifdef LINEMODE
/*
* add_slc
*
* Add an slc triplet to the slc buffer.
*/
void add_slc(char func, char flag, cc_t val) {
add_slcbuf_char(func);
add_slcbuf_char(flag);
add_slcbuf_char(val);
}
Alexander
---
From: Adiel Sol
Subject: Remote Pre-Auth Buffer Overflow in GNU Inetutils telnetd
(LINEMODE SLC)
Date: Wed, 11 Mar 2026 12:20:57 +0000
Hi GNU Inetutils / Savannah Security Team,
I would like to report a security vulnerability in GNU Inetutils telnetd.
Summary
The telnetd server has a buffer overflow in the LINEMODE SLC (Set Local
Characters) suboption handler. An unauthenticated attacker can trigger it by
connecting to port 23 and sending a crafted SLC suboption with many triplets.
No login is required; the bug is hit during option negotiation, before the
login prompt. The overflow corrupts memory and can be turned into arbitrary
writes. In practice this can lead to remote code execution. Because telnetd
usually runs as root (e.g. under inetd or xinetd), a successful exploit would
give the attacker full control of the system.
Affected Component
telnetd/slc.c (functions add_slc, process_slc, do_opt_slc; buffer slcbuf at
line 59)
Versions: All GNU Inetutils through 2.7, including current development HEAD.
Any telnetd derived from the BSD codebase that still uses this SLC
implementation may also be affected.
Root Cause
The SLC response is built in a fixed 108-byte buffer, slcbuf, with only 104
bytes used for data after a 4-byte header. The function add_slc() (lines
162-175) appends 3 bytes per SLC triplet but never checks whether the buffer is
full. The pointer slcptr is just incremented each time.
When the server gets an SLC suboption, it calls process_slc() for each triplet.
For any triplet with function code greater than 18 (NSLC), the server calls
add_slc() to queue a "not supported" reply. There is no limit on how many such
replies are queued.
The client can send a long SLC suboption (the suboption buffer is 512 bytes, so
about 170 triplets). After about 35 triplets with func > 18, the 104-byte space
is exceeded and the code writes past the end of slcbuf. That corrupts whatever
lies after it in BSS (including the slcptr pointer). Later, end_slc() uses the
corrupted slcptr to write the suboption end marker, which gives the attacker an
arbitrary write in memory. So the bug is a classic buffer overflow with no
bounds check (CWE-120, CWE-787).
Impact
CVSS 3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8 Critical)
CWE-120 (Buffer Copy without Checking Size of Input), CWE-787 (Out-of-bounds
Write)
Any unauthenticated attacker with network access to port 23 can trigger the
overflow with a single telnet connection and a crafted SLC suboption.
Successful exploitation can lead to arbitrary code execution as root and full
compromise of the host (backdoors, data exfiltration, pivot, etc.).
Proof of Concept
1. Start GNU Inetutils telnetd (e.g. with inetd or run telnetd manually) so it
listens on port 23.
2. From another machine, connect to the telnet port and complete the initial
handshake. When the server sends DO LINEMODE, reply with WILL LINEMODE so the
server enters LINEMODE negotiation.
3. Send a single LINEMODE SLC suboption containing at least 40 to 50 triplets,
each with a function code greater than 18 (e.g. 19, 20, 21, ... 68). Each
triplet is 3 bytes (func, flag, value). Use 0x00 for flag and value. The
suboption must be properly framed with IAC SB LINEMODE LM_SLC at the start and
IAC SE at the end.
4. The server will call add_slc() for each triplet. After about 35 triplets it
will write past the end of slcbuf. You should observe a crash, or (if you craft
the overflow) memory corruption and possibly code execution.
Credit Request
We kindly request that the following researchers be credited for this discovery:
Adiel Sol, Arad Inbar, Erez Cohen, Nir Somech, Ben Grinberg, Daniel Lubel -
DREAM Security Research Team
Best regards,
DREAM Security Research Team
Attachment: 13-16-27.mp4