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 was tcpreplay, which consists of many binaries. This report recites about a heap overflow vulnerability discovered in the `tcpreplay-edit` binary, which includes all the functionality of both tcpreplay and tcprewrite.
An heap overflow was triggered in function `dlt_en10mb_encode()` at file `en10mb.c`, due to inappropriate values in memmove() [1].
The workflow
1. Packet is read
2. Packet is passed to source DLT (Data link type) decoder
3. Decoder fills out tcpedit internal data structure (tcpeditdlt_t)
4. tcpeditdlt_t is passed to destination DLT encoder
5. DLT Decoder returns new L2 header
6. Packet is rebuilt
7. L3 and above is processed
8. Packet is written
Ref: 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 <dlt_en10mb_encode+1548> mov eax, DWORD PTR [eax+0x28] 0x8079358 <dlt_en10mb_encode+1551> cmp eax, DWORD PTR [ebp-0x44] 0x807935b <dlt_en10mb_encode+1554> je 0x807938f <dlt_en10mb_encode+1606> -> 0x807935d <dlt_en10mb_encode+1556> mov eax, DWORD PTR [ebp+0x8] 0x8079360 <dlt_en10mb_encode+1559> mov eax, DWORD PTR [eax+0x28] 0x8079363 <dlt_en10mb_encode+1562> mov edx, DWORD PTR [ebp+0x10] 0x8079366 <dlt_en10mb_encode+1565> sub edx, eax 0x8079368 <dlt_en10mb_encode+1567> mov eax, edx 0x807936a <dlt_en10mb_encode+1569> 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
–cachefile takes a .cache file as an input which is generated by tcpprep binary. It consists of some rules to split traffic between different interfaces 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