Inspecting packets
Get detailed description of the packet along with datatypes
>>> packet = IP()/TCP()
>>> ls(packet)
version : BitField = 4 (4)
ihl : BitField = None (None)
tos : XByteField = 0 (0)
len : ShortField = None (None)
id : ShortField = 1 (1)
flags : FlagsField = 0 (0)
frag : BitField = 0 (0)
ttl : ByteField = 64 (64)
proto : ByteEnumField = 6 (0)
chksum : XShortField = None (None)
src : Emph = '127.0.0.1' (None)
dst : Emph = '127.0.0.1' ('127.0.0.1')
options : PacketListField = [] ([])
[-- snipped --]
show()
Displays detailed headers but does not assemble the packet
>>> packet.show()
###[ IP ]###
version= 4
ihl= None
len= None
[...]
proto= hopopt
chksum= None
src= 192.168.1.100
dst= Net('8.8.8.8/30')
show2
Similar to show() but also assembles the packet and calculates the checksums and IHL.
>>> packet.show2()
###[ IP ]###
version= 4L
ihl= 5L
[...]
ttl= 64
proto= hopopt
chksum= 0xa8cd
src= 192.168.1.100
dst= 8.8.8.8
Get only user supplied values
>>> b.hide_defaults( )
summary
- Display short & interesting summary of a packet.
>>> packet.summary()
'Ether / IP / TCP 192.168.1.100:ftp_data > 8.8.8.8:domain S'
nsummary
- Display short & interesting summary of a packet with numbering.
>>> pkts[0].nsummary()
0000 IP / TCP 192.168.1.103:ftp_data > 198.58.109.32:tcpmux S ==> IP / TCP 198.58.109.32:tcpmux > 192.168.1.103:ftp_data SA
0001 IP / TCP 192.168.1.103:ftp_data > 198.58.109.32:3128 S ==> IP / TCP 198.58.109.32:3128 > 192.168.1.103:ftp_data SA
0002 IP / TCP 192.168.1.103:ftp_data > 198.58.109.32:http_alt S ==> IP / TCP 198.58.109.32:http_alt > 192.168.1.103:ftp_data SA
summary()
and nsummary()
supports advanced features such as:
- Filtering packets by individual header field values using
lfilter
argument. - Printing only necessary parts of packet using
prn
argument.
>>> egadz[0].nsummary(lfilter= lambda (s,r): r[TCP].sport == 3128 or r[TCP].sport==1)
0000 IP / TCP 192.168.1.103:ftp_data > 198.58.109.32:tcpmux S ==> IP / TCP 198.58.109.32:tcpmux > 192.168.1.103:ftp_data SA
0001 IP / TCP 192.168.1.103:ftp_data > 198.58.109.32:3128 S ==> IP / TCP 198.58.109.32:3128 > 192.168.1.103:ftp_data SA
>>> egadz[0].nsummary(lfilter= lambda (s,r): r[TCP].sport == 3128, prn = lambda (s,r): s.dst)
0001 198.58.109.32
Interacting with fields inside packet
To access a specific field: [packet_name].[field]
>>> packet.dst
'd8:55:a3:fe:80:78'
For fields that are not unique [packet_name][proto].[field]
>>> packet[Ether].dst
'd8:55:a3:fe:80:78'
>>> packet[IP].dst
'8.8.8.8'
.payload
ignores the lowest layer and parses the next layer.
>>> packet.payload.flags
0
>>> packet.payload.payload.flags
2
Checking for presence of layer in packet
haslayer method
checks for presence of a layer in a packet
>>> if packet.haslayer(TCP):
... print packet[TCP].flags
...
2
>>>
Using an in
construct
>>> pkt = IP()/TCP()/DNS()
>>>
>>> DNS in pkt
True
Scapy’s sprintf
sprintf()
method is one of the very powerful features of Scapy.sprintf
comes very handy while writing custom tools.sprintf
fills a format string with values from the packet , much like it sprintf from C Library, except here it fills the format string with field values from packets.
>>> packet.sprintf("Ethernet source is %Ether.src% and IP proto is %IP.proto%")
'Ethernet source is 00:00:00:00:00:00 and IP proto is icmp'
>>> a=Ether( )/Dot1Q(vlan=42)/IP(dst="192.168.0.1")/TCP(flags="RA")
>>>
>>> a.sprintf("%dst% %IP.dst% vlan=%Dot1Q.vlan%")
'00:00:d4:ae:3f:71 192.168.0.1 vlan=42'
>>>
>>>a.sprintf(" %TCP.flags% | %5s,TCP.flags% | %#05xr,TCP.flags%")
' RA | RA | 0x014'
>>> res.nsummary(lfilter = lambda (s,r): r[TCP].flags & 2)
0008 IP / TCP 192.168.5.20:ftp-data > 192.168.5.22:discard S ==>
IP / TCP 192.168.5.22:discard > 192.168.5.20:ftp-data SA / Padding
>>> res.nsummary(lfilter = lambda (s,r): r[TCP].flags & 2, prn = lambda (s,r):s.dport)
0008 9
0012 13
0021 22
0024 25
Packet handlers
In the below example, we used lambda function to write a packet handler that can handle TCP packets but this function does not work with anything other than TCP packets.
>>> f=lambda x:x.sprintf("%IP.dst%:%TCP.dport%")
>>> f(IP(dst='8.8.8.8')/TCP())
'8.8.8.8:www'
>>> f(IP('8.8.8.8')/UDP())
'8.8.8.8:??'
Having a function that can work with various packets can be helpful in practical senarios, we can achieve this using conditional substrings in sprintf(). A conditional substring is only triggered when a layer is present in the packet or else it is ignored. You can also use !
for checking the absence of a layer.
>>> f=lambda x: x.sprintf("=> {IP:ip=%IP.dst% {UDP:dport=%UDP.dport%}\
... {TCP:%TCP.dport%/%TCP.flags%}{ICMP:type=%r,ICMP.type%}}\
... {!IP:not an IP packet}")
>>>
>>> f(IP()/TCP())
'=> ip=127.0.0.1 http/S'
>>>
>>> f(IP()/UDP())
'=> ip=127.0.0.1 dport=domain'
>>>
>>> f(IP()/ICMP())
'=> ip=127.0.0.1 type=8'
>>>
>>> f(Ether()/ARP())
'=> not an IP packet'
Python’s format method
- Python string format method generates beautiful output but unlike sprintf it prints literal values.
>>> "Ether source is: {} & IP proto is: {}".format(packet.src, packet.proto)
'Ether source is: 00:00:00:00:00:00 & IP proto is: 1'