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
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
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:
Root Cause
dlt_message_header_flags_v2() receives a caller-provided output buffer:
However, when rendering DLTv2 tag names, it repeatedly appends data using memcpy() without validating the remaining capacity.
The attacker can control:
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:
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:
GDB shows that the saved return address of main() is overwritten with attacker-controlled bytes:
Impact
This issue affects DLTv2 rendering paths that call dlt_message_header_v2() or dlt_message_print_*_v2(), including: