Bug 270263 - telnet buffer overflow if server sends long TELQUAL_NAME for sra
Summary: telnet buffer overflow if server sends long TELQUAL_NAME for sra
Status: In Progress
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: CURRENT
Hardware: Any Any
: --- Affects Some People
Assignee: John Baldwin
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-03-16 10:35 UTC by Robert Morris
Modified: 2025-04-16 13:42 UTC (History)
4 users (show)

See Also:


Attachments
telnet server that overflows telnet's uprompt[] in sra_reply() (2.68 KB, text/plain)
2023-03-16 10:35 UTC, Robert Morris
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Robert Morris 2023-03-16 10:35:24 UTC
Created attachment 240895 [details]
telnet server that overflows telnet's uprompt[] in sra_reply()

telnet's auth_name() allows the name in TELQUAL_NAME to be up to 255
bytes long:

  auth_name(unsigned char *data, int cnt)
    unsigned char savename[256];
    if ((size_t)cnt > sizeof(savename) - 1) {
      error...
    auth_encrypt_user(savename)

auth_encrypt_user() copies the name to UserNameRequested.

But sra_reply() says:

        char uprompt[256],tuser[256];
        ...;
        sprintf(uprompt,"User (%s): ",UserNameRequested);

uprompt[] isn't guaranteed to be big enough, so sprintf can overflow uprompt[].

I've attached a demo telnet server. You may have to re-compile libtelnet
and telnet with -fsanitize=address to reliably see a problem:

# cc telnet17d.c
# ./a.out
listening...

And in another window:

# telnet localhost
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
=================================================================
==34863==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffffffdfa0 at pc 0x0000010a77e3 bp 0x7fffffffcfe0 sp 0x7fffffffc7a8
WRITE of size 252 at 0x7fffffffdfa0 thread T0
    #0 0x10a77e2 in memcpy /usr/src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc:899:5
    #1 0x80173809d in __sfvwrite /usr/src/lib/libc/stdio/fvwrite.c:132:6
    #2 0x801740c5b in __sprint /usr/src/lib/libc/stdio/vfprintf.c:166:8
    #3 0x801740c5b in io_flush /usr/src/lib/libc/stdio/printfcommon.h:157:10
    #4 0x801740c5b in __vfprintf /usr/src/lib/libc/stdio/vfprintf.c:1033:3
    #5 0x80174910d in vsprintf_l /usr/src/lib/libc/stdio/vsprintf.c:62:8
    #6 0x80174910d in vsprintf /usr/src/lib/libc/stdio/vsprintf.c:69:9
    #7 0x10aeac2 in vsprintf /usr/src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc:1765:1
    #8 0x10af2c6 in sprintf /usr/src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc:1808:1
    #9 0x1150c70 in sra_reply /usr/src/contrib/telnet/libtelnet/sra.c:273:3
    #10 0x113ed83 in suboption /usr/src/contrib/telnet/telnet/telnet.c:944:4
    #11 0x113d521 in telrcv /usr/src/contrib/telnet/telnet/telnet.c:1874:7
    #12 0x113fc5e in Scheduler /usr/src/contrib/telnet/telnet/telnet.c:2098:17
    #13 0x113f2d9 in telnet /usr/src/contrib/telnet/telnet/telnet.c:2163:6
    #14 0x112c65a in tn /usr/src/contrib/telnet/telnet/commands.c:2497:5
    #15 0x113448a in main /usr/src/contrib/telnet/telnet/main.c:374:7

Address 0x7fffffffdfa0 is located in stack of thread T0 at offset 288 in frame
    #0 0x11508ef in sra_reply /usr/src/contrib/telnet/libtelnet/sra.c:247

  This frame has 3 object(s):
    [32, 288) 'uprompt' (line 248)
    [352, 608) 'tuser' (line 248) <== Memory access at offset 288 partially underflows this variable
    [672, 688) 'skey' (line 249)
Comment 1 John Baldwin freebsd_committer freebsd_triage 2025-04-14 22:04:01 UTC
Reproduced on CHERI Morello:

Core was generated by `telnet localhost'.
Program terminated with signal SIGPROT, CHERI protection violation.
Capability bounds fault.
#0  0x0000000041124c64 in memcpy (
    dst0=0xfffffff7f8e0 [rwRW,0xfffffff7f7e0-0xfffffff7f8e0], 
    src0=<optimized out>, length=3)
    at /usr/home/john/work/git/cheribsd/lib/libc/string/bcopy.c:142
142				TLOOP1(*--dst = *--src);
(gdb) where
#0  0x0000000041124c64 in memcpy (
    dst0=0xfffffff7f8e0 [rwRW,0xfffffff7f7e0-0xfffffff7f8e0], 
    src0=<optimized out>, length=3)
    at /usr/home/john/work/git/cheribsd/lib/libc/string/bcopy.c:142
#1  0x0000000040a763a4 in __sfvwrite (
    fp=0xfffffff7f440 [rwRW,0xfffffff7f440-0xfffffff7f610], 
    uio=0xfffffff7e9a0 [rwRW,0xfffffff7e990-0xfffffff7eac0])
    at /usr/home/john/work/git/cheribsd/lib/libc/stdio/fvwrite.c:124
#2  0x0000000040b80d1c in __sprint (
    fp=0xfffffff7f8e0 [rwRW,0xfffffff7f7e0-0xfffffff7f8e0], 
    uio=0xfffffff7e9a0 [rwRW,0xfffffff7e990-0xfffffff7eac0], 
    locale=<optimized out>)
    at /usr/home/john/work/git/cheribsd/lib/libc/stdio/vfprintf.c:177
#3  io_flush (iop=0xfffffff7e990, locale=<optimized out>)
    at /usr/home/john/work/git/cheribsd/lib/libc/stdio/printfcommon.h:168
#4  __vfprintf (fp=0xfffffff7f440 [rwRW,0xfffffff7f440-0xfffffff7f610], 
    locale=0x40afd3e0 <__xlocale_global_locale> [rwRWE,0x40afd3e0-0x40afd540], 
    serrno=0, fmt0=0x112276 [rR,0x112276-0x112282] "User (%s): ", 
    ap=<optimized out>)
    at /usr/home/john/work/git/cheribsd/lib/libc/stdio/vfprintf.c:1147
#5  0x0000000040b87c40 in vsprintf_l (
    locale=0x40afd3e0 <__xlocale_global_locale> [rwRWE,0x40afd3e0-0x40afd540], 
    str=<optimized out>, fmt=<optimized out>, ap=<optimized out>)
    at /usr/home/john/work/git/cheribsd/lib/libc/stdio/vsprintf.c:60
#6  vsprintf (
    str=0xfffffff7f7e0 [rwRW,0xfffffff7f7e0-0xfffffff7f8e0] "User (\00601111110111111", '0' <repeats 179 times>..., 
    fmt=0x112276 [rR,0x112276-0x112282] "User (%s): ", 
    ap=0xfffffff7f6d0 [rRW,0xfffffff7f6d0-0xfffffff7f6e0])
    at /usr/home/john/work/git/cheribsd/lib/libc/stdio/vsprintf.c:67
#7  0x0000000040b7de2c in sprintf (
    str=0xfffffff7f8e0 [rwRW,0xfffffff7f7e0-0xfffffff7f8e0] "`\371\367\377\377\377", fmt=0x11227e [rR,0x112276-0x112282] "): ")
    at /usr/home/john/work/git/cheribsd/lib/libc/stdio/sprintf.c:55
#8  0x000000000013cb94 in sra_reply (
    ap=0x169720 <authenticators+224> [rwRW,0x169640-0x169800], 
    data=<optimized out>, cnt=<optimized out>)
    at /usr/home/john/work/git/cheribsd/contrib/telnet/libtelnet/sra.c:269
#9  0x0000000000132e80 in telrcv ()
    at /usr/home/john/work/git/cheribsd/contrib/telnet/telnet/telnet.c:1872
#10 0x00000000001348e0 in Scheduler (block=<optimized out>)
    at /usr/home/john/work/git/cheribsd/contrib/telnet/telnet/telnet.c:2096
#11 0x0000000000134348 in telnet (user=<optimized out>)
    at /usr/home/john/work/git/cheribsd/contrib/telnet/telnet/telnet.c:2161
#12 0x000000000012bc48 in tn (argc=<optimized out>, argv=<optimized out>)
    at /usr/home/john/work/git/cheribsd/contrib/telnet/telnet/commands.c:2506
#13 0x000000000012f274 in main (argc=-530016, argv=<optimized out>)
    at /usr/home/john/work/git/cheribsd/contrib/telnet/telnet/main.c:372
Comment 2 John Baldwin freebsd_committer freebsd_triage 2025-04-14 22:41:55 UTC
I tested two fixes, in the first, I kept uprompt[] the same size and just aborted if the prompt size was too long.  In the second case, I added 10 chars to uprompt so that the prompt fits.  Output from the cases below:

First:

$ telnet localhost
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Connection closed by foreign host.

Second:

$ telnet localhost
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
User (011111101111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111100): 
000000Connection closed by foreign host.
$
Comment 3 John Baldwin freebsd_committer freebsd_triage 2025-04-15 00:21:47 UTC
Patch at https://reviews.freebsd.org/D49832
Comment 4 commit-hook freebsd_committer freebsd_triage 2025-04-16 13:42:26 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=5737c2ae06e143e49496df2ab5a64f76d5456012

commit 5737c2ae06e143e49496df2ab5a64f76d5456012
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2025-04-16 13:41:03 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2025-04-16 13:41:03 +0000

    telnet: Prevent buffer overflow in the user prompt for SRA

    The Secure RPC authenticator for telnet prompts the local user for the
    username to use for authentication.  Previously it was using sprintf()
    into a buffer of 256 bytes, but the username received over the wire
    can be up to 255 bytes long which would overflow the prompt buffer.
    Fix this in two ways: First, use snprintf() and check for overflow.
    If the prompt buffer overflows, fail authentication without prompting
    the user.  Second, add 10 bytes to the buffer size to account for the
    overhead of the prompt so that a maximally sized username fits.

    While here, replace a bare 255 in the subsequent telnet_gets call with
    an expression using sizeof() the relevant buffer.

    PR:             270263
    Reported by:    Robert Morris <rtm@lcs.mit.edu>
    Tested on:      CHERI
    Reviewed by:    emaste
    Differential Revision:  https://reviews.freebsd.org/D49832

 contrib/telnet/libtelnet/sra.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)