Headline
CVE-2021-21901: TALOS-2021-1353 || Cisco Talos Intelligence Group
A stack-based buffer overflow vulnerability exists in the CMA check_udp_crc function of Garrett Metal Detectors’ iC Module CMA Version 5.0. A specially-crafted packet can lead to a stack-based buffer overflow during a call to memcpy. An attacker can send a malicious packet to trigger this vulnerability.
Summary
A stack-based buffer overflow vulnerability exists in the CMA check_udp_crc function of Garrett Metal Detectors’ iC Module CMA Version 5.0. A specially-crafted packet can lead to a stack-based buffer overflow during a call to memcpy
. An attacker can send a malicious packet to trigger this vulnerability.
Tested Versions
Garrett Metal Detectors iC Module CMA Version 5.0
Product URLs
https://garrett.com/security/walk-through/accessories
CVSSv3 Score
9.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE
CWE-120 - Buffer Copy without Checking Size of Input (‘Classic Buffer Overflow’)
Details
The Garrett iC Module provides network connectivity to either the Garrett PD 6500i or Garrett MZ 6100 models of walk-through metal detectors. This module enables a remote user to monitor statistics such as alarm and visitor counts in real time as well as make configuration changes to metal detectors.
The Garrett iC Module exposes a discovery service on UDP port 6877. The “CMA Connect” software, used to interact with the iC modules from a remote system, can broadcast a particularly formatted UDP packet onto the network and iC modules that receive this packet will reply with various descriptors such as MAC address, serial number, and location. A function call to memcpy within the CRC validation logic of these UDP packets is vulnerable to a stack-based buffer overflow.
This buffer overflow occurs due to a mismatch in maximum buffer sizes between the function responsible for handling incoming UDP packets, udp_thread
, and the function responsible for validating the message’s checksum, check_udp_crc
. When a UDP message is received inside of udp_thread
, a msghdr
struct is populated with an iovec
struct whose message
attribute points to a 512-byte long character array called msgbuf
.
.text:0001D730 SUB R3, R11, #-msgbuf [1] uint8_t msgbuf[512]
.text:0001D734 MOV R0, R3 ; s
.text:0001D738 MOV R1, #0 ; c
.text:0001D73C MOV R2, #0x200 ; n
.text:0001D740 BL memset [2] memset(msgbuf, 0, 512)
.text:0001D744 SUB R3, R11, #-msgbuf
.text:0001D748 STR R3, [R11,#iov] [3] iov[0].iov_base = msgbuf
.text:0001D74C MOV R3, #0x200
.text:0001D750 STR R3, [R11,#iov.iov_len] [4] iov[0].iov_len = 512
.text:0001D754 SUB R3, R11, #-(src_addr.__ss_padding+4)
.text:0001D758 SUB R3, R3, #0xC
.text:0001D75C STR R3, [R11,#message]
.text:0001D760 MOV R3, #0x80
.text:0001D764 STR R3, [R11,#message.msg_namelen]
.text:0001D768 SUB R3, R11, #-iov
.text:0001D76C STR R3, [R11,#message.msg_iov] [5] message.msg_iov = iov
.text:0001D770 MOV R3, #1
.text:0001D774 STR R3, [R11,#message.msg_iovlen] [6] message.msg_iovlen = 1
.text:0001D778 SUB R3, R11, #-(cmbuf+0xC)
.text:0001D77C SUB R3, R3, #0xC
.text:0001D780 STR R3, [R11,#message.msg_control]
.text:0001D784 MOV R3, #0x100
.text:0001D788 STR R3, [R11,#message.msg_controllen]
.text:0001D78C SUB R3, R11, #-message
.text:0001D790 LDR R0, [R11,#udpFd] ; fd
.text:0001D794 MOV R1, R3 ; message
.text:0001D798 MOV R2, #0 ; flags
.text:0001D79C BL recvmsg [7] recvmsg(udpFd, &message, 0);
After a successful call to recvmsg
, the udp_thread
function will null-terminate the msgbuf
buffer at the first instance of \r\n
. The msgbuf
buffer is then passed to check_udp_crc
to confirm the payload’s CRC is valid.
.text:0001D7C8 SUB R3, R11, #-msgbuf
.text:0001D7CC MOV R0, R3 ; s
.text:0001D7D0 LDR R1, =asc_2FCD8 ; "\r\n"
.text:0001D7D4 BL strcspn [8] $r0 = strcspn(msgbuf, "\r\n")
.text:0001D7D8 MOV R2, R0
.text:0001D7DC LDR R3, =0xFFFFFC8C
.text:0001D7E0 SUB R0, R11, #-var_C
.text:0001D7E4 ADD R2, R0, R2
.text:0001D7E8 ADD R3, R2, R3
.text:0001D7EC MOV R2, #0
.text:0001D7F0 STRB R2, [R3] [9] msgbuf[$r0] = '\0'
.text:0001D7F4 SUB R3, R11, #-msgbuf
.text:0001D7F8 MOV R0, R3 ; msg
.text:0001D7FC BL check_udp_crc [10] check_udp_crc(msgbuf)
While the maximum length of the UDP payload in udp_thread
is 512 bytes, the check_udp_crc
function only allocates 256 bytes for its internal copy of the payload. A memcpy
is executed which will copy strlen(msg)
bytes from msgbuf
into msg
, resulting in a very straightforward buffer overflow.
.text:0001D134 PUSH {R11,LR}
.text:0001D138 ADD R11, SP, #4
.text:0001D13C SUB SP, SP, #0x128
.text:0001D140 STR R0, [R11,#msg]
.text:0001D144 SUB R3, R11, #-msg_buf
.text:0001D148 MOV R0, R3 ; s
.text:0001D14C MOV R1, #0 ; c
.text:0001D150 MOV R2, #0x100 ; n
.text:0001D154 BL memset [11] memset(msg, 0, 256)
.text:0001D158 LDR R0, [R11,#msg] ; s
.text:0001D15C BL strlen
.text:0001D160 MOV R3, R0 [12] msglen = strlen(msg)
.text:0001D164 SUB R2, R11, #-msg_buf
.text:0001D168 MOV R0, R2 ; dest
.text:0001D16C LDR R1, [R11,#msg] ; src
.text:0001D170 MOV R2, R3 ; n
.text:0001D174 BL memcpy [13] memcpy(msg, msgbuf, msglen)
As shown above, a maliciously crafted UDP packet with a significantly long payload will result in a buffer overflow. This overflow directly leads to attacker control of the program counter, which may be seen in the debugger output below.
Crash Information
Thread 2 "cma" received signal SIGSEGV, Segmentation fault.
0x4d4d4d4c in ?? ()
──────────────────────────────────────────────────────────────────────────────────────────────── registers ────
$r0 : 0x1
$r1 : 0x3b
$r2 : 0x1
$r3 : 0x1
$r4 : 0x0
$r5 : 0xb636b6b0 → "eth0"
$r6 : 0x0
$r7 : 0x152
$r8 : 0xbefffbe0 → 0x00000000
$r9 : 0xb6ff86d0 → 0xb6ff8db8 → 0x00000001
$r10 : 0xb636c460 → 0x00000001
$r11 : 0x4d4d4d4d ("MMMM"?)
$r12 : 0xb636b6a8 → 0x00000000
$sp : 0xb636b6a0 → 0xb636b7d0 → 0x00000018
$lr : 0xb6bf7898 → <strrchr+40> cmp r0, #0
$pc : 0x4d4d4d4c ("LMMM"?)
$cpsr: [negative ZERO CARRY overflow interrupt fast THUMB]
─────────────────────────────────────────────────────────────────────────────────────────── code:arm:THUMB ────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x4d4d4d4c
───────────────────────────────────────────────────────────────────────────────────────────────────────────────
Timeline
2021-08-17 - Vendor Disclosure
2021-11-10 - Talos granted disclosure extension
2021-12-13 - Vendor patched
2021-12-15 - Talos tested patch
2021-12-20 - Public Release
Discovered by Matt Wiseman of Cisco Talos.