With scapy we can to this :
p = Ether() / IP()
What kind of operation does '/' really do ? What kind of object does it return ? If I type p in a python interpreter it returns this
<Ether ... | IP ...>
The / operator has been used as a composition operator between two layers. When doing so, the lower layer can have one or more of its defaults fields overloaded according to the upper layer. (You still can give the value you want). A string can be used as a raw layer.,For the moment, we have only generated one packet. Let see how to specify sets of packets as easily. Each field of the whole packet (ever layers) can be a set. This implicitly defines a set of packets, generated using a kind of cartesian product between all the fields.,Depending on the driver, the commands needed to get a working frame injection interface may vary. You may also have to replace the first pseudo-layer (in the example RadioTap()) by PrismHeader(), or by a proprietary pseudo-layer, or even to remove it.,In case we want to do some expert analysis of responses, we can use the following command to indicate which ports are open:
$ sudo scapy - H Welcome to Scapy(2.4 .0) >>>
C: \ > scapy
Welcome to Scapy(2.4 .0) >>>
INFO: Can 't import python matplotlib wrapper. Won'
t be able to plot.
INFO: Can 't import PyX. Won'
t be able to use psdump() or pdfdump().
DefaultTheme, BrightTheme, RastaTheme, ColorOnBlackTheme, BlackAndWhite, HTMLTheme, LatexTheme
conf.color_theme = BrightTheme()
>>> a = IP(ttl = 10) >>>
a <
IP ttl = 10 |>
>>>
a.src’ 127.0 .0 .1’ >>>
a.dst = "192.168.1.1" >>>
a <
IP ttl = 10 dst = 192.168 .1 .1 |>
>>>
a.src’ 192.168 .8 .14’ >>>
del(a.ttl) >>>
a <
IP dst = 192.168 .1 .1 |>
>>>
a.ttl
64
Each field (such as the Ethernet dst value or ICMP type value) is a key:value pair in the appropriate layer. These fields (and nested layers) are all mutable so we can reassign them in place using the assignment operator. Scapy has packet methods for viewing the layers and fields that I will introduce next.,Scapy uses Python dictionaries as the data structure for packets. Each packet is a collection of nested dictionaries with each layer being a child dictionary of the previous layer, built from the lowest layer up. Visualizing the nested packet layers would look something like this:,Getting the value of the list returns a quick glance at what type of packets were sniffed.,Scapy builds and dissects packets by the layers contained in each packet, and then by the fields in each layer. Each layer is nested inside the parent layer as can be seen with the nesting of the < and > brackets:
Now let's go back to our pkt
and have some fun with it using Scapy's Interactive mode. We already know that using the summary()
method will give us a quick look at the packet's layers:
>>> pkt[0].summary()
'Ether / IP / ICMP 172.16.20.10 > 4.2.2.1 echo-request 0 / Raw'
show()
method is for:
>>> pkt[0].show() # # #[Ethernet] # # # dst = 00: 24: 97: 2 e: d6: c0 src = 00: 00: 16: aa: bb: cc type = 0x800 # # #[IP] # # # version = 4 L ihl = 5 L tos = 0x0 len = 84 id = 57299 flags = frag = 0 L ttl = 64 proto = icmp chksum = 0x0 src = 172.16 .20 .10 dst = 4.2 .2 .1\ options\ # # #[ICMP] # # # type = echo - request code = 0 chksum = 0xd8af id = 0x9057 seq = 0x0 # # #[Raw] # # #
Very cool, that's some good info. If you're familiar with Python you have probably noticed the list index, [0]
, after the pkt
variable name. Remember that our sniff only returned a single packet, but if we increase the count
argument value, we will get back an list with multiple packets:
>>> pkts = sniff(count=10)
>>> pkts
<Sniffed: TCP:0 UDP:0 ICMP:10 Other:0>
And we can show the summary or packet contents of any single packet by using the list index with that packet value. So, let's look at the contents of the 4th packet (Remember, list indexes start counting at 0):
>>> pkts[3] <Ether dst=00:00:16:aa:bb:cc src=00:24:97:2e:d6:c0 type=0x800 |<IP version=4L ihl=5L tos=0x20 len=84 id=47340 flags=frag=0L ttl=57 proto=icmp chksum=0x3826 src=4.2.2.1 dst=172.16.20.10 options=[] |<ICMP type=echo-reply code=0 chksum=0xcfbf id=0x3060 seq=0x1 |<Raw |>>>>
The show()
method will give us a cleaner print out:
>>> pkts[3].show() # # #[Ethernet] # # # dst = 00: 00: 16: aa: bb: cc src = 00: 24: 97: 2 e: d6: c0 type = 0x800 # # #[IP] # # # version = 4 L ihl = 5 L tos = 0x20 len = 84 id = 47340 flags = frag = 0 L ttl = 57 proto = icmp chksum = 0x3826 src = 4.2 .2 .1 dst = 172.16 .20 .10\ options\ # # #[ICMP] # # # type = echo - reply code = 0 chksum = 0xcfbf id = 0x3060 seq = 0x1 # # #[Raw] # # #
Scapy uses the python interpreter as a command board. That means that you can use directly python language (assign variables, use loops, define functions, etc.) , We can easily plot some harvested values using Gnuplot. For example, we can observe the IP ID patterns to know how many distinct IP stacks are used behind a load balancer : , Provided that your wireless card and driver are correctly configured for frame injection , Now, let's manipulate some packets. Here you can see layers that are supported for the moment. It's really easy to add one.
First, we play a bit and create 4 IP packets at once. Let's see how it works. We first instantiate the IP class. Then, we instantiate it again and we provide a destination that is worth 4 IP addresses (/30 gives the netmask). Using a Python idiom, we develop this implicit packet in a set of explicit packets.
# ./scapy.py Welcome to Scapy (0.9.17.108beta) >>> IP() <IP |> >>> target="www.target.com" >>> target="www.target.com/30" >>> ip=IP(dst=target) >>> ip <IP dst=<Net www.target.com/30> |> >>> [p for p in ip] [<IP dst=207.171.175.28 |>, <IP dst=207.171.175.29 |>, <IP dst=207.171.175.30 |>, <IP dst=207.171.175.31 |>]
The configuration is hold into a variable named conf, which is saved in the session.
>>> conf
Version = 0.9.17.108beta
BTsocket = <class __main__.BluetoothSocket at 0xb7d73f2c>
IPCountry_base = 'GeoIPCountry4Scapy.gz'
L2listen = <class __main__.L2ListenSocket at 0xb7d73e3c>
L2socket = <class __main__.L2Socket at 0xb7d73e0c>
L3socket = <class __main__.L3PacketSocket at 0xb7d73ddc>
checkIPID = 1
checkIPaddr = 1
checkIPsrc = 1
color_theme = <class __main__.HTMLTheme at 0xb7d263ec>
countryLoc_base = 'countryLoc.csv'
debug_dissector = 0
debug_match = 0
except_filter = ''
gnuplot_world = 'world.dat'
histfile = '/home/pbi/.scapy_history'
iface = 'eth1'
nmap_base = '/usr/share/nmap/nmap-os-fingerprints'
p0f_base = '/etc/p0f.fp'
padding = 1
promisc = 1
prompt = '>>> '
queso_base = '/etc/queso.conf'
route =
Network Netmask Gateway Iface Output IP
127.0.0.0 255.0.0.0 0.0.0.0 lo 127.0.0.1
192.168.5.0 255.255.255.0 0.0.0.0 eth1 192.168.5.21
0.0.0.0 0.0.0.0 192.168.5.1 eth1 192.168.5.21
session = 'mysession'
session_tracking = {}
sniff_promisc = 1
stealth = 'not implemented'
verb = 2
warning_threshold = 5
wepkey = ''
>>> conf.verb=1
>>> conf.color_theme=RastaTheme()
>>> IP() <IP |> >>> a=IP(dst="172.16.1.40") >>> a <IP dst=172.16.1.40 |> >>> a.dst '172.16.1.40' >>> a.ttl 64
>>> a.ttl=32
>>> a
<IP ttl=32 dst=172.16.1.40 |>
>>> del(a.ttl)
>>> a
<IP dst=172.16.1.40 |>
>>> a.ttl
64
>>> t=TCP() >>> t.flags="SA" >>> t.flags 18 >>> t <TCP flags=SA |> >>> t.flags=23 >>> t <TCP flags=FSRA |> >>> i=IP(flags="DF+MF") >>> i.flags 3 >>> i <IP flags=MF+DF |> >>> i.flags=6 >>> i <IP flags=DF+evil |>
>>> a.dst
'172.16.1.40'
>>> a.src
'172.16.1.24'
>>> del(a.dst)
>>> a.dst
'127.0.0.1'
>>> a.src
'127.0.0.1'
>>> a.dst="192.168.11.10"
>>> a.src
'192.168.11.1'
>>> a.dst=target
>>> a.src
'172.16.1.24'
>>> a.src="1.2.3.4"
>>> a
<IP src=1.2.3.4 dst=<Net www.target.com> |>
2.1 Integer Type Sizes2.2 Byte Encoding2.3 Host and Network Byte Order2.4 Converting Between Encodings,It also has a layering operator: ,Note that a string (or byte string) can also be indexed like a list and iterated over.,2.3 Host and Network Byte Order
#include <stdio.h>
int main(int argc, char** argv) {
for ( int i = 0; i < 10; ++i ) {
printf("i = %d\n",i);
}
return 0;
}
main:
.LFB0:
.cfi_startproc
endbr64
pushq % rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq % rsp, % rbp
.cfi_def_cfa_register 6
subq $32, % rsp
movl % edi, -20( % rbp)
movq % rsi, -32( % rbp)
movl $0, -4( % rbp)
jmp.L2
.L3:
movl - 4( % rbp), % eax
movl % eax, % esi
leaq.LC0( % rip), % rdi
movl $0, % eax
call printf @PLT
addl $1, -4( % rbp)
.L2:
cmpl $9, -4( % rbp)
jle.L3
movl $0, % eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
(gdb) disassemble/r main
Dump of assembler code for function main:
0x0000000000001149 <+0>: f3 0f 1e fa endbr64
0x000000000000114d <+4>: 55 push %rbp
0x000000000000114e <+5>: 48 89 e5 mov %rsp,%rbp
0x0000000000001151 <+8>: 48 83 ec 20 sub $0x20,%rsp
0x0000000000001155 <+12>: 89 7d ec mov %edi,-0x14(%rbp)
0x0000000000001158 <+15>: 48 89 75 e0 mov %rsi,-0x20(%rbp)
0x000000000000115c <+19>: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
0x0000000000001163 <+26>: eb 1a jmp 0x117f <main+54>
0x0000000000001165 <+28>: 8b 45 fc mov -0x4(%rbp),%eax
0x0000000000001168 <+31>: 89 c6 mov %eax,%esi
0x000000000000116a <+33>: 48 8d 3d 93 0e 00 00 lea 0xe93(%rip),%rdi
0x0000000000001171 <+40>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000001176 <+45>: e8 d5 fe ff ff callq 0x1050 <printf@plt>
0x000000000000117b <+50>: 83 45 fc 01 addl $0x1,-0x4(%rbp)
0x000000000000117f <+54>: 83 7d fc 09 cmpl $0x9,-0x4(%rbp)
0x0000000000001183 <+58>: 7e e0 jle 0x1165 <main+28>
0x0000000000001185 <+60>: b8 00 00 00 00 mov $0x0,%eax
0x000000000000118a <+65>: c9 leaveq
0x000000000000118b <+66>: c3 retq
End of assembler dump.
f30f 1 efa 5548 89e5 4883 ec20 897 d ec48 8975 e0c7 45 fc 0000 0000 eb1a 8 b45 fc89 c648 8 d3d 930 e 0000 b800 0000 00e8 d5fe ffff 8345 fc01 837 d fc09 7 ee0 b800 0000 00 c9 c3
00001140: f30f 1 efa e977 ffff fff3 0 f1e fa55 4889 00001150: e548 83 ec 2089 7 dec 4889 75e0 c745 fc00 00001160: 0000 00 eb 1 a8b 45 fc 89 c6 488 d 3 d93 0e00 00001170: 00 b8 0000 0000 e8d5 feff ff83 45 fc 0183 00001180: 7 dfc 097 e e0b8 0000 0000 c9c3 0 f1f 4000
(gdb) disassemble main
Dump of assembler code for function main:
=> 0x0000560f61c4b149 <+0>: endbr64
0x0000560f61c4b14d <+4>: push %rbp
0x0000560f61c4b14e <+5>: mov %rsp,%rbp
0x0000560f61c4b151 <+8>: sub $0x20,%rsp
0x0000560f61c4b155 <+12>: mov %edi,-0x14(%rbp)
0x0000560f61c4b158 <+15>: mov %rsi,-0x20(%rbp)
0x0000560f61c4b15c <+19>: movl $0x0,-0x4(%rbp)
0x0000560f61c4b163 <+26>: jmp 0x560f61c4b17f <main+54>
0x0000560f61c4b165 <+28>: mov -0x4(%rbp),%eax
0x0000560f61c4b168 <+31>: mov %eax,%esi
0x0000560f61c4b16a <+33>: lea 0xe93(%rip),%rdi # 0x560f61c4c004
0x0000560f61c4b171 <+40>: mov $0x0,%eax
0x0000560f61c4b176 <+45>: callq 0x560f61c4b050 <printf@plt>
0x0000560f61c4b17b <+50>: addl $0x1,-0x4(%rbp)
0x0000560f61c4b17f <+54>: cmpl $0x9,-0x4(%rbp)
0x0000560f61c4b183 <+58>: jle 0x560f61c4b165 <main+28>
0x0000560f61c4b185 <+60>: mov $0x0,%eax
0x0000560f61c4b18a <+65>: leaveq
0x0000560f61c4b18b <+66>: retq
End of assembler dump.