pcapedit: An Interactive Scapy-based Pcap Editor


« 📅 published on 15/Nov/2014 »

🔖 tagged code


Introduction

While working with pcap files, I occasionally come across situations where I need to edit them for some weird usecases. When in such a situation, I had to write a loop that iterates over packets, check if the current packet needs modification, apply changes if so and save these changes to a new pcap. As I repeated these steps a few times, need for automation became obvious. I sat together with my colleague and good friend Natraj and discussed about how we can automate this process so that it helps with our IPS signature development efforts. Eventually, a draft for an interactive tool was formulated and pcapedit was born.

pcapedit is a Python-based tool that allows users to enter commands to edit and save pcaps. It has a hard dependency on Scapy and cmd2 modules so make sure these are installed. Here is a snippet from test run:

$ ./pcapedit.py
PcapEdit - An Interactive Pcap Editor

>>>
>>>
>>> help

Documented commands (type help <topic>):
========================================
_load           commands  history  ls       r         shell
_relative_load  ed        l        outpcap  run       shortcuts
analyze         edit      li       pause    save      show
back            hexdump   list     pdfdump  Scapycmd  summary
cmdenvironment  hi        load     py       set       wireshark

Undocumented commands:
======================
EOF  eof  exit  help  q  quit

>>> commands

  [01] analyze ......... load a pcap for analysis
  [02] ls .............. list packet details
  [03] summary ......... show summary of a packet
  [04] hexdump ......... show hexdump of a packet
  [05] pdfdump ......... dump packet to a PDF
  [06] Scapycmd ........ show Scapy command to generate a packet
  [07] wireshark ....... show a packet in Wireshark
  [08] edit ............ select a packet for set operations
  [09] outpcap ......... set name for output pcap
  [10] set ............. change value of a protocol field
  [11] save ............ save packets to a pcap

>>> q

Usecases

So we have a few commands to try out. Let's assume that you have a http.pcap file that needs editing. Let's see how pcapedit can be useful in this case:

$ ./pcapedit.py
PcapEdit - An Interactive Pcap Editor

>>> analyze http.cap
Read 43 packets from http.cap
(http.cap) >>> summary
     0: 2004/05/13 15:47:07    145.254.160.237:3372 -> 65.208.228.223:80       TCP S
     1: 2004/05/13 15:47:08       65.208.228.223:80 -> 145.254.160.237:3372    TCP SA
     2: 2004/05/13 15:47:08    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
     3: 2004/05/13 15:47:08    145.254.160.237:3372 -> 65.208.228.223:80       TCP PA (479 bytes)
     4: 2004/05/13 15:47:08       65.208.228.223:80 -> 145.254.160.237:3372    TCP A
     5: 2004/05/13 15:47:08       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
     6: 2004/05/13 15:47:09    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
     7: 2004/05/13 15:47:09       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
     8: 2004/05/13 15:47:09    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
     9: 2004/05/13 15:47:09       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
    10: 2004/05/13 15:47:09       65.208.228.223:80 -> 145.254.160.237:3372    TCP PA (1380 bytes)
    11: 2004/05/13 15:47:09    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    12: 2004/05/13 15:47:09    145.254.160.237:3009 -> 145.253.2.203:53        UDP (47 bytes)
    13: 2004/05/13 15:47:09       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
    14: 2004/05/13 15:47:10    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    15: 2004/05/13 15:47:10       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
    16: 2004/05/13 15:47:10        145.253.2.203:53 -> 145.254.160.237:3009    UDP (244 bytes)
    17: 2004/05/13 15:47:10    145.254.160.237:3371 -> 216.239.59.99:80        TCP PA (721 bytes)
    18: 2004/05/13 15:47:10    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    19: 2004/05/13 15:47:10       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
    20: 2004/05/13 15:47:10       65.208.228.223:80 -> 145.254.160.237:3372    TCP PA (1380 bytes)
    21: 2004/05/13 15:47:10    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    22: 2004/05/13 15:47:10       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
    23: 2004/05/13 15:47:10        216.239.59.99:80 -> 145.254.160.237:3371    TCP A
    24: 2004/05/13 15:47:11    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    25: 2004/05/13 15:47:11        216.239.59.99:80 -> 145.254.160.237:3371    TCP PA (1430 bytes)
    26: 2004/05/13 15:47:11        216.239.59.99:80 -> 145.254.160.237:3371    TCP PA (160 bytes)
    27: 2004/05/13 15:47:11    145.254.160.237:3371 -> 216.239.59.99:80        TCP A
    28: 2004/05/13 15:47:11       65.208.228.223:80 -> 145.254.160.237:3372    TCP PA (1380 bytes)
    29: 2004/05/13 15:47:11    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    30: 2004/05/13 15:47:11       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
    31: 2004/05/13 15:47:11       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
    32: 2004/05/13 15:47:11    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    33: 2004/05/13 15:47:11       65.208.228.223:80 -> 145.254.160.237:3372    TCP A (1380 bytes)
    34: 2004/05/13 15:47:11    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    35: 2004/05/13 15:47:12        216.239.59.99:80 -> 145.254.160.237:3371    TCP PA (1430 bytes)
    36: 2004/05/13 15:47:12    145.254.160.237:3371 -> 216.239.59.99:80        TCP A
    37: 2004/05/13 15:47:12       65.208.228.223:80 -> 145.254.160.237:3372    TCP PA (424 bytes)
    38: 2004/05/13 15:47:12    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    39: 2004/05/13 15:47:25       65.208.228.223:80 -> 145.254.160.237:3372    TCP FA
    40: 2004/05/13 15:47:25    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
    41: 2004/05/13 15:47:37    145.254.160.237:3372 -> 65.208.228.223:80       TCP FA
    42: 2004/05/13 15:47:37       65.208.228.223:80 -> 145.254.160.237:3372    TCP A
(http.cap) >>>
(http.cap) >>> summary 6
     6: 2004/05/13 15:47:09    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
(http.cap) >>>

The summary command displays a listing of all packets and their most common attributes. When provided an integer argument, it considers that to be the packet index and shows a one-line description for the packet itself. Let's now assume you need to edit packet #6:

(http.cap) >>> edit 6
Editing packet id: 6
     6: 2004/05/13 15:47:09    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
(http.cap|#6) >>>
(http.cap|#6) >>> set ether.src 11:22:33:44:55
     6: Ether.src: 00:00:01:00:00:00 -> 11:22:33:44:55
(http.cap|#6) >>>
(http.cap|#6) >>> set ether.dst 55:44:33:22:11
     6: Ether.dst: fe:ff:20:00:01:00 -> 55:44:33:22:11
(http.cap|#6) >>>
(http.cap|#6) >>> set ether.type 2048
     6: Ether.type: 2048 -> 2048
(http.cap|#6) >>>
(http.cap|#6) >>> set tcp.sport 1234
     6: TCP.sport: 3372 -> 1234
(http.cap|#6) >>>
(http.cap|#6) >>> set tcp.dport 4321
     6: TCP.dport: 80 -> 4321
(http.cap|#6) >>>
(http.cap|#6) >>> ls
dst        : DestMACField         = '55:44:33:22:11' (None)
src        : SourceMACField       = '11:22:33:44:55' (None)
type       : XShortEnumField      = 2048            (0)
--
version    : BitField             = 4L              (4)
ihl        : BitField             = 5L              (None)
tos        : XByteField           = 0               (0)
len        : ShortField           = 40              (None)
id         : ShortField           = 3910            (1)
flags      : FlagsField           = 2L              (0)
frag       : BitField             = 0L              (0)
ttl        : ByteField            = 128             (64)
proto      : ByteEnumField        = 6               (0)
chksum     : XShortField          = None            (None)
src        : Emph                 = '145.254.160.237' (None)
dst        : Emph                 = '65.208.228.223' ('127.0.0.1')
options    : PacketListField      = []              ([])
--
sport      : ShortEnumField       = 3372            (20)
dport      : ShortEnumField       = 4321            (80)
seq        : IntField             = 951058419       (0)
ack        : IntField             = 290219760       (0)
dataofs    : BitField             = 5L              (None)
reserved   : BitField             = 0L              (0)
flags      : FlagsField           = 16L             (2)
window     : ShortField           = 9660            (8192)
chksum     : XShortField          = None            (None)
urgptr     : ShortField           = 0               (0)
options    : TCPOptionsField      = {}              ({})
None
(http.cap|#6) >>>

So we selected the packet id, provided the respective protocol field names, new values and used the ls command to glance over changes. Since all looks good, we can now request pcapedit to save these changes, which it will then write to a filename.mod.pcap named file (you can override the default name and provide new name via the outpcap command):

(http.cap|#6) >>> save
Wrote 1 packet(s) to http.mod.cap
(http.cap|#6) >>>
(http.cap|#6) >>> q
$
$ ls -l *.cap
-rwxrwxr-x 1 shiv shiv 25803 Nov 15 12:22 http.cap
-rw-rw-r-- 1 shiv shiv    94 Nov 15 14:31 http.mod.cap

One thing to note here is that the save command only write one packet (#6) to the file and skip the rest. This is because pcapedit is a context-sensitive tool, ie. it takes current context into consideration while executing commands. Since at the time of issuing save command, the user was in per-packet editing mode (for packet #6), it intentionally skipped other packets. This is an immensely powerful feature as packets can now be edited individually and extracted from source pcap file to a new pcap file. However, if one wishes to save all packets, all they have to do is issue the back command to exit per-packet editing mode and then issue save command which will now honor the context and work as expected:

(http.cap|#6) >>> back
(http.cap) >>>
(http.cap) >>> save
Wrote 43 packet(s) to http.mod.cap
(http.cap) >>> q
$
$ ls -l *.cap
-rw-rw-r-- 1 shiv shiv 25803 Nov 15 12:22 http.cap
-rw-rw-r-- 1 shiv shiv 25901 Nov 15 14:09 http.mod.cap

Here's a screenshot of Wireshark parsing and decoding the edited packet:

pcapedit-wireshark.png

So at this point we know how to edit individual packets and save them to a new file, but doing this for hundreds or thousands of packets would still remain a daunting task and something that needs automation. That's where the scriptfile feature comes handy. You can save commands to a file and pass them as input to pcapedit and it will happily do as instructed:

$ ./pcapedit.py <tcpcmdstest.txt
PcapEdit - An Interactive Pcap Editor

>>> Read 43 packets from http.cap
(http.cap) >>>
(http.cap) >>> Editing packet id: 6
     6: 2004/05/13 15:47:09    145.254.160.237:3372 -> 65.208.228.223:80       TCP A
(http.cap|#6) >>>
(http.cap|#6) >>>      6: Ether.src: 00:00:01:00:00:00 -> 11:22:33:44:55
(http.cap|#6) >>>      6: Ether.dst: fe:ff:20:00:01:00 -> 55:44:33:22:11
(http.cap|#6) >>>      6: Ether.type: 2048 -> 2048
(http.cap|#6) >>>
(http.cap|#6) >>>      6: IP.version: 4 -> 88
(http.cap|#6) >>>      6: IP.ihl: 5 -> 56
(http.cap|#6) >>>      6: IP.tos: 0 -> 33
(http.cap|#6) >>>      6: IP.len: 40 -> 36
(http.cap|#6) >>>      6: IP.id: 3910 -> 12
(http.cap|#6) >>>      6: IP.flags: 2 -> 81
(http.cap|#6) >>>      6: IP.frag: 0 -> 22
(http.cap|#6) >>>      6: IP.ttl: 128 -> 27
(http.cap|#6) >>>      6: IP.proto: 6 -> 15
(http.cap|#6) >>>      6: IP.chksum: None -> 78
(http.cap|#6) >>>      6: IP.src: 145.254.160.237 -> 12
(http.cap|#6) >>>      6: IP.dst: 65.208.228.223 -> 21
(http.cap|#6) >>>      6: IP.options: [] -> ['99']
(http.cap|#6) >>>
(http.cap|#6) >>>      6: TCP.sport: 3372 -> 1234
(http.cap|#6) >>>      6: TCP.dport: 80 -> 4321
(http.cap|#6) >>>      6: TCP.seq: 951058419 -> 11
(http.cap|#6) >>>      6: TCP.ack: 290219760 -> 12
(http.cap|#6) >>>      6: TCP.dataofs: 5 -> 1
(http.cap|#6) >>>      6: TCP.reserved: 0 -> 9
(http.cap|#6) >>>      6: TCP.flags: 16 -> 90
(http.cap|#6) >>>      6: TCP.window: 9660 -> 36
(http.cap|#6) >>>      6: TCP.chksum: None -> 88
(http.cap|#6) >>>      6: TCP.urgptr: 0 -> 67
(http.cap|#6) >>>      6: TCP.options: {} -> {}
(http.cap|#6) >>>
(http.cap|#6) >>>      6: 2004/05/13 15:47:09                 12:1234 -> 21:4321                 TCP SPAE
(http.cap|#6) >>>
(http.cap|#6) >>> Wrote 11 packet(s) to http.mod.cap
(http.cap|#6) >>>
(http.cap|#6) >>> q
$
$ ls -l *.cap
-rwxrwxr-x 1 shiv shiv 25803 Nov 15 12:22 http.cap
-rw-rw-r-- 1 shiv shiv  6805 Nov 15 14:44 http.mod.cap
$

Conclusion

There are a few other nifty commands that will be useful and I strongly recommend you to give pcapedit a try.


« Little PDF Puzzle from Didier ... «

» Flowinspect: A Network Inspect... »

  