tcpdump icon indicating copy to clipboard operation
tcpdump copied to clipboard

[Patch] Add support for decrypting ESP packets in packet_dump mode

Open guyharris opened this issue 12 years ago • 2 comments

Converted from SourceForge issue 3431245, submitted by alanre

I got fed up decoding ESP manually so I added the functionality into tcpdump. I've enhanced the existing -E flag which currently only prints the decrypted ESP packets tp the screen

Now if -E and -w flags are present then both the raw ESP packet and the decoded ESP packet are dumped to the file.

( I use a perl based wrapper around tcpdump which extracts the SPIs and encryption keys from a setkey -D output)

cheers AlanE

guyharris avatar Apr 16 '13 00:04 guyharris

Submitted by guy_harris

A version that dynamically allocates and reallocates the buffer, rather than imposing an arbitrary 1500-byte limit, might be better.

Even a version that prints a warning and continues, rather than exiting (error() exits) would be better.

guyharris avatar Apr 16 '13 00:04 guyharris

The originally proposed patch:

diff -rupN tcpdump-org/Makefile.in tcpdump/Makefile.in
--- tcpdump-org/Makefile.in	2011-10-31 15:26:18.526869894 +0000
+++ tcpdump/Makefile.in	2011-10-31 15:30:29.964230206 +0000
@@ -93,7 +93,7 @@ CSRC =	addrtoname.c af.c checksum.c cpac
 	print-symantec.c print-syslog.c print-tcp.c print-telnet.c print-tftp.c \
 	print-timed.c print-token.c print-udld.c print-udp.c print-usb.c \
 	print-vjc.c print-vqp.c print-vrrp.c print-vtp.c print-forces.c \
-	print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c
+	print-wb.c print-zephyr.c signature.c setsignal.c tcpdump.c util.c decrypt-esp.c
 
 LIBNETDISSECT_SRC=print-isakmp.c
 LIBNETDISSECT_OBJ=$(LIBNETDISSECT_SRC:.c=.o)
diff -rupN tcpdump-org/decrypt-esp.c tcpdump/decrypt-esp.c
--- tcpdump-org/decrypt-esp.c	1970-01-01 01:00:00.000000000 +0100
+++ tcpdump/decrypt-esp.c	2011-10-31 15:30:36.030853743 +0000
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <tcpdump-stdinc.h>
+
+#include <stdlib.h>
+
+#ifdef HAVE_LIBCRYPTO
+#ifdef HAVE_OPENSSL_EVP_H
+#include <openssl/evp.h>
+#endif
+#endif
+
+#include <stdio.h>
+#include <netinet/in.h>
+
+#include "interface.h"
+#include "ether.h"
+#include "ip.h"
+#include "udp.h"
+#include "esp.h"
+#ifdef INET6
+#include "ip6.h"
+#endif
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#ifndef HAVE_SOCKADDR_STORAGE
+#ifdef INET6
+struct sockaddr_storage {
+	union {
+		struct sockaddr_in sin;
+		struct sockaddr_in6 sin6;
+	} un;
+};
+#else
+#define sockaddr_storage sockaddr
+#endif
+#endif /* HAVE_SOCKADDR_STORAGE */
+
+#ifdef HAVE_LIBCRYPTO
+struct sa_list {
+	struct sa_list	*next;
+	struct sockaddr_storage daddr;
+	u_int32_t	spi;          /* if == 0, then IKEv2 */
+	int             initiator;
+	u_char          spii[8];      /* for IKEv2 */
+	u_char          spir[8];
+	const EVP_CIPHER *evp;
+	int		ivlen;
+	int		authlen;
+	u_char          authsecret[256];
+	int             authsecret_len;
+	u_char		secret[256];  /* is that big enough for all secrets? */
+	int		secretlen;
+};
+
+
+int esp_decrypt(u_char *user, const struct pcap_pkthdr *h, const u_char *sp, struct pcap_pkthdr *decrypt_h, u_char *decrypt_sp);
+u_int16_t ip_cksum(u_int16_t len, u_char buff[]);
+
+int esp_decrypt(u_char *user, const struct pcap_pkthdr *h, const u_char *sp, struct pcap_pkthdr *decrypt_h, u_char *decrypt_sp) {
+
+    struct ether_header *ether_h;
+    struct ip *ip_h, *decrypt_ip_h;
+    struct udphdr *udp_h;
+    struct newesp *esp_h;
+    struct sa_list *sa;
+    netdissect_options *ndo;
+    u_char *iv;
+    int ilen, olen, olen_final;
+    u_char  *esp_data;
+    u_char pad, next_hdr;
+    
+    EVP_CIPHER_CTX ctx;
+
+
+    /* Sanity check pcap_pkthdr */
+    if (h->caplen > 1500) {  
+	error("Can't handle packets > 1500 bytes\n");
+	return(0);
+    }
+    if (h->caplen < h->len) {
+	error("Captured packet is truncated\n");
+	return(0);
+    }
+
+    decrypt_h->ts = h->ts;
+    ether_h = (struct ether_header*)(sp);
+    memcpy(decrypt_sp, sp, ETHER_HDRLEN);
+    sp = sp + ETHER_HDRLEN;
+    ip_h = (struct ip *)sp;
+    sp = sp + (IP_HL(ip_h)*4);
+
+    if (ip_h->ip_p == IPPROTO_ESP) {
+	esp_h = (struct newesp *)sp;
+    	ilen = ntohs(ip_h->ip_len) - (IP_HL(ip_h)*4);
+    } else if (ip_h->ip_p == IPPROTO_UDP) { 
+
+    /* Check if ESP is UDP ENC  UDP Port 4500 */
+
+    	udp_h = (struct udphdr*)sp;
+    	if (ntohs(udp_h->uh_sport) == ISAKMP_PORT_NATT || ntohs(udp_h->uh_dport) == ISAKMP_PORT_NATT) {
+    	    sp = sp + sizeof(struct udphdr);
+	    esp_h = (struct newesp *)sp;
+    	    ilen = ntohs(udp_h->uh_ulen) - sizeof(struct udphdr);
+        } else {
+	    return(0);
+        }	
+    }
+    else {
+	return(0);
+    }
+
+    sp = sp + sizeof(struct newesp) + 8;
+    esp_data = (u_char *)sp;
+    
+    /* Strip of the 24 byte ESP Header and 12 byte ESP Authentication tail */
+    ilen = ilen - 28;
+    iv = (u_char *)(esp_h + sizeof(struct newesp));
+    
+    /* see if we can find the SA, and if so, decode it */
+    ndo = gndo;
+    esp_print_decodesecret(ndo);
+    for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
+        struct sockaddr_in *sin = (struct sockaddr_in *)&sa->daddr;
+        if (sa->spi == ntohl(esp_h->esp_spi) &&
+                 sin->sin_family == AF_INET &&
+                 sin->sin_addr.s_addr == ip_h->ip_dst.s_addr) {
+                 break;
+        }
+    }
+    if(sa == NULL) {
+	return 0;
+    }
+    if(sa->evp == NULL) {
+        return 0;
+    }
+
+    memset(&ctx, 0, sizeof(ctx));
+
+    if (EVP_DecryptInit_ex(&ctx, sa->evp, NULL, sa->secret, iv) != 1) {
+   	EVP_CIPHER_CTX_cleanup(&ctx);
+	return 0;
+    }
+
+    EVP_CIPHER_CTX_set_padding(&ctx, 0); /* Disable padding */
+
+    if (EVP_DecryptUpdate(&ctx, decrypt_sp + ETHER_HDRLEN, &olen, esp_data, ilen) != 1) {
+   	EVP_CIPHER_CTX_cleanup(&ctx);
+	return 0;
+    }
+    if (EVP_DecryptFinal_ex(&ctx, decrypt_sp + ETHER_HDRLEN + olen, &olen_final) != 1) {
+   	EVP_CIPHER_CTX_cleanup(&ctx);
+	return 0;
+   }
+   EVP_CIPHER_CTX_cleanup(&ctx);
+   olen = olen + olen_final;
+   pad = decrypt_sp[ETHER_HDRLEN + olen - 2];
+   next_hdr = decrypt_sp[ETHER_HDRLEN + olen - 1]; 
+   olen = olen - pad - 2; /* Remove Pad, Pad Length and Next Hdr */
+   
+   
+/* Fixup the IP Header */
+
+   decrypt_ip_h = (struct ip *)(decrypt_sp + ETHER_HDRLEN);
+
+   decrypt_ip_h->ip_vhl = ip_h->ip_vhl;
+   decrypt_ip_h->ip_tos = ip_h->ip_tos;
+   decrypt_ip_h->ip_len = htons(olen);
+   decrypt_ip_h->ip_off = ip_h->ip_off;
+   decrypt_ip_h->ip_sum = 0x0000;
+   decrypt_ip_h->ip_sum = htons(ip_cksum(20, (u_char *)decrypt_ip_h));
+   
+   decrypt_h->len = olen + ETHER_HDRLEN;
+   decrypt_h->caplen = olen + ETHER_HDRLEN;
+
+   return (1);
+	
+}
+
+
+u_int16_t ip_cksum(u_int16_t len, u_char buff[]) {
+
+u_int16_t word16;
+u_int32_t sum=0;
+u_int16_t i;
+    
+// make 16 bit words out of every two adjacent 8 bit words in the packet and add them up
+    for (i=0;i<len;i=i+2) {
+        word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF);
+        sum = sum + (u_int32_t) word16;	
+    }
+
+// take only 16 bits out of the 32 bit sum and add up the carries
+    while (sum>>16)
+        sum = (sum & 0xFFFF)+(sum >> 16);
+    
+// one's complement the result
+    sum = ~sum;
+   
+    return ((u_int16_t) sum);
+}
+    												  			
+    												  			
+#endif  // End ifdef HAVE_LIBCRYPTO
diff -rupN tcpdump-org/tcpdump.c tcpdump/tcpdump.c
--- tcpdump-org/tcpdump.c	2011-10-31 15:26:18.657904541 +0000
+++ tcpdump/tcpdump.c	2011-10-31 15:32:57.282095007 +0000
@@ -97,6 +97,8 @@ static int Jflag;			/* list available ti
 #endif
 static char *zflag = NULL;		/* compress each savefile using a specified command (like gzip or bzip2) */
 
+static int decrypt_esp_flag;
+
 static int infodelay;
 static int infoprint;
 
@@ -727,6 +729,7 @@ main(int argc, char **argv)
 			warning("crypto code not compiled in");
 #endif
 			gndo->ndo_espsecret = optarg;
+			decrypt_esp_flag++;
 			break;
 
 		case 'f':
@@ -1571,6 +1574,8 @@ static void
 dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
 {
 	struct dump_info *dump_info;
+    	struct pcap_pkthdr decrypt_h;
+    	u_char decrypt_sp[1500];
 
 	++packets_captured;
 
@@ -1681,6 +1686,11 @@ dump_packet_and_trunc(u_char *user, cons
 	}
 
 	pcap_dump((u_char *)dump_info->p, h, sp);
+        if (decrypt_esp_flag) {
+	    if (esp_decrypt(user, h, sp, &decrypt_h, &decrypt_sp) == 1) {
+	        pcap_dump(user, &decrypt_h, decrypt_sp);
+	    }
+	}
 #ifdef HAVE_PCAP_DUMP_FLUSH
 	if (Uflag)
 		pcap_dump_flush(dump_info->p);
@@ -1694,11 +1704,18 @@ dump_packet_and_trunc(u_char *user, cons
 static void
 dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
 {
-	++packets_captured;
+    struct pcap_pkthdr decrypt_h;
+    u_char decrypt_sp[1500];
 
+	++packets_captured;
 	++infodelay;
 
 	pcap_dump(user, h, sp);
+        if (decrypt_esp_flag) {
+	    if (esp_decrypt(user, h, sp, &decrypt_h, &decrypt_sp) == 1) {
+	        pcap_dump(user, &decrypt_h, decrypt_sp);
+	    }
+	}
 #ifdef HAVE_PCAP_DUMP_FLUSH
 	if (Uflag)
 		pcap_dump_flush((pcap_dumper_t *)user);

infrastation avatar Jul 31 '18 09:07 infrastation