pcapdiff compares two packet captures for anomalies

pcapdiff is a python tool that will compare two packet captures (one from the src system and one from the dst system) and highlight suspicious, mangled or possibly injected packets. It requires pcapy and, of course, python. Saw this over on Nate Lawson’s blog in discussing how to detect TCP RSTs, which is apparently being used by Comcast to combat BitTorrent file sharing.

3 thoughts on “pcapdiff compares two packet captures for anomalies

  1. Hi Michael,
    Did you try this tool? I gave it a shot but did not have much luck. For example, I saved a trace like this:
    $ tcpdump -nqt -r icmp.hacom.1.lpc
    reading from file icmp.hacom.1.lpc, link-type EN10MB (Ethernet)
    IP 69.255.105.234 > 82.98.86.161: ICMP echo request, id 23074, seq 1, length 64
    IP 82.98.86.161 > 69.255.105.234: ICMP echo reply, id 23074, seq 1, length 64
    IP 69.255.105.234 > 82.98.86.161: ICMP echo request, id 23074, seq 2, length 64
    IP 82.98.86.161 > 69.255.105.234: ICMP echo reply, id 23074, seq 2, length 64
    IP 69.255.105.234 > 82.98.86.161: ICMP echo request, id 23074, seq 3, length 64
    IP 82.98.86.161 > 69.255.105.234: ICMP echo reply, id 23074, seq 3, length 64
    then removed a few packets like this:
    $ tcpdump -nqt -r icmp.hacom.1.edited.lpc
    reading from file icmp.hacom.1.edited.lpc, link-type EN10MB (Ethernet)
    IP 69.255.105.234 > 82.98.86.161: ICMP echo request, id 23074, seq 1, length 64
    IP 69.255.105.234 > 82.98.86.161: ICMP echo request, id 23074, seq 2, length 64
    IP 69.255.105.234 > 82.98.86.161: ICMP echo request, id 23074, seq 3, length
    When I ran the tool I got this:
    $ python pcapdiff.py -VV icmp.hacom.1.lpc 69.255.105.234 icmp.hacom.1.edited.lpc 69.255.105.234
    Parsed. Local num of unique packets: 2, num of duplicate packets: 0
    Remote num of unmatching packets: 2
    ————–
    Traceback (most recent call last):
    File “pcapdiff.py”, line 346, in ?
    print “ERROR: ip addrs should have been dropped %d -> %d” % (ip_from, ip_to)
    TypeError: int argument required
    Oh well…

  2. Yeah, I got it to work pretty nicely. I’ll see if I can show what I did here. For starters, I think your second IP should be the destination IP address, but that should still output properly even if it is wrong. I think you have a pcapy problem?
    I first installed python-pcapy through apt-get (yay Ubuntu!).
    I then downloaded and unzipped pcapdiff.
    SOURCE (192.168.10.108) CAPTURE:
    michael@orion:~/Desktop/pcapdiff$ tcpdump -nqt -r src1.cap
    reading from file src1.cap, link-type EN10MB (Ethernet)
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 1, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 1, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 2, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 2, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 3, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 3, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 4, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 4, length 64
    DESTINATION (192.168.10.110) CAPTURE:
    michael@orion:~/Desktop/pcapdiff$ tcpdump -nqt -r dst1.cap
    reading from file dst1.cap, link-type EN10MB (Ethernet)
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 1, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 1, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 2, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 2, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 3, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 3, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 4, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 4, length 64
    I edited out a few packets like you, and have this as my DESTINATION capture:
    michael@orion:~/Desktop/pcapdiff$ tcpdump -nqt -r dst1-edit.cap
    reading from file dst1-edit.cap, link-type EN10MB (Ethernet)
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 1, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 1, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 2, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 3, length 64
    IP 192.168.10.108 > 192.168.10.110: ICMP echo request, id 52584, seq 4, length 64
    IP 192.168.10.110 > 192.168.10.108: ICMP echo reply, id 52584, seq 4, length 64
    Now let’s compare:
    michael@orion:~/Desktop/pcapdiff$ python pcapdiff.py -VV src1.cap 192.168.10.108 dst1-edit.cap 192.168.10.110
    Parsed. Local num of unique packets: 2, num of duplicate packets: 0
    Remote num of unmatching packets: 2
    ————–
    !!! FORGED: 1
    ip_version: 4
    ip_header_length: 5
    ip_total_length: 84
    ip_ident: 61810
    ip_flags_plus_offset: 0000
    ip_protocol: 1
    ip_src: 192.168.10.110
    ip_dest: 192.168.10.108
    ip_options:
    ip_payload_length: 62
    ip_payload_data: \x00\x00#\xa0\xcdh\x00\x03\x9c\x89\x9eG\xe1\x1f\x08\x00\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\'()*+,-./012345
    ————–
    !!! FORGED: 1
    ip_version: 4
    ip_header_length: 5
    ip_total_length: 84
    ip_ident: 61809
    ip_flags_plus_offset: 0000
    ip_protocol: 1
    ip_src: 192.168.10.110
    ip_dest: 192.168.10.108
    ip_options:
    ip_payload_length: 62
    ip_payload_data: \x00\x00\xf6\xa0\xcdh\x00\x02\x9b\x89\x9eG\x0f \x08\x00\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\'()*+,-./012345
    Here are the IP identification fields for forged and dropped packets:
    list of inbound forged packets: 61810 61809
    list of outbound forged packets:
    list of inbound dropped packets:
    list of outbound dropped packets:
    ————-
    Packet counts
    ————-
    inbound outbound
    sent: 2 4
    received: 4 4
    forged: 2 0
    dropped: 0 0

  3. I see — here’s what I tried.
    $ tcpdump -nqt -r pcap.1.lpc
    reading from file pcap.1.lpc, link-type EN10MB (Ethernet)
    IP 192.168.2.101 > 64.233.161.147: ICMP echo request, id 9239, seq 1, length 64
    IP 64.233.161.147 > 192.168.2.101: ICMP echo reply, id 9239, seq 1, length 64
    IP 192.168.2.101 > 64.233.161.147: ICMP echo request, id 9239, seq 2, length 64
    IP 64.233.161.147 > 192.168.2.101: ICMP echo reply, id 9239, seq 2, length 64
    IP 192.168.2.101 > 64.233.161.147: ICMP echo request, id 9239, seq 3, length 64
    IP 64.233.161.147 > 192.168.2.101: ICMP echo reply, id 9239, seq 3, length 64
    IP 192.168.2.101 > 64.233.161.147: ICMP echo request, id 9239, seq 4, length 64
    IP 64.233.161.147 > 192.168.2.101: ICMP echo reply, id 9239, seq 4, length 64
    For the edited version I deleted the first three ICMP echo replies.
    $ tcpdump -nqt -r pcap.1.edited.lpc reading from file pcap.1.edited.lpc, link-type EN10MB (Ethernet)
    IP 192.168.2.101 > 64.233.161.147: ICMP echo request, id 9239, seq 1, length 64
    IP 192.168.2.101 > 64.233.161.147: ICMP echo request, id 9239, seq 2, length 64
    IP 192.168.2.101 > 64.233.161.147: ICMP echo request, id 9239, seq 3, length 64
    IP 192.168.2.101 > 64.233.161.147: ICMP echo request, id 9239, seq 4, length 64
    IP 64.233.161.147 > 192.168.2.101: ICMP echo reply, id 9239, seq 4, length 64
    $ python pcapdiff.py -VV pcap.1.lpc 192.168.2.101 pcap.1.edited.lpc 64.233.161.147
    Parsed. Local num of unique packets: 3, num of duplicate packets: 0
    Remote num of unmatching packets: 3
    ————–
    !!! FORGED: 1
    ip_version: 4
    ip_header_length: 5
    ip_total_length: 84
    ip_ident: 0
    ip_flags_plus_offset: 4000
    ip_protocol: 1
    ip_src: 64.233.161.147
    ip_dest: 192.168.2.101
    ip_options:
    ip_payload_length: 64
    ip_payload_data: \x00\x00\x89\x00$\x17\x00\x02@S\x9fGzH\x0e\x00\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\'()*+,-./01234567
    ————–
    !!! FORGED: 1
    ip_version: 4
    ip_header_length: 5
    ip_total_length: 84
    ip_ident: 0
    ip_flags_plus_offset: 4000
    ip_protocol: 1
    ip_src: 64.233.161.147
    ip_dest: 192.168.2.101
    ip_options:
    ip_payload_length: 64
    ip_payload_data: \x00\x00l\xff$\x17\x00\x03AS\x9fG\x95H\x0e\x00\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\'()*+,-./01234567
    ————–
    !!! FORGED: 1
    ip_version: 4
    ip_header_length: 5
    ip_total_length: 84
    ip_ident: 0
    ip_flags_plus_offset: 4000
    ip_protocol: 1
    ip_src: 64.233.161.147
    ip_dest: 192.168.2.101
    ip_options:
    ip_payload_length: 64
    ip_payload_data: \x00\x00[\x0c$\x17\x00\x01?S\x9fG\xa9=\x0e\x00\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\'()*+,-./01234567
    Here are the IP identification fields for forged and dropped packets:
    list of inbound forged packets: 0 0 0
    list of outbound forged packets:
    list of inbound dropped packets:
    list of outbound dropped packets:
    ————-
    Packet counts
    ————-
    inbound outbound
    sent: 1 4
    received: 4 4
    forged: 3 0
    dropped: 0 0
    Here’s the reverse output:
    $ python pcapdiff.py -VV pcap.1.edited.lpc 64.233.161.147 pcap.1.lpc 192.168.2.101
    Parsed. Local num of unique packets: 3, num of duplicate packets: 0
    Remote num of unmatching packets: 3
    ————–
    !!! FORGED: -1
    ip_version: 4
    ip_header_length: 5
    ip_total_length: 84
    ip_ident: 0
    ip_flags_plus_offset: 4000
    ip_protocol: 1
    ip_src: 64.233.161.147
    ip_dest: 192.168.2.101
    ip_options:
    ip_payload_length: 64
    ip_payload_data: \x00\x00\x89\x00$\x17\x00\x02@S\x9fGzH\x0e\x00\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\'()*+,-./01234567
    ————–
    !!! FORGED: -1
    ip_version: 4
    ip_header_length: 5
    ip_total_length: 84
    ip_ident: 0
    ip_flags_plus_offset: 4000
    ip_protocol: 1
    ip_src: 64.233.161.147
    ip_dest: 192.168.2.101
    ip_options:
    ip_payload_length: 64
    ip_payload_data: \x00\x00l\xff$\x17\x00\x03AS\x9fG\x95H\x0e\x00\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\'()*+,-./01234567
    ————–
    !!! FORGED: -1
    ip_version: 4
    ip_header_length: 5
    ip_total_length: 84
    ip_ident: 0
    ip_flags_plus_offset: 4000
    ip_protocol: 1
    ip_src: 64.233.161.147
    ip_dest: 192.168.2.101
    ip_options:
    ip_payload_length: 64
    ip_payload_data: \x00\x00[\x0c$\x17\x00\x01?S\x9fG\xa9=\x0e\x00\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !”#$%&\'()*+,-./01234567
    Here are the IP identification fields for forged and dropped packets:
    list of inbound forged packets:
    list of outbound forged packets: 0 0 0
    list of inbound dropped packets:
    list of outbound dropped packets:
    ————-
    Packet counts
    ————-
    inbound outbound
    sent: 4 1
    received: 4 4
    forged: 0 3
    dropped: 0 0
    I’m not convinced the decision logic makes much sense here but it is noticing odd activity when I use the IPs as you suggested!

Comments are closed.