Heap based Buffer Over-read vulnerability in get_next_packet() - tcpreplay 4.3
Loginsoft-2018-17582
September 28, 2018
CVE Number
CVE-2018-17582
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
Tcp replay v4.3.0 beta1 contains a heap-based buffer over-read. The get_next_packet() function in the send_packets.c file uses the memcpy() function unsafely to copy sequences from the source buffer pkt data to the destination (*prev_packet)->pkt data. This will result in a Denial of Service (DoS) and potentially Information Exposure when the application attempts to process a file.
SYNOPSIS
Another heap overflow in tcrepay was discovered while processing a packet from a corrupted pcapfile. Below is in-short layout of a pcapfile format, which briefs about the vulnerability. Pcap file strcuture(as perlibpcap3.4)Apcapfile consists of various blocks which sum up & form a file.- Section Header Block: Also called global headers, it identifies the beginning of a pcapfile. Fields are - Major version, minor version, section length etc.
typedef struct pcap_hdr_s {
guint32 magic_number; /* magic number */
guint16 version_major; /* major version number */
guint16 version_minor; /* minor version number */
gint32 thiszone; /* GMT to local correction */
guint32 sigfigs; /* accuracy of timestamps */
guint32 snaplen; /* max length of captured packets, in octets */
guint32 network; /* data link type */
} pcap_hdr_t;
- Interface description block: Specified the characteristics of network interface on which the capture has been made. Fields:LinkType,Snaplen, Reserved etc.- Packet block: It's of our interest. It contains a single captured packet, or a portion of it. It's again separated into two sections, packet header & packet data.The fields contained in packet header are - Timestamp, captured length, packet length etc.
typedef struct pcaprec_hdr_s {
guint32 ts_sec; /* timestamp seconds */
guint32 ts_usec; /* timestamp microseconds */
guint32 incl_len; /* number of octets of packet saved in file (Captured length) */
guint32 orig_len; /* actual length of packet (Packet length)*/
} pcaprec_hdr_t;
Reference
http://www.tcpdump.org/pcap/pcap.html
https://wiki.wireshark.org/Development/LibpcapFileFormat
Another important point to note is the maximum size for a tcp packet is 65535, the same information about every individual packet is contained inside the packet header(packet lengthfield).
Root cause analysis
Void preload_pcap_file(tcpreplay_t *ctx, int idx) [1]
{
.
.
.
dlt = pcap_datalink(pcap);
while ((pktdata = get_next_packet(ctx, pcap, &pkthdr, idx, prev_packet)) != NULL) { [2]
packetnum++;
(*prev_packet)->pktdata = safe_malloc(pktlen);
memcpy((*prev_packet)->pktdata, pktdata, pktlen); [3]
memcpy(&((*prev_packet)->pkthdr), pkthdr, sizeof(struct pcap_pkthdr));
Moving on to the vulnerability, the function preload_pcap_file() [1] loads the pcapfile into the RAM for performance improvement . The function is invoked by the switch–K[5] ,which enables the caching of packets to internal memory. A per official documentation- "Cache pcapfile(s) the first time they are cached in RAM so that subsequent loops don’t incur any disk I/O latency in order to increase performance. It then calls get_next_packet()[2] which is responsible for getting the next packet to be sent out (this will either read from the pcap file or will retrieve the packet from the internal cache).
While getting the next package, it utilized a memcpy()[3]where the length field `pktlen`, being received from pkthdr->lenis invalid (packet length larger than the actual packet limit), causing an invalid read as it tries reading beyond the actual limitation of at cp packet which is of 65535, causing a heap based buffer over-read.
Analysis
1044 (*prev_packet)->pktdata = safe_malloc(pktlen);
// prev_packet=0xbfffef60 -> [...] -> 0x00000000, pktdata=0xbfffef24 -> [...] -> 0x07290c00
->1045 memcpy((*prev_packet)->pktdata, pktdata, pktlen); //Buffer overflow
1046 memcpy(&((*prev_packet)->pkthdr), pkthdr, sizeof(struct pcap_pkthdr));
1047 }
1048 }
1049 }
[#0] 0x8052f3a->Name: get_next_packet(ctx=0xb6403280, pcap=0xb4203280, pkthdr=0xbffff000, idx=0x0, prev_packet=0xbfffefc0)
[#1] 0x804e922->Name: preload_pcap_file(ctx=0xb6403280, idx=0x0)
[#2] 0x805615c->Name: main(argc=0x1, argv=0xbffff724)
gef> info locals
options = 0xb6200200
pktdata = 0xb3514800 ""
pktlen = 0x80003e
__PRETTY_FUNCTION__ = "get_next_packet"
__FUNCTION__ = "get_next_packet"
gef> ptype (*prev_packet)->pktdata
type = unsigned char *
gef> p pktdata
$30 = (u_char *) 0xb3514800 ""
gef> p (*prev_packet)->pktdata
$27 = (u_char *) 0xb2afe800 ""
gef> x (*prev_packet)->pktdata
0xb2afe800: 0
gef> ptype pktlen
type = unsigned int
gef> p/d pktlen
$25 = 8388670
gef> i r
eax 0xb4800320 0xb4800320
ecx 0x3 0x3
edx 0x0 0x0
ebx 0xb4800310 0xb4800310
esp 0xbfffef00 0xbfffef00
ebp 0xbfffef48 0xbfffef48
esi 0x0 0x0
edi 0xb2afe800 0xb2afe800
eip 0x8052f3a 0x8052f3a
eflags 0x246 [ PF ZF IF ]
cs 0x73 0x73
ss 0x7b 0x7b
ds 0x7b 0x7b
es 0x7b 0x7b
fs 0x0 0x0
gs 0x33 0x33
ASAN Output
=================================================================
==22604==ERROR: AddressSanitizer: heap-buffer-overflow on address 0xb35247ff at pc 0xb7adba75 bp 0xbfffeec8 sp 0xbfffea9c
READ of size 8388670 at 0xb35247ff thread T0
#0 0xb7adba74 in __asan_memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8aa74)
#1 0xb7adbc2f in memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8ac2f)
#2 0x8052fb6 in get_next_packet /home/loginsoft/ACE/tcpreplay/src/send_packets.c:1045
#3 0x804e921 in preload_pcap_file /home/loginsoft/ACE/tcpreplay/src/send_packets.c:445
#4 0x805615b in main /home/loginsoft/ACE/tcpreplay/src/tcpreplay.c:126
#5 0xb784c636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
#6 0x804a7a0 (/usr/local/bin/tcpreplay+0x804a7a0)
0xb35247ff is located 0 bytes to the right of 65535-byte region [0xb3514800,0xb35247ff)
allocated by thread T0 here:
#0 0xb7ae7dee in malloc (/usr/lib/i386-linux-gnu/libasan.so.2+0x96dee)
#1 0xb7a28e7c (/usr/lib/i386-linux-gnu/libpcap.so.0.8+0x1ce7c)
SUMMARY: AddressSanitizer: heap-buffer-overflow ??:0 __asan_memcpy
Shadow bytes around the buggy address:
0x366a48a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x366a48b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x366a48c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x366a48d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x366a48e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x366a48f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00[07]
0x366a4900: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x366a4910: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x366a4920: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x366a4930: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x366a4940: 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
==22604==ABORTING
[Inferior 1 (process 22604) exited with code 01]
Valgrind Report
==13353== Source and destination overlap in memcpy(0x467d028, 0x4648c50, 8388670)
==13353== at 0x4030D39: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==13353== by 0x804D24B: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804BCC0: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804E3FA: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x40DD636: (below main) (libc-start.c:291)
==13353==
==13353== Invalid read of size 4
==13353== at 0x4030DD0: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==13353== by 0x804D24B: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804BCC0: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804E3FA: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x40DD636: (below main) (libc-start.c:291)
==13353== Address 0x467d024 is 4 bytes before a block of size 8,388,670 alloc'd
==13353== at 0x402C17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==13353== by 0x8053B5B: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804D22E: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804BCC0: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804E3FA: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x40DD636: (below main) (libc-start.c:291)
==13353==
==13353== Invalid read of size 4
==13353== at 0x4030DDE: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==13353== by 0x804D24B: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804BCC0: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804E3FA: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x40DD636: (below main) (libc-start.c:291)
==13353== Address 0x467d020 is 8 bytes before a block of size 8,388,670 alloc'd
==13353== at 0x402C17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==13353== by 0x8053B5B: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804D22E: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804BCC0: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x804E3FA: ??? (in /usr/local/bin/tcpreplay)
==13353== by 0x40DD636: (below main) (libc-start.c:291)
==13353==
Proof of Concept
tcpreplay -i en33 -t –K –loop 4 –unique-ip $POC
-t switch is for performanceimprovementto send the packets as fast as possible.-K[5]--loop is used to specify the loopnumber, to loop through the capture file N times.The issue istriggeredwhen supplied a craftedpcapfile via the above given command.
Timeline
Vendor Disclosure: 2018-09-25
Public Disclosure: 2018-09-28
Credit
Discovered by ACE Team - Loginsoft