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)
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
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. $
Patch at https://reviews.freebsd.org/D49832
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(-)