Skip to content

Stack buffer overflow in dlt_message_header_flags_v2() #893

Description

@cryptcrack

Summary

A controllable buffer overflow exists in dlt_message_header_flags_v2() when rendering DLTv2 WTGS tag fields.

The function copies attacker-controlled tag names into a caller-provided output buffer without checking the remaining buffer size. A crafted DLTv2 file can overflow this buffer.

The strongest confirmed impact is in dlt-convert-v2, where the output buffer is stack-allocated, causing a crafted DLTv2 file to trigger a stack-buffer-overflow.

Vulnerable Code

if ((flags & DLT_HEADER_SHOW_TAG) == DLT_HEADER_SHOW_TAG) {
    if ((DLT_IS_HTYP2_WTGS(msg->baseheaderv2->htyp2)) &&
        (msg->extendedheaderv2.notg != 0)) {
        for(int i=0; i<msg->extendedheaderv2.notg; i++){
            memcpy(text + currtextlength,
                msg->extendedheaderv2.tag[i].tagname,
                (size_t)msg->extendedheaderv2.tag[i].taglen + 1);
            currtextlength = currtextlength +
                (int)msg->extendedheaderv2.tag[i].taglen;
            snprintf(text + currtextlength,
                textlength - (size_t)currtextlength,
                " ");
            currtextlength++;
        }
    }
}

The code does not check whether currtextlength is still within textlength.
It also does not check whether taglen + 1 fits into the remaining output buffer.

A similar unchecked copy exists for the WSFLN filename field:

memcpy(text + currtextlength,
    msg->extendedheaderv2.fina,
    (size_t)msg->extendedheaderv2.finalen);

Root Cause

dlt_message_header_flags_v2() receives a caller-provided output buffer:

char *text
size_t textlength

However, when rendering DLTv2 tag names, it repeatedly appends data using memcpy() without validating the remaining capacity.

The attacker can control:

- notg: number of tags
- taglen: length of each tag
- tagname: bytes copied into the output buffer

This allows the accumulated output length to exceed the caller-provided buffer.

Reproduction Summary

A crafted DLTv2 file containing many WTGS tags was used to reproduce the issue.

The issue was reproduced with:

./dlt-convert-v2 -s crafted_file.dlt

Source analysis shows that -x also reaches the same vulnerable header rendering path because dlt_message_print_hex_v2() calls dlt_message_header_v2() before printing the payload.

Observed Result

ASan reports:

=================================================================
==2562480==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f67c6b033c8 at pc 0x57727fec42a8 bp 0x7fff5d055f10 sp 0x7fff5d0556d0
WRITE of size 255 at 0x7f67c6b033c8 thread T0
    #0 0x57727fec42a7 in __asan_memcpy (root/dlt-daemon/latest/bin/dlt-convert-v2+0xdc2a7) (BuildId: be7e74a5111c1e11b95657cb62377d5f32b17940)
    #1 0x57727ff14279 in dlt_message_header_flags_v2 root/dlt-daemon/latest/code/src/shared/dlt_common.c:1318:17
    #2 0x57727ff05a8c in main root/dlt-daemon/latest/code/src/console/dlt-convert-v2.c:738:25
    #3 0x7f67c83b31c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    #4 0x7f67c83b328a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
    #5 0x57727fe2b514 in _start (root/dlt-daemon/latest/bin/dlt-convert-v2+0x43514) (BuildId: be7e74a5111c1e11b95657cb62377d5f32b17940)

Address 0x7f67c6b033c8 is located in stack of thread T0 at offset 13256 in frame
    #0 0x57727ff0490f in main root/dlt-daemon/latest/code/src/console/dlt-convert-v2.c:434

  This frame has 9 object(s):
    [32, 40) 'files.i' (line 334)
    [64, 208) 'st.i' (line 335)
    [272, 1296) 'tmp_filename.i' (line 336)
    [1424, 1880) 'file' (line 451)
    [1952, 3104) 'filter' (line 452)
    [3232, 13256) 'text' (line 458) <== Memory access at offset 13256 overflows this variable
    [13520, 14544) 'tmp_filename' (line 461)
    [14672, 14680) 'files' (line 464)
    [14704, 14720) 'req' (line 772)
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 (root/dlt-daemon/latest/bin/dlt-convert-v2+0xdc2a7) (BuildId: be7e74a5111c1e11b95657cb62377d5f32b17940) in __asan_memcpy
Shadow bytes around the buggy address:
  0x7f67c6b03100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f67c6b03180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f67c6b03200: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f67c6b03280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f67c6b03300: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7f67c6b03380: 00 00 00 00 00 00 00 00 00[f2]f2 f2 f2 f2 f2 f2
  0x7f67c6b03400: f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2
  0x7f67c6b03480: f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 00 00 00 00 00 00
  0x7f67c6b03500: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f67c6b03580: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x7f67c6b03600: 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
==2562480==ABORTING

GDB shows that the saved return address of main() is overwritten with attacker-controlled bytes:

(gdb) frame 2
#2  0x00005ffb5f474d87 in main (argc=3, argv=0x7ffc2aa759d8)
    at /root/dlt-daemon/latest/code/src/console/dlt-convert-v2.c:738
738                         if (dlt_message_header_v2(&file.msgv2, text,
(gdb) info frame
Stack level 2, frame at 0x7ffc2aa758c0:
 rip = 0x5ffb5f474d87 in main (/root/dlt-daemon/latest/code/src/console/dlt-convert-v2.c:738);
    saved rip = 0x4141414141414141
 caller of frame at 0x7ffc2aa72210
 source language c.
 Arglist at 0x7ffc2aa72210, args: argc=3, argv=0x7ffc2aa759d8
 Locals at 0x7ffc2aa72210, Previous frame's sp is 0x7ffc2aa758c0
 Saved registers:
  rbx at 0x7ffc2aa75888, rbp at 0x7ffc2aa758b0, r12 at 0x7ffc2aa75890, r13 at 0x7ffc2aa75898, r14 at 0x7ffc2aa758a0,
  r15 at 0x7ffc2aa758a8, rip at 0x7ffc2aa758b8
(gdb) p/x &text
$1 = 0x7ffc2aa73160```

The observed offset from the start of the stack buffer text to the saved return address was:

```text
&text     = 0x7ffc2aa73160
saved RIP = 0x7ffc2aa758b8
offset    = 0x2758 = 10072

Impact

This issue affects DLTv2 rendering paths that call dlt_message_header_v2() or dlt_message_print_*_v2(), including:

- dlt-convert-v2
- dlt-receive-v2
- dlt-control-v2
- dlt-daemon when DLTv2 mode and local console output are enabled

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions