Heap based buffer Over-read vulnerability in dlt_en10mb_encode() - tcpreplay 4.3
Loginsoft-2018-17974
October 3, 2018
CVE Number
CVE-2018-17974
CWE
CWE-126: Buffer Over-read
Product Details
Tcpreplay is a suite of free Open Source utilities for editing and replaying previously captured network traffic.
URL:https://tcpreplay.appneta.com/
Vulnerable Versions
4.3 branch
Vulnerability Details
An issue was discovered in Tcpreplay 4.3.0 beta1. A heap-based buffer over-read was triggered in the function dlt_en10mb_encode() of the file plugins/dlt_en10mb/en10mb.c, due to inappropriate values in the function memmove(). The length (pktlen + ctx -> l2len) can be larger than source value (packet + ctx->l2len) because the function fails to ensure the length of a packet is valid. This leads to Denial of Service.
SYNOPSIS
Another interesting package on our hit list wastcpreplay, which consists of manybinaries. This report recites about a heap overflow vulnerability discovered in the `tcpreplay-edit` binary, which includes all the functionality of bothtcpreplayandtcprewrite.An heap overflow was triggered in function `dlt_en10mb_encode()` at file `en10mb.c`, due to inappropriate values inmemmove()[1].The workflow1. Packet is read2. Packet is passed to source DLT (Data link type) decoder3. Decoder fills out tcpedit internal data structure (tcpeditdlt_t)4. tcpeditdlt_t is passed to destination DLT encoder5. DLT Decoder returns new L2 header6. Packet is rebuilt7. L3 and above is processed8. Packet is writtenRef:http://tcpreplay.synfin.net/wiki/tcpeditDesign
Root cause analysis
```
/* rewrite Layer 2 */
if ((pktlen = tcpedit_dlt_process(tcpedit->dlt_ctx, pktdata, (*pkthdr)->caplen, direction)) == TCPEDIT_ERROR) [2]
errx(-1, "%s", tcpedit_geterr(tcpedit));
```
```
int
tcpedit_dlt_decode(tcpeditdlt_t *ctx, const u_char *packet, const int pktlen) [3]
{
return ctx->decoder->plugin_decode(ctx, packet, pktlen);
}
```
```
int
tcpedit_dlt_encode(tcpeditdlt_t* ctx, u_char *packet, int pktlen, tcpr_dir_t direction) [4]
{
return ctx->encoder->plugin_encode(ctx, packet, pktlen, direction);
}
```
```
if (newl2len != ctx->l2len)
memmove(packet + newl2len, packet + ctx->l2len, pktlen - ctx->l2len); [5]
/* update the total packet length */
pktlen += newl2len - ctx->l2len;
```
The function tcpedit_packet()
Processes a given packet and edits the pkthdr/pktdata structures according to the rules specified in tcpedit. It then calls tcpedit_dlt_process()
, which is responsible for rewriting the Layer 2 headers (edit) & returns the new total packet length [2]. This helps in resending the traffic to a number of forwarding devices.Now the packet is passed to source DLT decoder [3], which fills out tcpedit internal data structure (tcpeditdlt_t) & then is passed to destination DLT encoder tcpedit_dlt_encode() [4], which then calls dlt_en10mb_encode(), where our issue gets triggered while creating space for the new L2 header calling memmove() [5]. The memmove utilized the old l2 length to create the new layer 2 header, of length pktlen received from pkthdr->len.
Analysis
-----------------------------------------------------------------------------------------------------------------------------------------------[ code:i386 ]----
0x8079355 mov eax, DWORD PTR [eax+0x28]
0x8079358 cmp eax, DWORD PTR [ebp-0x44]
0x807935b je 0x807938f
-> 0x807935d mov eax, DWORD PTR [ebp+0x8]
0x8079360 mov eax, DWORD PTR [eax+0x28]
0x8079363 mov edx, DWORD PTR [ebp+0x10]
0x8079366 sub edx, eax
0x8079368 mov eax, edx
0x807936a mov ebx, eax
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------[ source:./plugins/dlt_en10mb/en10mb.c+488 ]----
483 return TCPEDIT_ERROR;
484 }
485
486 /* Make space for our new L2 header */
487 if (newl2len != ctx->l2len)
// ctx=0xbfffe6c0 -> [...] -> 0x00000001, packet=0xbfffe6c4 -> [...] -> 0xbfb32500, pktlen=0xbeL, newl2len=0x12L
-> 488 memmove(packet + newl2len, packet + ctx->l2len, pktlen - ctx->l2len); // Buffer overflow
489
490 /* update the total packet length */
491 pktlen += newl2len - ctx->l2len;
492
493 /* always set the src & dst address as the first 12 bytes */
-------[ source:./plugins/dlt_en10mb/en10mb.c+488 ]----
[#0] 0x807935d->Name: dlt_en10mb_encode(ctx=0xb4c01910, packet=0xb6001640 "", pktlen=0xbe, dir=TCPR_DIR_C2S)
[#1] 0x8075218->Name: tcpedit_dlt_encode(ctx=0xb4c01910, packet=0xb6001640 "", pktlen=0xbe, direction=TCPR_DIR_C2S)
[#2] 0x8074857->Name: tcpedit_dlt_process(ctx=0xb4c01910, packet=0xbfffe940, pktlen=0xbe, direction=TCPR_DIR_C2S)
[#3] 0x80655dd->Name: tcpedit_packet(tcpedit=0xb6402880, pkthdr=0xbfffe9c0, pktdata=0xbfffe940, direction=TCPR_DIR_C2S)
[#4] 0x805158b->Name: send_packets(ctx=0xb6403280, pcap=0xb4203000, idx=0x0)
[#5] 0x8063194->Name: replay_file(ctx=0xb6403280, idx=0x0)
[#6] 0x8061fb1->Name: tcpr_replay_index(ctx=0xb6403280)
[#7] 0x8060e81->Name: tcpreplay_replay(ctx=0xb6403280)
[#8] 0x80586eb->Name: main(argc=0x1, argv=0xbffff4d8)
//tcpedit.c:133
gef> p/d pktlen - ctx->l2len
$61 = 176
gef> p/d ctx->l2len
$62 = 14
gef> p/d newl2len
$63 = 18
gef> x packet
0xb6001640: 0
//tcpedit.c+133
gef> ptype pkthdr->caplen
type = unsigned int
gef> p/d pkthdr->caplen
$21 = 190
ASAN Output
=================================================================
==5237==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xb600167e at pc 0xb7adbfc8 bp 0xbfffe628 sp 0xbfffe1fc
READ of size 76 at 0xb600167e thread T0
#0 0xb7adbfc7 in __asan_memmove (/usr/lib/i386-linux-gnu/libasan.so.2+0x8afc7)
#1 0xb7adc3df in __interceptor_memmove (/usr/lib/i386-linux-gnu/libasan.so.2+0x8b3df)
#2 0x807938b in dlt_en10mb_encode plugins/dlt_en10mb/en10mb.c:488
#3 0x8075217 in tcpedit_dlt_encode plugins/dlt_plugins.c:402
#4 0x8074856 in tcpedit_dlt_process plugins/dlt_plugins.c:245
#5 0x80655dc in tcpedit_packet /home/ace/ACE/tcpreplay/src/tcpedit/tcpedit.c:133
#6 0x805158a in send_packets /home/ace/ACE/tcpreplay/src/send_packets.c:554
#7 0x8063193 in replay_file /home/ace/ACE/tcpreplay/src/replay.c:188
#8 0x8061fb0 in tcpr_replay_index /home/ace/ACE/tcpreplay/src/replay.c:61
#9 0x8060e80 in tcpreplay_replay /home/ace/ACE/tcpreplay/src/tcpreplay_api.c:1135
#10 0x80586ea in main /home/ace/ACE/tcpreplay/src/tcpreplay.c:139
#11 0xb784c636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
#12 0x804a985 (/usr/local/bin/tcpreplay-edit+0x804a985)
0xb600167e is located 0 bytes to the right of 62-byte region [0xb6001640,0xb600167e)
allocated by thread T0 here:
#0 0xb7ae7dee in malloc (/usr/lib/i386-linux-gnu/libasan.so.2+0x96dee)
#1 0x808c354 in _our_safe_malloc /home/ace/ACE/tcpreplay/src/common/utils.c:50
#2 0x805515d in get_next_packet /home/ace/ACE/tcpreplay/src/send_packets.c:1044
#3 0x80506d1 in preload_pcap_file /home/ace/ACE/tcpreplay/src/send_packets.c:445
#4 0x8058626 in main /home/ace/ACE/tcpreplay/src/tcpreplay.c:126
#5 0xb784c636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 __asan_memmove
Shadow bytes around the buggy address:
0x36c00270: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x36c00280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x36c00290: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x36c002a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x36c002b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x36c002c0: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00[06]
0x36c002d0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
0x36c002e0: 00 00 00 00 00 00 04 fa fa fa fa fa 00 00 00 00
0x36c002f0: 00 00 06 fa fa fa fa fa 00 00 00 00 00 00 04 fa
0x36c00300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x36c00310: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
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
==5237==ABORTING
[Inferior 1 (process 5237) exited with code 01]
Proof of Concept
sudo tcpreplay-edit --cachefile=example.cache --intf1=ens33 --intf2=lo --enet-vlan=add --enet-vlan-tag=40 $POC
--cachefiletakesa .cachefile as an input which is generated bytcpprepbinary. Itconsists of some rules to split traffic between differentinterfaces specified.
Interfaces can be specified using the intf switch:--intf1=ens33 --intf2=lo
-enet-vlan switch takes add or del as an input. The operations are adding or removing 802.1q VLAN tag information from ethernet frames.
The issue is triggered when supplied a crafted pcap file via the above given command.
Timeline
Vendor Disclosure: 2018-10-02
Public Disclosure: 2018-10-03
Credit
Discovered by ACE Team - Loginsoft