Stack Overflow in SIP Header Processing

Apr 4, 2024

CVE Number

CVE-2024-3119

Credits

Huascar Tejeda of Pentraze Cybersecurity

Summary

A buffer overflow vulnerability exists in sngrep’s processing of Call-ID and X-Call-ID SIP headers, enabling the remote execution of arbitrary code or causing a denial of service through specially crafted SIP messages.

CVSS

9.0 (Critical) - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H

Details

The vulnerability arises from improper handling of Call-ID and X-Call-ID SIP headers within the sip_get_callid and sip_get_xcallid functions in sip.c. Specifically, the strncpy function is utilized to copy the contents of these headers into fixed-size local buffers without verifying that the data length does not exceed buffer limits. This issue is apparent in the lines:

242             strncpy(callid, payload + pmatch[2].rm_so, (int) pmatch[2].rm_eo - pmatch[2].rm_so);
...
255             strncpy(xcallid, payload + pmatch[2].rm_so, (int) pmatch[2].rm_eo - pmatch[2].rm_so);

These operations attempt to copy the whole of the “Call-ID” or “X-Call-ID” from a SIP message payload into respective buffers, each allocated 1024 bytes, without prior validation against the buffer’s capacity. This can lead to a buffer overflow condition if an oversized header is processed.

The primary concern is the lack of bounds checking before the strncpy operation, and strncpy’s failure to ensure null-termination, potentially leading to further vulnerabilities.

Steps to reproduce:

  1. Start sngrep in terminal mode: $ sudo sngrep -T
  2. Send a crafted SIP message with an oversized Call-ID or X-Call-ID header over the network.

Sample triggers:

  1. Call-ID:
   INVITE sip:[email protected] SIP/2.0\r\n
   Via: SIP/2.0/TCP 10.1.0.2:5060;branch=z9hG4bK-123456\r\n"
   Call-ID: A*2080\r\n
   Content-Length: 0\r\n\r\n
  1. X-Call-ID:
  INVITE sip:[email protected] SIP/2.0\r\n
  Via: SIP/2.0/TCP 10.1.0.2:5060;branch=z9hG4bK-123456\r\n"
  X-Call-ID: A*2080\r\n
  Content-Length: 0\r\n\r\n

Proposed Fix:

I’ve developed a patch that introduces bounds checking and ensures proper null-termination of strings, preventing the input size from exceeding the allocated buffer sizes for “Call-ID” and “X-Call-ID”. To facilitate an easy review, I’ve forked the repository, applied the patch, and pushed the changes to my repo. The commit with the proposed fix can be viewed here: https://github.com/htejeda/sngrep/commit/73c15c82d14c69df311e05fa75da734faafd365f.

ASAN

$ sudo ./src/sngrep -T
Dialog count: 0=================================================================
==3678432==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffff40738a0 at pc 0x55555565157a bp 0x7ffff4afe7b0 sp 0x7ffff4afdf70
WRITE of size 1030 at 0x7ffff40738a0 thread T1
    #0 0x555555651579 in strncpy (/home/htejeda/sngrep/src/sngrep+0xfd579) (BuildId: 62e1f4d43b909825)
    #1 0x5555556b6f08 in sip_get_xcallid /home/htejeda/sngrep/src/sip.c:255:9
    #2 0x5555556b82ed in sip_check_packet /home/htejeda/sngrep/src/sip.c:381:9
    #3 0x5555556ae86b in capture_packet_parse /home/htejeda/sngrep/src/capture.c:988:13
    #4 0x5555556a8a93 in parse_packet /home/htejeda/sngrep/src/capture.c:455:9
    #5 0x7ffff7eddc28 in pcap_handle_packet_mmap /build/libpcap-bgtaqR/libpcap-1.10.1/./pcap-linux.c:3978:2
    #6 0x7ffff7ede1c3 in pcap_read_linux_mmap_v3 /build/libpcap-bgtaqR/libpcap-1.10.1/./pcap-linux.c:4128:10
    #7 0x7ffff7ee207d in pcap_loop /build/libpcap-bgtaqR/libpcap-1.10.1/./pcap.c:2904:9
    #8 0x5555556a6e46 in capture_thread /home/htejeda/sngrep/src/capture.c:1069:5
    #9 0x7ffff7a94ac2 in start_thread nptl/pthread_create.c:442:8
    #10 0x7ffff7b2684f  misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

Address 0x7ffff40738a0 is located in stack of thread T1 at offset 2208 in frame
    #0 0x5555556b7bcf in sip_check_packet /home/htejeda/sngrep/src/sip.c:328

  This frame has 3 object(s):
    [32, 1056) 'callid' (line 331)
    [1184, 2208) 'xcallid' (line 331)
    [2336, 12576) 'payload' (line 332) <== Memory access at offset 2208 partially underflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
      (longjmp and C++ exceptions *are* supported)
Thread T1 created by T0 here:
    #0 0x55555564ec0a in pthread_create (/home/htejeda/sngrep/src/sngrep+0xfac0a) (BuildId: 62e1f4d43b909825)
    #1 0x5555556b07f2 in capture_launch_thread /home/htejeda/sngrep/src/capture.c:1054:13
    #2 0x5555556ca643 in main /home/htejeda/sngrep/src/main.c:451:9
    #3 0x7ffff7a29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/htejeda/sngrep/src/sngrep+0xfd579) (BuildId: 62e1f4d43b909825) in strncpy
Shadow bytes around the buggy address:
  0x7ffff4073600: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff4073680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff4073700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff4073780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff4073800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7ffff4073880: 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
  0x7ffff4073900: f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff4073980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff4073a00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff4073a80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7ffff4073b00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==3678432==ABORTING

GDB Backtrace

(gdb) bt
#0  0x00007ffff7c969fc in __pthread_kill_implementation (/lib/x86_64-linux-gnu/libc.so.6)
                       at ./nptl/pthread_kill.c:44

#1  0x00007ffff7c969fc in __pthread_kill_internal (/lib/x86_64-linux-gnu/libc.so.6)
                       at ./nptl/pthread_kill.c:78

#2  0x00007ffff7c969fc in __GI___pthread_kill (/lib/x86_64-linux-gnu/libc.so.6)
                       at ./nptl/pthread_kill.c:89

#3  0x00007ffff7c42476 in __GI_raise (/lib/x86_64-linux-gnu/libc.so.6)
                       at ../sysdeps/posix/raise.c:26

#4  0x00007ffff7c287f3 in __GI_abort (/lib/x86_64-linux-gnu/libc.so.6)
                       at ./stdlib/abort.c:79

#5  0x00007ffff7c89676 in __libc_message (/lib/x86_64-linux-gnu/libc.so.6)
                       at ../sysdeps/posix/libc_fatal.c:155

#6  0x00007ffff7d3659a in __GI___fortify_fail (/lib/x86_64-linux-gnu/libc.so.6)
                       at ./debug/fortify_fail.c:26

#7  0x00007ffff7d34f16 in __GI___chk_fail (/lib/x86_64-linux-gnu/libc.so.6)
                       at ./debug/chk_fail.c:28

#8  0x00007ffff7d34959 in __strncpy_chk (/lib/x86_64-linux-gnu/libc.so.6)
                       at ./debug/strncpy_chk.c:26

#9  0x00005555555615ce in strncpy (/home/htejeda/sngrep/src/sngrep)
                       ??: char strncpy(__len = (size_t)<optimized out>, __src = (const char * restrict)<optimized out>, __dest = (char * restrict)0x7ffff7886c60 "") {
                       ||:
                       ||: /* Local reference: size_t __len = <optimized out>; */
                       ||: /* Local reference: char * restrict __dest = 0x7ffff7886c60 ""; */
                       ||: /* Local reference: const char * restrict __src = <optimized out>; */
                       93:              size_t __len))
                       94: {
                       95:   return __builtin___strncpy_chk (__dest, __src, __len,
                       ||:
                       --: }
                       at /usr/include/x86_64-linux-gnu/bits/string_fortified.h:95

#10 0x00005555555615ce in sip_get_callid (/home/htejeda/sngrep/src/sngrep)
                       ???: char sip_get_callid(callid = (char *)0x7ffff7886c60 "", payload = (const char *)0x7ffff7887460 "INVITE sip:[email protected] SIP/2.0\r\nVia: SIP/2.0/TCP 10.1.0.2:5060;branch=z9hG4bK-123456\r\nX-Call-ID: ", 'A' <repeats 92 times>...) {
                       |||:
                       |||: /* Local reference: u_char [10240] payload = "INVITE sip:[email protected] SIP/2.0\r\nVia: SIP/2.0/TCP 10.1.0.2:5060;branch=z9hG4bK-123456\r\nX-Call-ID: ", 'A' <repeats 2056 times>...; */
                       |||: /* Local reference: regmatch_t [3] pmatch = {{rm_so = 2190, rm_eo = 4280}, {rm_so = 2190, rm_eo = 2197}, {rm_so = 2199, rm_eo = 4279}}; */
                       |||: /* Local reference: char [1024] callid = '\000' <repeats 1023 times>; */
                       240:     if (regexec(&calls.reg_callid, payload, 3, pmatch, 0) == 0) {
                       241:         // Copy the matching part of payload
                       242:         strncpy(callid, payload + pmatch[2].rm_so, (int) pmatch[2].rm_eo - pmatch[2].rm_so);
                       |||:
                       ---: }
                       at sip.c:242

#11 0x00005555555615ce in sip_check_packet (/home/htejeda/sngrep/src/sngrep)
                       327: sip_msg_t sip_check_packet(packet = (packet_t *)0x7ffff0000e70) {
                       |||:
                       |||: /* Local reference: u_char [10240] payload = "INVITE sip:[email protected] SIP/2.0\r\nVia: SIP/2.0/TCP 10.1.0.2:5060;branch=z9hG4bK-123456\r\nX-Call-ID: ", 'A' <repeats 2056 times>...; */
                       |||: /* Local reference: char [1024] callid = '\000' <repeats 1023 times>; */
                       346:
                       347:     // Get the Call-ID of this message
                       348:     if (!sip_get_callid((const char*) payload, callid))
                       |||:
                       ---: }
                       at sip.c:348

#12 0x000055555555e1f3 in capture_packet_parse (/home/htejeda/sngrep/src/sngrep)
                       980: int capture_packet_parse(packet = (packet_t *)0x7ffff0000e70) {
                       |||:
                       |||: /* Local reference: packet_t * packet = 0x7ffff0000e70; */
                       986:     if (packet_payloadlen(packet)) {
                       987:         // Parse this header and payload
                       988:         if (sip_check_packet(packet)) {
                       |||:
                       ---: }
                       at capture.c:988

#13 0x000055555555f298 in parse_packet (/home/htejeda/sngrep/src/sngrep)
                       320: void parse_packet(info = (u_char *)0x5555555a9700 "\001\001", header = (const struct pcap_pkthdr *)0x7ffff788ed40, packet = (const u_char *)<optimized out>) {
                       |||:
                       |||: /* Local reference: const u_char * packet = <optimized out>; */
                       |||: /* Local reference: packet_t * pkt = 0x7ffff0000e70; */
                       453:     capture_lock();
                       454:     // Check if we can handle this packet
                       455:     if (capture_packet_parse(pkt) == 0) {
                       |||:
                       ---: }
                       at capture.c:455

#14 0x00007ffff7f00c54 in pcap_offline_read (/lib/x86_64-linux-gnu/libpcap.so.0.8)
                       at ./savefile.c:654

#15 0x00007ffff7ee2048 in pcap_loop (/lib/x86_64-linux-gnu/libpcap.so.0.8)
                       at ./pcap.c:2897

#16 0x000055555555d410 in capture_thread (/home/htejeda/sngrep/src/sngrep)
                       1064: void capture_thread(info = (void *)0x5555555a9700) {
                       ||||:
                       ||||: /* Local reference: capture_info_t * capinfo = 0x5555555a9700; */
                       1067:
                       1068:     // Parse available packets
                       1069:     pcap_loop(capinfo->handle, -1, parse_packet, (u_char *) capinfo);
                       ||||:
                       ----: }
                       at capture.c:1069

#17 0x00007ffff7c94ac3 in start_thread (/lib/x86_64-linux-gnu/libc.so.6)
                       at ./nptl/pthread_create.c:442

#18 0x00007ffff7d26850 in clone3 (/lib/x86_64-linux-gnu/libc.so.6)
                       at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

Crash context:
Execution stopped here ==> 0x00007ffff7c969fc: mov    r13d,eax

Register info:
   rax - 0x0000000000000000 (0)
   rbx - 0x00007ffff788f640 (140737346336320)
   rcx - 0x00007ffff7c969fc (140737350560252)
   rdx - 0x0000000000000006 (6)
   rsi - 0x000000000008eb2b (584491)
   rdi - 0x000000000008e556 (582998)
   rbp - 0x000000000008eb2b (0x8eb2b)
   rsp - 0x00007ffff78868a0 (0x7ffff78868a0)
    r8 - 0x00007ffff7886970 (140737346300272)
    r9 - 0x0000000000000000 (0)
   r10 - 0x0000000000000008 (8)
   r11 - 0x0000000000000246 (582)
   r12 - 0x0000000000000006 (6)
   r13 - 0x0000000000000016 (22)
   r14 - 0x0000000000000002 (2)
   r15 - 0x0000000000000001 (1)
   rip - 0x00007ffff7c969fc (0x7ffff7c969fc <__GI___pthread_kill+300>)
eflags - 0x00000246 ([ PF ZF IF ])
    cs - 0x00000033 (51)
    ss - 0x0000002b (43)
    ds - 0x00000000 (0)
    es - 0x00000000 (0)
    fs - 0x00000000 (0)
    gs - 0x00000000 (0)

¿Ver el sitio en español?