Using Lua Dissectors in Wireshark

In the previous post, we saw how to write a simple script to “fuzz” the Version field in the packet headers of a BGP Open message. Now, how do we make sure it does the right thing without visually inspecting every packet?

Validating the Fuzz

Using tshark (the command-line tool bundled with Wireshark), we can find each of the BGP Open Message packets:

tshark -n -r bgp.pcap -R 'bgp && bgp.type==1'

Those arguments tell tshark to:

  • Disable all name resolution (-n). While this keeps well-known port numbers from being resolved via the listings in the /etc/services file on Linux and Mac OS X or the %WINDIR%\system32\drivers\etc\services file on Windows systems, it also prevents DNS lookups which can significantly increase the command’s execution time and are unimportant in this case.
  • Read packets from the bgp.pcap file (-r) that we generated in the previous post. And finally,
  • Use a packet filter (-R), in Wireshark display filter syntax, to select BGP packets that are of the “Open” type. The filters that you pass to tshark as arguments to the -R option are the same as what you might type into the Filter field in the Wireshark GUI.

Sample output:

  5 0.004354000    127.0.0.1 -> 127.0.0.1    BGP 85 OPEN Message
  7 0.004570000    127.0.0.1 -> 127.0.0.1    BGP 85 OPEN Message
  9 0.004625000    127.0.0.1 -> 127.0.0.1    BGP 85 OPEN Message

Next, we need to extract the value of the version field and count how many times each version shows up. There should be 256 individual occurrences, one for each version (0 through 255). The command we should be able to use is this:

tshark -n -r bgp.pcap -R 'bgp && bgp.type==1' \
  -T fields -e bgp.version

The new command-line options that we added indicate:

  • the output should only display the values of fields we specify (-T fields), and
  • the only field we want to see is the BGP Version field (-e bgp.version).

Only we’ve encountered a minor problem.

Paraphrased output:
poop

When you use tshark to display the BGP Version, it gives no output other than a newline character for each matching packet. It turns out that the current version of Wireshark (1.10.6 at the time of writing) does not expose the full BGP dissector data for use in a Wireshark/tshark display filter. Bummer.

Choose Your Own Adventure

At this point, we have a few options to consider.

Option One: Eyeballs

Use Wireshark to visually verify all packets. Not very efficient, and not realistic for large numbers of packets. For example, what if we send a packet containing each version number dozens or hundreds of times in an attempt to provoke bad behavior on the behalf of a router or other network device?

Option Two: Kludge

Use tshark with the –V option to obtain verbose output, then parse the resulting text using other command-line tools.

For example:

tshark -n -V -r bgp.pcap -R 'bgp && bgp.type==1' | \
  sed -e '/^ *Type: OPEN/,/ *Version:/!d' \
      -e '/^ *Type:/d' \
      -e 's/^.*: //' | \
  sort -n | \
  uniq -c

The above:

  1. Takes our previous tshark command and makes it really verbose (-V)
  2. Pipes the output of that to sed, invoking multiple sed commands against that output:
    1. Remove lines that don’t match the multi-line string beginning with Type and ending with Version. If we just tried to grep for the Version, we would also get the “Version: 4” string from the IP header.
    2. Now that we have just two lines, remove the Type line.
    3. Remove everything from the remaining string except the version number itself.
  3. Pipes the output from the sed command into sort, using the -n option to specify that we want the output sorted numerically. And then,
  4. Pipes the sorted list to the uniq command, using the -c option to count the number of unique occurrences of each version.

Sample output:

   1 0
   1 1
   1 2
   etc...

This does work, but if it becomes necessary to analyze additional fields in each packet — perhaps in order to troubleshoot or extend the functionality of the fuzzer — it would require a lot more juggling, and the command pipeline is already pretty ugly. It seems reasonable to think that since tshark can already display the value as part of the verbose output, there should also be a way to tell tshark to specifically display that value. But alas, not without some additional hacking.

Option Three: Clever Incomprehensibility

Write a Bash (v4 or newer) script that iterates over every possible value and use an additional tshark filter to obtain the data, like so:

#!/usr/bin/env bash

usage() {
  echo "usage: $0 <pcapfile>"
  exit 1
}

[ -z "${1}" ] && usage || PCAP=$1

i=0
while ((i <= 16#FF)); do # loop from 0 to 255
  X=$(printf "%X\n" $i)
  COUNT=`tshark -n -r ${PCAP} \
    -R "bgp && bgp.type==1 && bgp[19:1]==${X#0X}" | \
    wc -l | \
    awk '{ print $1 }'`
  echo "BGP OPEN packets specifying version ${i}: ${COUNT}"
  ((i=i+1))
done

exit

Sample output:

  BGP OPEN packets specifying version 0: 1
  BGP OPEN packets specifying version 1: 1
  BGP OPEN packets specifying version 2: 1
  etc...

The new display filter we added (bgp[19:1]) grabs one byte 19 bytes from the beginning of the BGP Open message, i.e. where the “type” field resides. This is also a possibility, but it is ugly, slow, and even more cryptic. It dances around the data rather than grab it directly, the way you can for other BGP fields.

Option Four: A New Beginning Dissector

Lastly, write our own BGP dissector in Lua. This offers a more “natural” interface to the data, using mechanisms native to tshark. Unfortunately, even with the current stable version of Wireshark, there doesn’t appear to be a way to simply extend an existing protocol dissector. But we may want to do other customizations and may run into similar issues with other fields, so this seems to be the best (or at least most exciting, using a geek’s definition of “exciting”) way to proceed.

A Simplified BGP Dissector in Lua

Here’s what a simple BGP Dissector written in Lua might look like. Note the parallels to the Node.js fuzzer code from the previous post. (Click to expand the example.)

-- mybgp.lua

-- a simplified BGP OPEN message dissector
-- this only looks at a few specific fields;
-- add others as needed

do
  -- create the dissector
  local mybgp = Proto("mybgp", "BGP Open Dissector")

  -- initialize the dissector
  function mybgp.init()
  end

  -- tell wireshark how to parse and display the packet data
  local f = mybgp.fields

  local types = {
    [1] = "OPEN",
    [2] = "UPDATE",
    [3] = "NOTIFICATION",
    [4] = "KEEPALIVE"
  }

  f.marker   = ProtoField.bytes("mybgp.marker", "Marker", base.HEX)
  f.length   = ProtoField.uint16("mybgp.length", "Length", base.DEC)
  f.type     = ProtoField.uint8("mybgp.type", "Type", nil, types)
  f.version  = ProtoField.uint8("mybgp.version", "Version", base.DEC)
  f.as       = ProtoField.uint16("mybgp.as", "Autonomous System", base.DEC)
  f.holdtime = ProtoField.uint16("mybgp.holdtime", "Hold Time", base.DEC)
  f.id       = ProtoField.uint32("mybgp.id", "BGP Identifier", base.DEC)
  f.parmlen  = ProtoField.uint8("mybgp.parmlen", "Optional Parameters Length", base.DEC)
  f.parms    = ProtoField.bytes("mybgp.parms", "Optional Parameters", base.HEX)

  -- called for each packet
  function mybgp.dissector(buffer, pinfo, tree)
    local subtree = tree:add(mybgp, buffer())
    local offset = 0
    
    local marker = buffer(offset, 16)
    subtree:add(f.marker, marker)
    offset = offset + 16

    local length = buffer(offset, 2)
    subtree:add(f.length, length)
    offset = offset + 2

    local type = buffer(offset, 1)
    subtree:add(f.type, type)
    offset = offset + 1

    local version = buffer(offset, 1)
    subtree:add(f.version, version)
    offset = offset + 1

    local as = buffer(offset, 2)
    subtree:add(f.as, as)
    offset = offset + 2

    local holdtime = buffer(offset, 2)
    subtree:add(f.holdtime, holdtime)
    offset = offset + 2

    local id = buffer(offset, 4)
    subtree:add(f.id, id)
    offset = offset + 4

    local parmlen = buffer(offset, 1)
    subtree:add(f.parmlen, parmlen)
    offset = offset + 1

    local parms = buffer(offset, tonumber(parmlen))
    subtree:add(f.parms, parms)
  end

  local tcp_table = DissectorTable.get("tcp.port")
  tcp_table:add(179, mybgp)
end

If you save the above in a file named mybgp.lua and place it in your plugins directory, tshark will automatically load that file and use it whenever TCP packets sent to port 179 are encountered during packet dissection.

With this file in place, it finally allows us to do what we want: select only the BGP version.

tshark –n -r bgp.pcap \
  -R 'mybgp && mybgp.type==1' \
  -T fields -e mybgp.version | \
  sort -n | \
  uniq -c

Note the use of mybgp instead of bgp in the example above. That’s because as long as that Lua script exists in the Wireshark plugins directory, it overrides the existing BGP dissector provided by Wireshark. If we don’t want that sort of behavior all the time, we can put the script somewhere else and specifically invoke the custom dissector on a per-case basis using the -X command line switch:

tshark -n -r bgp.pcap \
  -X lua_script:/path/to/mybgp.lua \
  -R 'mybgp && mybgp.type==1' \
  -T fields \
  -e mybgp.version | \
  sort -n | \
  uniq -c

Sample output from either of the previous two commands:

   1 0
   1 1
   1 2
   etc...

Whatever Works

The first three options might be more than adequate depending on your particular needs. Ideally, the tshark display filter would have just worked, and in the spirit of open source we should probably write a patch to change that behavior and submit it to the Wireshark project. However, a dissector written in Lua provides an interim solution that can be extended and used as needed, without requiring clumsy text parsing (at least not as much) or recompilation of the Wireshark source code.

Most importantly, we are now sure that our BGP fuzzer is working as intended, sending fuzzed version numbers for BGP Open messages. Hurray!