Scripting Packet Analysis and Modification


Thomas Whitton


2013/01/10

What you should take away from this presentation?

  1. What it is possible to do and how easy it is.
  2. An overview of the techniques and libraries available to you.
  3. A comparison of these techniques and libraries i.e. when you should use what?

Why the hell do we want to know about scripting packet analysis and modification?

The idea behind using scripting to analyse and modify packet data is that tools can created for a very specific task that other people may not have thought up yet.

We can't always rely on someone having the exact same problem and creating an opensource tool for the job.

And with using a scripting language the tool can probably be a couple of lines of code, plus everyone knows at least one scripting language (right?).

Packet Analysis

Why not use current tools?

For 99% of your packet analysis Wireshark does a pretty damn good job

A couple of cases in which it might be preferable to use a scripting language:

  • Wireshark/Tshark cannot be installed on a system easily and the pcap (from a tcpdump) can't be copied over
  • The scenario you need to find is way too complicated to be caught by a standard Wireshark filter

Packet Modification

Why not use current tools?

There are some cool tools available for packet modification, however, none are feature rich and as widely used as wireshark.

They are all for very specific tasks, where as we might like to do something very different.

Scripting Techniques and Libraries

  • dpkt - A python library for dealing with packet data
  • scapy - A widely used python library for analysing the network and more
  • packetfu - A ruby library for dealing with packet data

Example Use Cases

  • Editing a pcap file only changing specific packets
  • Finding a very specific set of packets in a tcp stream
  • Hand crafting packets
  • Sending packets without using tcprelay
  • Sending and receiving packets and then doing something with them
  • Combine the above to create custom tools to help us to further automate our processes

dpkt


Advantages
  • Works
  • asn.1 decoder


Disadvantages
  • Lacks features
  • Too verbose
  • Development stopped
  • No documentation

dpkt examples


 import dpkt
 from dpkt.asn1 import decode
 from dpkt.ip import IP
 from dpkt.icmp import ICMP
 from ipaddr import *

 def pcap_test():
     with open("test.pcap") as f:
         pcap = dpkt.pcap.Reader(f)
         for ts, buf in pcap:
             eth = dpkt.ethernet.Ethernet(buf)
             ip = eth.data
             if type(eth.data) == dpkt.ip.IP:
                 src = IPAddress(Bytes(ip.src))
                 dst = IPAddress(Bytes(ip.dst))
                 print str(src) + " -> " + str(dst)
                 tcp = ip.data
                 port = tcp.dport
                 print "%s, %d" % (port, len(tcp.data))
                 if tcp.dport == 80 and len(tcp.data) > 0:
                     http = dpkt.http.Request(tcp.data)
                       

                        

 def creation_test():
     ip = IP(src=v4_int_to_packed(IPAddress("5.6.7.8")._ip), dst=v4_int_to_packed(IPAddress("1.2.3.4")._ip), p=1)
     print ip.v # 4
     print ip.src # '\x01\x02\x03\x04'
     print ip.data # ''
     icmp = ICMP(type=8, data=ICMP.Echo(id=123, seq=1, data='foobar'))
     print icmp # ICMP(type=8, data=Echo(id=123, seq=1, data='foobar'))
     print len(icmp) # 15
     ip.data = icmp
     ip.len += len(ip.data)
     print ip
     # IP(src='\x01\x02\x03\x04', dst='\x05\x06\x07\x08', len=34, p=1, data=ICMP(type=8, data=Echo(id=123, seq=1, data='foobar')))
     pkt = str(ip)
     print pkt # 'E\x00\x00"\x00\x00\x00\x00@\x01j\xc8\x01\x02\x03\x04\x05\x06\x07\x08\x08\x00\xc0?\x00{\x00\x01foobar'
     print IP(pkt) # IP(src='\x01\x02\x03\x04', dst='\x05\x06\x07\x08', sum=27336, len=34, p=1, data=ICMP(sum=49215, type=8, data=Echo(id=123, seq=1, data='foobar')))
                    

 def asn1_test():
     s = '0\x82\x02Q\x02\x01\x0bc\x82\x02J\x04xcn=Douglas J Song 1, ou=Information Technology Division, ou=Faculty and Staff, ou=People, o=University of Michigan, c=US\n\x01\x00\n\x01\x03\x02\x01\x00\x02\x01\x00\x01\x01\x00\x87\x0bobjectclass0\x82\x01\xb0\x04\rmemberOfGroup\x04\x03acl\x04\x02cn\x04\x05title\x04\rpostalAddress\x05\x0ftelephoneNumber\x04\x04mail\x04\x06member\x04\thomePhone\x04\x11homePostalAddress\x04\x0bobjectClass\x04\x0bdescription\x04\x18facsimileTelephoneNumber\x04\x05pager\x04\x03uid\x04\x0cuserPassword\x04\x08joinable\x04\x10associatedDomain\x04\x05owner\x04\x0erfc822ErrorsTo\x04\x08ErrorsTo\x04\x10rfc822RequestsTo\x04\nRequestsTo\x04\tmoderator\x04\nlabeledURL\x04\nonVacation\x04\x0fvacationMessage\x04\x05drink\x04\x0elastModifiedBy\x04\x10lastModifiedTime\x04\rmodifiersname\x04\x0fmodifytimestamp\x04\x0ccreatorsname\x04\x0fcreatetimestamp'
     print decode(s) # [(48, [(2, 11), (99, [(4, 'cn=Douglas J Song 1, ou=Information Technology Division, ou=Faculty and Staff, ou=People, o=University of Michigan, c=US'), (10, '\x00'), (10, '\x03'), (2, 0), (2, 0), (1, '\x00'), (135, 'objectclass'), (48, [(4, 'memberOfGroup'), (4, 'acl'), (4, 'cn'), (4, 'title'), (4, 'postalAddress'), (4, 'telephoneNumber'), (4, 'mail'), (4, 'member'), (4, 'homePhone'), (4, 'homePostalAddress'), (4, 'objectClass'), (4, 'description'), (4, 'facsimileTelephoneNumber'), (4, 'pager'), (4, 'uid'), (4, 'userPassword'), (4, 'joinable'), (4, 'associatedDomain'), (4, 'owner'), (4, 'rfc822ErrorsTo'), (4, 'ErrorsTo'), (4, 'rfc822RequestsTo'), (4, 'RequestsTo'), (4, 'moderator'), (4, 'labeledURL'), (4, 'onVacation'), (4, 'vacationMessage'), (4, 'drink'), (4, 'lastModifiedBy'), (4, 'lastModifiedTime'), (4, 'modifiersname'), (4, 'modifytimestamp'), (4, 'creatorsname'), (4, 'createtimestamp')])])])]
                    

scapy


Advantages
  • Simple to use
  • Tons of features
  • Widely used
  • Good documentation


Disadvantages
  • Not really actively developed

Scapy Examples


 from scapy.all import *

 def pcap_test():
     pkts = rdpcap("test.pcap")
     pkts[0][IP].src = '1.8.3.5'
     del(pkts[0][TCP].chksum)
     del(pkts[0][IP].chksum)
     pkts[0].show2()
     wrpcap("result.pcap", pkts[0])
                    

 def show_test():
     # requires root privelages
     pkts = sniff(iface="wlan0", filter="icmp", count=10)
     print len(pkts)
     pkts[0].show()

                    


 def sniff_test():
     # requires root privelages
     # same filter expression used by wireshark capture filters and tcpdump
     pkts = sniff(iface="wlan0", filter="icmp", count=10)
     pkts = sniff(iface="wlan0", filter="tcp and host 192.168.1.8", count=10, prn=pkt_print)
     def pkt_print(pkt):
         hexdump(pkt)
         print pkt[IP].src + "->" + pkt[IP].dst
                    

 def create_test():
     a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 \n\n"
     a=Ether()/IP(dst="www.slashdot.org")/TCP()/Raw(load="x")
                    


 def send_test():
     send(IP(dst="1.2.3.4")/ICMP())
     sendp(rdpcap("test.pcap")) # tcpreplay
     # inter is sleep time between packets and loop > 0 means carry on until keyboard interupt
     sendp("I'm travelling on Ethernet", iface="eth1", loop=1, inter=0.2)
     # fuzz randomises default values
     send(IP(dst="target")/fuzz(UDP()/NTP(version=4)),loop=1)
                    

 def sending_and_receiving_test():
     p1=sr1(IP(dst="www.slashdot.org")/ICMP()/"XXXXXXXXXXX")
     p=sr(IP(dst="www.slashdot.org")/ICMP()/"XXXXXXXXXXX")
                    


 def filter_test():
     pkts = rdpcap("test.pcap")
     pkts.filter(lambda x: TCP in x)
                    

packetfu


Advantages
  • Clean API
  • Simple to use
  • Actively developed
  • Good documentation


Disadvantages
  • Lacks features
  • Uses Ruby :)

Packetfu Examples


 require 'packetfu'
 include PacketFu
 iface = "wlan0"

 def sniff_test(iface)
   # requires root privelages
   cap = Capture.new(:iface => iface, :start => true)
   cap.stream.each do |p|
     pkt = Packet.parse p
     if pkt.is_ip?
       next if pkt.ip_saddr == Utils.ifconfig(iface)[:ip_saddr]
       packet_info = [pkt.ip_saddr, pkt.ip_daddr, pkt.size, pkt.proto.last]
       puts "%-15s -> %-15s %-4d %s" % packet_info
     end
   end
 end
                     

 def creation_test()
   pkt = TCPPacket.new
   pkt.ip_saddr = "1.2.3.4"
   pkt.tcp_spot = "80"
   pkt.recalc
 end
                     


 def send_test()
   pkt = IPPacket.new
   pkt.ip_id = 0xbabe
   pkt.ip_saddr = "10.20.30.40"
   pkt.ip_daddr = "10.1.2.3"
   pkt.payload = "hello"
   pkt.recalc
   3.times { pkt.to_w("wlan0") }
 end
                     

 def hex_test()
   pkt = TCPPacket.new
   pkt.ip_saddr = "1.2.3.4"
   pkt.tcp_sport = "80"
   pkt.recalc
   pkt.tcp_recalc
   # prints out the hex of the packet
   puts pkt.inspect_hex
 end
                     


 def read_and_write_pcap_test()
   # use as singleton
   pfile = PcapFile.new
   pkts = pfile.read_packets("test.pcap")
   pfile.array_to_file(:filename => "result.pcap", :array => pkts)
 end