Talos Vulnerability Report

TALOS-2022-1681

Weston Embedded uC-FTPs PORT command parameter extraction out-of-bounds read vulnerability

May 10, 2023
CVE Number

CVE-2022-46377,CVE-2022-46378

SUMMARY

An out-of-bounds read vulnerability exists in the PORT command parameter extraction functionality of Weston Embedded uC-FTPs v 1.98.00. A specially-crafted set of network packets can lead to denial of service. An attacker can send packets to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Weston Embedded uC-FTPs v 1.98.00

PRODUCT URLS

uC-FTPs - https://weston-embedded.com/micrium/overview

CVSSv3 SCORE

6.5 - CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H

CWE

CWE-823 - Use of Out-of-range Pointer Offset

DETAILS

uC-FTPs is Micrium’s FTP server. It is designed to be used on embedded systems running the µC/OS-II or µC/OS-III RTOS kernels. This FTP server implements part of the following RFCs: RFC 959, RFC 2389, Draft IETF.

When an authenticated user sends the PORT command to uC-FTPs with no arguments, the server attempts to find an ascii digit within the network buffer when extracting the ip address argument and the port argument. If no ascii digit is found, it will infinitely increment the pointer until the pointer accesses unmapped memory. This bug results in a denial of service in any case where unmapped memory is accessed prior to an ascii digit being read from memory.

CVE-2022-46377 - IP address parsing

The portion of the code responsible for extracting the IP address from the PORT command expects to find an ascii digit within the network buffer.

for (i = 0; i <= 3; i++) {
                    dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
                    while ((dig == DEF_NO) && (ftp_session->CtrlCmdArgs != (CPU_CHAR *)0)) {
                        ftp_session->CtrlCmdArgs++;
                        dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
                    }
                    p_addr[i] = Str_ParseNbr_Int32U(ftp_session->CtrlCmdArgs, &ftp_session->CtrlCmdArgs, 10);
                }

Crash Information

Thread 3 "app" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff759d700 (LWP 8653)]
FTPs_ProcessCtrlCmd (ftp_session=0x7ffff759cb70) at Source/ftp-s.c:1913
1913	                         dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
(gdb) i r
rax            0x555555588000      93824992444416
rbx            0x0                 0
rcx            0xe6                230
rdx            0x555555588000      93824992444416
rsi            0x7ffff759cb78      140737343245176
rdi            0x0                 0
rbp            0x7ffff759cb10      0x7ffff759cb10
rsp            0x7ffff759ca80      0x7ffff759ca80
r8             0x0                 0
r9             0xf                 15
r10            0x55555555f95d      93824992278877
r11            0x0                 0
r12            0x7ffff7d9ddde      140737351638494
r13            0x7ffff7d9dddf      140737351638495
r14            0x7ffff7d9dde0      140737351638496
r15            0x7ffff759cfc0      140737343246272
rip            0x5555555584c7      0x5555555584c7 <FTPs_ProcessCtrlCmd+1958>
eflags         0x10206             [ PF IF RF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
k0             0x0                 0
k1             0x0                 0
k2             0x0                 0
k3             0x0                 0
k4             0x0                 0
k5             0x0                 0
k6             0x0                 0
k7             0x0                 0
(gdb) bt
#0  FTPs_ProcessCtrlCmd (ftp_session=0x7ffff759cb70) at Source/ftp-s.c:1913
#1  0x00005555555573ff in FTPs_CtrlTask (p_arg=0x7ffff7d9dec0) at Source/ftp-s.c:1007
#2  0x00007ffff7f9c609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#3  0x00007ffff7ec1133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Mitigation

The while loop responsible for incrementing the ftp_session->CtrlCmdArgs should also terminate in the case where a NULL byte is read from the network buffer. The network buffer is guaranteed to contain a NULL byte because the caller FTPs_CtrlTask explicitly places one at the end of received network data. Example bugfix below:

for (i = 0; i <= 3; i++) {
                    dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
                    while ((dig == DEF_NO) && (ftp_session->CtrlCmdArgs != (CPU_CHAR *)0) && (*ftp_session->CtrlCmdArgs != 0)) {
                        ftp_session->CtrlCmdArgs++;
                        dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
                    }
                    p_addr[i] = Str_ParseNbr_Int32U(ftp_session->CtrlCmdArgs, &ftp_session->CtrlCmdArgs, 10);
                }

CVE-2022-46378 - Port parsing

The portion of the code responsible for extracting the port number from the PORT command expects to find an ascii digit within the network buffer.

for (i = 0; i <= 1; i++) {
                    dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
                    while ((dig == DEF_NO) && (ftp_session->CtrlCmdArgs != 0)) {
                        ftp_session->CtrlCmdArgs++;
                        dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
                    }
                    p_port[i] = Str_ParseNbr_Int32U(ftp_session->CtrlCmdArgs, &ftp_session->CtrlCmdArgs, 10);
                }

Crash Information

Core was generated by `./app'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  FTPs_ProcessCtrlCmd (ftp_session=0x7f19f6bf1b70) at Source/ftp-s.c:1921
1921	                         dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
[Current thread is 1 (Thread 0x7f19f6bf2700 (LWP 8975))]
(gdb) i r
rax            0x56184ef03000      94662403567616
rbx            0x0                 0
rcx            0xe6                230
rdx            0x56184ef03000      94662403567616
rsi            0x7f19f6bf1b78      139749490629496
rdi            0x0                 0
rbp            0x7f19f6bf1b10      0x7f19f6bf1b10
rsp            0x7f19f6bf1a80      0x7f19f6bf1a80
r8             0x0                 0
r9             0x19                25
r10            0x56184eefb95d      94662403537245
r11            0x0                 0
r12            0x7f19f73f2dde      139749499022814
r13            0x7f19f73f2ddf      139749499022815
r14            0x7f19f73f2de0      139749499022816
r15            0x7f19f6bf1fc0      139749490630592
rip            0x56184eef457f      0x56184eef457f <FTPs_ProcessCtrlCmd+2142>
eflags         0x10206             [ PF IF RF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
k0             0x0                 0
k1             0x0                 0
k2             0x0                 0
k3             0x0                 0
k4             0x0                 0
k5             0x0                 0
k6             0x0                 0
k7             0x0                 0
(gdb) bt
#0  FTPs_ProcessCtrlCmd (ftp_session=0x7f19f6bf1b70) at Source/ftp-s.c:1921
#1  0x000056184eef33ff in FTPs_CtrlTask (p_arg=0x7f19f73f2ec0) at Source/ftp-s.c:1007
#2  0x00007f19f75f1609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#3  0x00007f19f7516133 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Mitigation

The while loop responsible for incrementing the ftp_session->CtrlCmdArgs should also terminate in the case where a NULL byte is read from the network buffer. The network buffer is guaranteed to contain a NULL byte because the caller FTPs_CtrlTask explicitly places one at the end of received network data. Example bugfix below:

for (i = 0; i <= 1; i++) {
                    dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
                    while ((dig == DEF_NO) && (ftp_session->CtrlCmdArgs != 0) && (*ftp_session->CtrlCmdArgs != 0)) {
                        ftp_session->CtrlCmdArgs++;
                        dig = ASCII_IsDig(*ftp_session->CtrlCmdArgs);
                    }
                    p_port[i] = Str_ParseNbr_Int32U(ftp_session->CtrlCmdArgs, &ftp_session->CtrlCmdArgs, 10);
                }
TIMELINE

2022-12-13 - Vendor Disclosure
2023-02-24 - Vendor Patch Release
2023-05-10 - Public Release

Credit

Discovered by Kelly Leuschner of Cisco Talos.