Decrypt IPSEC traffic with wireshark
I’ve setup a VPN IPSEC link between a Cisco and a Linux to demo the ESP decrypting feature of wireshark.
After having configured both sides of the IPSEC link, it’s time to test from the Cisco router:
ping 192.168.3.1 source 192.168.2.1 size 123 data CAFE
Type escape sequence to abort.
Sending 5, 123-byte ICMP Echos to 192.168.3.1, timeout is 2 seconds:
Packet sent with a source address of 192.168.2.1
Packet has data pattern 0xCAFE
..!!!
Success rate is 80 percent (3/5), round-trip min/avg/max = 1/3/4 ms
Sucess! The 2 first dot are because it took seconds to bring up the tunnel.
As you can see in the following trace, the icmp-request is unencrypted. Righ after we see some ESP traffic. Could it be the icmp-replies?
No. Time Source Destination Protocol Info heure
fw-1 chain
12 6.376835 10.100.100.200 10.100.100.100 ESP ESP (SPI=0x09bfd93b) 2011-07-17 20:36:08
.629993
13 6.376835 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=1/256, ttl=255) 2011-07-17 20:36:08
.629993
14 6.376933 10.100.100.100 10.100.100.200 ESP ESP (SPI=0x8134dedc) 2011-07-17 20:36:08
.630091
15 6.383264 10.100.100.200 10.100.100.100 ESP ESP (SPI=0x09bfd93b) 2011-07-17 20:36:08
.636422
16 6.383264 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=2/512, ttl=255) 2011-07-17 20:36:08
.636422
17 6.383348 10.100.100.100 10.100.100.200 ESP ESP (SPI=0x8134dedc) 2011-07-17 20:36:08
.636506
18 6.385546 10.100.100.200 10.100.100.100 ESP ESP (SPI=0x09bfd93b) 2011-07-17 20:36:08
.638704
19 6.385546 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=3/768, ttl=255) 2011-07-17 20:36:08
.638704
20 6.385614 10.100.100.100 10.100.100.200 ESP ESP (SPI=0x8134dedc) 2011-07-17 20:36:08
.638772
21 6.387761 10.100.100.200 10.100.100.100 ESP ESP (SPI=0x09bfd93b) 2011-07-17 20:36:08
.640919
22 6.387761 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=4/1024, ttl=255) 2011-07-17 20:36:08
.640919
23 6.387827 10.100.100.100 10.100.100.200 ESP ESP (SPI=0x8134dedc) 2011-07-17 20:36:08
.640985
Now, go find information that wireshark requires to decode the ESP traffic. Note that some implementation like the Cisco’s one doesn’t disclose the keys with a command. However it’s possible to find it within the Cisco IOS arcanes by drilling into a core dump and look for the good hex string. On Linux, you could use the ip xfrm state command to display the automatically generated session specific encryption and authentication keys:
$ sudo ip xfrm state
src 10.100.100.100 dst 10.100.100.200
proto esp spi 0x8134dedc reqid 0 mode tunnel
replay-window 4
auth hmac(md5) 0xa45c1bde51d20a61381025a1d55b675f
enc cbc(des3_ede) 0x7926ddbf7170fe9ece55bffede0e74e5a0b532958bcd0bc2
sel src 0.0.0.0/0 dst 0.0.0.0/0
src 10.100.100.200 dst 10.100.100.100
proto esp spi 0x09bfd93b reqid 0 mode tunnel
replay-window 4
auth hmac(md5) 0x09be7b297316b0085a5c99fd59745b54
enc cbc(des3_ede) 0xe4f6a1c56f8c31ff6c76a3ac24f284aaeb3a653cde839dcd
sel src 0.0.0.0/0 dst 0.0.0.0/0
Now, go to the wireshark preferences and fill the ESP protocol by the gathered information.
- Tick the ‘Attempt to detect/decode encrypted ESP payloads’ box
- SA #1 : IPv4|10.100.100.100|10.100.100.200|0x8134dedc
- Encryption algorithm #1: TripleDES-CBC [RFC2451]
- Authentication algorithm #1: HMAC-MD5-96 [RFC2403]
- Encryption Key #1: 0x7926ddbf7170fe9ece55bffede0e74e5a0b532958bcd0bc2
- Authentication Key #1: 0xa45c1bde51d20a61381025a1d55b675f
- SA #2 : IPv4|10.100.100.200|10.100.100.100|0x09bfd93b
- Encryption algorithm #2: TripleDES-CBC [RFC2451]
- Authentication algorithm #2: HMAC-MD5-96 [RFC2403]
- Encryption Key #2 : 0xe4f6a1c56f8c31ff6c76a3ac24f284aaeb3a653cde839dcd
- Authentication Key #2 : 0x09be7b297316b0085a5c99fd59745b54
After clicking the Apply button, the white magic occurs and a new tab called “Decrypted Tab” appears at the right of the frame’s one:
No. Time Source Destination Protocol Info heure
fw-1 chain
1 0.000000 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=1/256, ttl=255) 2011-07-17 20:36:08
.629993
2 0.000000 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=1/256, ttl=255) 2011-07-17 20:36:08
.629993
3 0.000098 192.168.3.1 192.168.2.1 ICMP Echo (ping) reply (id=0x0008, seq(be/le)=1/256, ttl=64) 2011-07-17 20:36:08
.630091
4 0.006429 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=2/512, ttl=255) 2011-07-17 20:36:08
.636422
5 0.006429 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=2/512, ttl=255) 2011-07-17 20:36:08
.636422
6 0.006513 192.168.3.1 192.168.2.1 ICMP Echo (ping) reply (id=0x0008, seq(be/le)=2/512, ttl=64) 2011-07-17 20:36:08
.636506
7 0.008711 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=3/768, ttl=255) 2011-07-17 20:36:08
.638704
8 0.008711 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=3/768, ttl=255) 2011-07-17 20:36:08
.638704
9 0.008779 192.168.3.1 192.168.2.1 ICMP Echo (ping) reply (id=0x0008, seq(be/le)=3/768, ttl=64) 2011-07-17 20:36:08
.638772
10 0.010926 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=4/1024, ttl=255) 2011-07-17 20:36:08
.640919
11 0.010926 192.168.2.1 192.168.3.1 ICMP Echo (ping) request (id=0x0008, seq(be/le)=4/1024, ttl=255) 2011-07-17 20:36:08
.640919
12 0.010992 192.168.3.1 192.168.2.1 ICMP Echo (ping) reply (id=0x0008, seq(be/le)=4/1024, ttl=64) 2011-07-17 20:36:08
.640985
Congrats! You are now able to read through IPSEC!
BPF aka Boolean Packet Filter language
Yes. BPF is Berkeley Packet Filter but could be renamed Boolean Packet Filter as the filter function output a true or false regarding a packet traveling from the kernel to the application.
This result can be obtained glad to a directed acyclic control flow graph composed of comparison predicates and boolean operations.
Here is how to decrypt the language helped by the next two examples. The people accustomed with the randomly chosen assembly languages should enjoy the syntax :-)
On a side snote, wireshark 1.6.0 feature a BPF compiler in the capture wizard to impress your girlfriend.
pello@Networker:~$ sudo tcpdump -i eth0 -d “ip”
(000) ldh [12] (Load a half-word (16 bits) at the position 12 in decimal)
(001) jeq #0x800 jt 2 jf 3 (jt = jump to (002) if true / jf = jump to (003) if false)
(002) ret #65535 (65535 = TRUE) <= DELIVERED TO THE APP (WIRESHARK by example)
(003) ret #0 (0 = FALSE) <= DELIVERED TO IGNORE LAND
Another example .. a little bit more complicated:
pello@Networker:~$ sudo tcpdump -i eth0 -d “tcp port 22”
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 8 (Check if IPv6)
(002) ldb [20] (Move to position 20 - Next header field - IPv6 branch)
(003) jeq #0x6 jt 4 jf 19 (Check if IPv6 next header is TCP)
(004) ldh [54]
(005) jeq #0x16 jt 18 jf 6 (Check if TCP source port equals 22 - IPv6 Branch)
(006) ldh [56]
(007) jeq #0x16 jt 18 jf 19 (Check if TCP destination port equals 22 - IPv6 Branch)
(008) jeq #0x800 jt 9 jf 19 (Check if IPv4 from instruction 000)
(009) ldb [23] (Move to the 23th byte position of the packet starting from the link layer header (Ethernet in this case) and read 1 byte considering 1 byte equals to 8 bits)
(010) jeq #0x6 jt 11 jf 19 (Check if TCP - Ipv4 branch)
(011) ldh [20]
(012) jset #0x1fff jt 19 jf 13 (Bitwise and with the IP fragmentation bit mask check. If non-zero then this is a fragmented packet so we cannot find the TCP header in this packet |=> ignored)
(013) ldxb 4*([14]&0xf) (Seek the IP header length bitwise and with 0xf then multiplicate it by 4. The result is stored as a Byte in the index register: x)
(014) ldh [x + 14] (TCP source port position derived from x - IPv4 branch - 16 bits)
(015) jeq #0x16 jt 18 jf 16 (True if 22)
(016) ldh [x + 16] (TCP destination port position derived from x - IPv4 branch - 16 bits)
(017) jeq #0x16 jt 18 jf 19 (True if 22)
(018) ret #65535
(019) ret #0
As a next step of playing with BPF filters the reader could reverse the order of the ‘and’ filters and observe the difference. As an example: “tcp port 22 and host 127.0.0.1” versus “host 127.0.0.1 and tcp port 22”
Happy BPF! :>
Big LAN and ARP broadcast
Sometimes the network suffers from a very BAD design (like large L2 domain).
In this situation, some (normal) network behavior are more visible than it should if the network had a better designer.
The reason of the bad design is often part of the history OR the hired consultant dislikes th company he works for and ship them with a bad design :D
One of the visible phenomenon occurs when many hosts are populated in ARP caches and the local table overflows. The default ARP cache on Linux (and every other OS) are not suited for the bad designed networks.
It results in broadcast storms that kills network performances. Another side effect that double the bad effect is when you have configured broadcast rate-limiter. This feature could kills ARP broadcast and make the packets dance … dance again and again through your L2 network.
To fix the network, you must go in two directions:
- re-think your broadcast rate-limiter (in some network devices it’s done automatically without configuration! don’t trust the vendor pre-sales in his well-suited costume, trust the packets!
- adjust ARP cache and garbage collector settings on your end hosts. And adjust CAM age entries on your transit L2 devices. For Linux, you can go with those parameters:
$ sudo echo ‘net.ipv4.neigh.default.gc_thresh3’ = 4096 » /etc/sysctl.conf
$ sudo echo ‘net.ipv4.neigh.default.gc_thresh2’ = 2048 » /etc/sysctl.conf
$ sudo echo ‘net.ipv4.neigh.default.gc_thresh1’ = 1024 » /etc/sysctl.conf
$ sudo sysctl -p
Et voilà!
Ntop listen with tcp6 (:::3000) and not tcp under my debian linux setup