scapy/doc/notebooks/Scapy in 15 minutes.ipynb

1418 lines
204 KiB
Plaintext
Raw Normal View History

2016-06-02 08:31:22 +00:00
{
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python2",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.11"
},
"name": "",
"signature": "sha256:79fcc04f3ba79fcc12a08e3e6de1e7f6fa95a3c528e45a46cd180c8fc3eed7fd"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Scapy in 15 minutes (or longer)"
]
},
{
"cell_type": "heading",
"level": 5,
"metadata": {},
"source": [
"Guillaume Valadon & Pierre Lalet"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Scapy](http://www.secdev.org/projects/scapy) is a powerful Python-based interactive packet manipulation program and library. It can be used to forge or decode packets for a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more.\n",
"\n",
"This iPython notebook provides a short tour of the main Scapy features. It assumes that you are familiar with networking terminology. All examples where built using the development version from [https://github.com/secdev/scapy](https://github.com/secdev/scapy), and tested on Linux. They should work as well on OS X, and other BSD.\n",
"\n",
"The current documentation is available on [http://scapy.readthedocs.io/](http://scapy.readthedocs.io/) !"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scapy eases network packets manipulation, and allows you to forge complicated packets to perform advanced tests. As a teaser, let's have a look a two examples that are difficult to express without Scapy:\n",
"\n",
"1_ Sending a TCP segment with maximum segment size set to 0 to a specific port is an interesting test to perform against embedded TCP stacks. It can be achieved with the following one-liner:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"send(IP(dst=\"1.2.3.4\")/TCP(dport=502, options=[(\"MSS\", 0)]))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"Sent 1 packets.\n"
]
}
],
"prompt_number": 30
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2_ Adanced firewalking using IP options is sometimes useful to perform network enumeration. Here is more complicate one-liner:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ans = sr([IP(dst=\"8.8.8.8\", ttl=(1, 8), options=IPOption_RR())/ICMP(seq=RandShort()), IP(dst=\"8.8.8.8\", ttl=(1, 8), options=IPOption_Traceroute())/ICMP(seq=RandShort()), IP(dst=\"8.8.8.8\", ttl=(1, 8))/ICMP(seq=RandShort())], verbose=False, timeout=3)[0]\n",
"ans.make_table(lambda (x, y): (\", \".join(z.summary() for z in x[IP].options) or '-', x[IP].ttl, y.sprintf(\"%IP.src% %ICMP.type%\")))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
" - IPOption_RR IPOption_Traceroute \n",
"1 192.168.42.1 time-exceeded 192.168.46.1 time-exceeded 192.168.46.1 time-exceeded \n",
"2 172.42.0.1 time-exceeded 172.42.0.1 time-exceeded 172.42.0.1 time-exceeded \n",
"3 42.10.69.251 time-exceeded 42.10.69.251 time-exceeded 42.10.69.251 time-exceeded \n",
"4 10.123.156.86 time-exceeded 10.123.156.86 time-exceeded - \n",
"5 69.156.98.177 time-exceeded 69.156.98.177 time-exceeded - \n",
"6 69.156.137.74 time-exceeded 69.156.137.74 time-exceeded - \n",
"7 209.85.172.150 time-exceeded - - \n",
"8 216.239.57.203 time-exceeded - - \n"
]
}
],
"prompt_number": 31
},
{
"cell_type": "heading",
"level": 4,
"metadata": {},
"source": [
"Now that, we've got your attention, let's start the tutorial !"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Quick setup"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The easiest way to try Scapy is to clone the github repository, then launch the `run_scapy` script as root. The following examples can be pasted on the Scapy prompt. There is no need to install any external Python modules."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```shell\n",
"git clone https://github.com/secdev/scapy --depth=1\n",
"sudo ./run_scapy\n",
"Welcome to Scapy (2.3.2-dev)\n",
">>>\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note: iPython users must import scapy as follows"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from scapy.all import *"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 13
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"First steps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With Scapy, each network layer is a Python class.\n",
"\n",
"The `'/'` operator is used to bind layers together. Let's put a TCP segment on top of IP and assign it to the `packet` variable, then stack it on top of Ethernet. "
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"packet = IP()/TCP()\n",
"Ether()/packet"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 2,
"text": [
"<Ether type=IPv4 |<IP frag=0 proto=tcp |<TCP |>>>"
]
}
],
"prompt_number": 2
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This last output displays the packet summary. Here, Scapy automatically filled the Ethernet type as well as the IP protocol field.\n",
"\n",
"Protocol fields can be listed using the `ls()` function:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
" >>> ls(IP, verbose=True)\n",
" version : BitField (4 bits) = (4)\n",
" ihl : BitField (4 bits) = (None)\n",
" tos : XByteField = (0)\n",
" len : ShortField = (None)\n",
" id : ShortField = (1)\n",
" flags : FlagsField (3 bits) = (0)\n",
" MF, DF, evil\n",
" frag : BitField (13 bits) = (0)\n",
" ttl : ByteField = (64)\n",
" proto : ByteEnumField = (0)\n",
" chksum : XShortField = (None)\n",
" src : SourceIPField (Emph) = (None)\n",
" dst : DestIPField (Emph) = (None)\n",
" options : PacketListField = ([])"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create a new packet to a specific IP destination. With Scapy, each protocol field can be specified. As shown in the `ls()` output, the interesting field is `dst`.\n",
"\n",
"Scapy packets are objects with some useful methods, such as `summary()`."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"p = Ether()/IP(dst=\"www.secdev.org\")/TCP()\n",
"p.summary()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 3,
"text": [
"\"Ether / IP / TCP 172.20.10.2:ftp_data > Net('www.secdev.org'):http S\""
]
}
],
"prompt_number": 3
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are not many differences with the previous example. However, Scapy used the specific destination to perform some magic tricks !\n",
"\n",
"Using internal mechanisms (such as DNS resolution, routing table and ARP resolution), Scapy has automatically set fields necessary to send the packet. This fields can of course be accessed and displayed."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print p.dst # first layer that has an src field, here Ether\n",
"print p[IP].src # explicitly access the src field of the IP layer\n",
"\n",
"# sprintf() is a useful method to display fields\n",
"print p.sprintf(\"%Ether.src% > %Ether.dst%\\n%IP.src% > %IP.dst%\")"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"3a:71:de:90:0b:64\n",
"172.20.10.2\n",
"b8:e8:56:45:8c:e6 > 3a:71:de:90:0b:64\n",
"172.20.10.2 > Net('www.secdev.org')\n"
]
}
],
"prompt_number": 10
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scapy uses default values that work most of the time. For example, `TCP()` is a SYN segment to port 80."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print p.sprintf(\"%TCP.flags% %TCP.dport%\")"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"S http\n"
]
}
],
"prompt_number": 9
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Moreover, Scapy has implicit packets. For example, they are useful to make the TTL field value vary from 1 to 5 to mimic traceroute."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"[p for p in IP(ttl=(1,5))/ICMP()]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 11,
"text": [
"[<IP frag=0 ttl=1 proto=icmp |<ICMP |>>,\n",
" <IP frag=0 ttl=2 proto=icmp |<ICMP |>>,\n",
" <IP frag=0 ttl=3 proto=icmp |<ICMP |>>,\n",
" <IP frag=0 ttl=4 proto=icmp |<ICMP |>>,\n",
" <IP frag=0 ttl=5 proto=icmp |<ICMP |>>]"
]
}
],
"prompt_number": 11
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Sending and receiving"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Currently, you know how to build packets with Scapy. The next step is to send them over the network !\n",
"\n",
"The `sr1()` function sends a packet and return the corresponding answer. `srp1()` does the same for layer two packets, i.e. Ethernet. If you are only interested in sending packets `send()` is your friend.\n",
"\n",
"As an example, we can use the DNS protocol to get www.example.com IPv4 address."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"sr1(IP(dst=\"8.8.8.8\")/UDP()/DNS(qd=DNSQR()))\n",
"p[DNS].an"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"Received 19 packets, got 1 answers, remaining 0 packets\n",
"Begin emission:\n",
"Finished to send 1 packets.\n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 23,
"text": [
"<DNSRR rrname='www.example.com.' type=A rclass=IN ttl=10011 rdata='93.184.216.34' |>"
]
}
],
"prompt_number": 23
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Another alternative is the `sr()` function. Like `srp1()`, the `sr1()` function can be used for layer 2 packets."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"r, u = srp(Ether()/IP(dst=\"8.8.8.8\", ttl=(5,10))/UDP()/DNS(rd=1, qd=DNSQR(qname=\"www.example.com\")))\n",
"r, u"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"Received 7 packets, got 6 answers, remaining 0 packets\n",
"Begin emission:\n",
"Finished to send 6 packets.\n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 47,
"text": [
"(<Results: TCP:0 UDP:0 ICMP:6 Other:0>,\n",
" <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)"
]
}
],
"prompt_number": 47
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`sr()` sent a list of packets, and returns two variables, here `r` and `u`, where:\n",
"1. `r` is a list of results (i.e tuples of the packet sent and its answer)\n",
"2. `u` is a list of unanswered packets"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Access the first tuple\n",
"print r[0][0].summary() # the packet sent\n",
"print r[0][1].summary() # the answer received\n",
"\n",
"# Access the ICMP layer. Scapy received a time-exceeded error message\n",
"r[0][1][ICMP]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Ether / IP / UDP / DNS Qry \"www.example.com\" \n",
"Ether / IP / ICMP / IPerror / UDPerror / DNS Qry \"www.example.com.\" \n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 48,
"text": [
"<ICMP type=time-exceeded code=ttl-zero-during-transit chksum=0x50d6 reserved=0 length=0 unused=None |<IPerror version=4L ihl=5L tos=0x0 len=61 id=1 flags= frag=0L ttl=1 proto=udp chksum=0xf389 src=172.20.10.2 dst=8.8.8.8 options=[] |<UDPerror sport=domain dport=domain len=41 chksum=0x593a |<DNS id=0 qr=0L opcode=QUERY aa=0L tc=0L rd=1L ra=0L z=0L ad=0L cd=0L rcode=ok qdcount=1 ancount=0 nscount=0 arcount=0 qd=<DNSQR qname='www.example.com.' qtype=A qclass=IN |> an=None ns=None ar=None |>>>>"
]
}
],
"prompt_number": 48
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With Scapy, list of packets, such as `r` or `u`, can be easily written to, or read from PCAP files."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"wrpcap(\"scapy.pcap\", r)\n",
"\n",
"pcap_p = rdpcap(\"scapy.pcap\")\n",
"pcap_p[0]"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 50,
"text": [
"<Ether dst=f4:ce:46:a9:e0:4b src=34:95:db:04:3c:29 type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=61 id=1 flags= frag=0L ttl=5 proto=udp chksum=0xb6e3 src=192.168.46.20 dst=8.8.8.8 options=[] |<UDP sport=domain dport=domain len=41 chksum=0xb609 |<DNS id=0 qr=0L opcode=QUERY aa=0L tc=0L rd=1L ra=0L z=0L ad=0L cd=0L rcode=ok qdcount=1 ancount=0 nscount=0 arcount=0 qd=<DNSQR qname='www.example.com.' qtype=A qclass=IN |> an=None ns=None ar=None |>>>>"
]
}
],
"prompt_number": 50
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Sniffing the network is a straightforward as sending and receiving packets. The `sniff()` function returns a list of Scapy packets, that can be manipulated as previously described."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"s = sniff(count=2)\n",
"s"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 52,
"text": [
"<Sniffed: TCP:0 UDP:2 ICMP:0 Other:0>"
]
}
],
"prompt_number": 52
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`sniff()` has many arguments. The `prn` one accepts a function name that will be called on received packets. Using the `lambda` keyword, Scapy could be used to mimic the `tshark` command behavior."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"sniff(count=2, prn=lambda p: p.summary())"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Ether / IP / TCP 172.20.10.2:52664 > 216.58.208.200:https A\n",
"Ether / IP / TCP 216.58.208.200:https > 172.20.10.2:52664 A\n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 53,
"text": [
"<Sniffed: TCP:2 UDP:0 ICMP:0 Other:0>"
]
}
],
"prompt_number": 53
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Alternatively, Scapy can use OS sockets to send and receive packets. The following example assigns an UDP socket to a Scapy `StreamSocket`, which is then used to query www.example.com IPv4 address.\n",
"Unlike other Scapy sockets, `StreamSockets` do not require root privileges."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import socket\n",
"\n",
"sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # create an UDP socket\n",
"sck.connect((\"8.8.8.8\", 53)) # connect to 8.8.8.8 on 53/UDP\n",
"\n",
"# Create the StreamSocket and gives the class used to decode the answer\n",
"ssck = StreamSocket(sck)\n",
"ssck.basecls = DNS\n",
"\n",
"# Send the DNS query\n",
"ssck.sr1(DNS(rd=1, qd=DNSQR(qname=\"www.example.com\")))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"Received 1 packets, got 1 answers, remaining 0 packets\n",
"Begin emission:\n",
"Finished to send 1 packets.\n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 79,
"text": [
"<DNS id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L ad=0L cd=0L rcode=ok qdcount=1 ancount=1 nscount=0 arcount=0 qd=<DNSQR qname='www.example.com.' qtype=A qclass=IN |> an=<DNSRR rrname='www.example.com.' type=A rclass=IN ttl=19681 rdata='93.184.216.34' |> ns=None ar=None |>"
]
}
],
"prompt_number": 79
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Visualization\n",
"Parts of the following examples require the [matplotlib](http://matplotlib.org/) module."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With `srloop()`, we can send 100 ICMP packets to 8.8.8.8 and 8.8.4.4."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ans, unans = srloop(IP(dst=[\"8.8.8.8\", \"8.8.4.4\"])/ICMP(), inter=.1, timeout=.1, count=100, verbose=False)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": []
}
],
"prompt_number": 25
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Then we can use the results to plot the IP id values."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%matplotlib inline\n",
"ans.multiplot(lambda (x, y): (y[IP].src, (y.time, y[IP].id)), plot_xy=True)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "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\nkkBpmWkA5BfkM2bBGK5vqyAi
"text": [
"<matplotlib.figure.Figure at 0x7f2c23bfbc50>"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 26,
"text": [
"[[<matplotlib.lines.Line2D at 0x7f2c2e113d10>],\n",
" [<matplotlib.lines.Line2D at 0x7f2c2e113f10>]]"
]
}
],
"prompt_number": 26
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `str()` constructor can be used to \"build\" the packet's bytes as they would be sent on the wire."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"pkt = IP() / UDP() / DNS(qd=DNSQR())\n",
"print repr(str(pkt))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"'E\\x00\\x00=\\x00\\x01\\x00\\x00@\\x11|\\xad\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\x005\\x005\\x00)\\xb6\\xd3\\x00\\x00\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x03www\\x07example\\x03com\\x00\\x00\\x01\\x00\\x01'\n"
]
}
],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Since some people cannot read this representation, Scapy can:\n",
" - give a summary for a packet"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print pkt.summary()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"IP / UDP / DNS Qry \"www.example.com\" \n"
]
}
],
"prompt_number": 10
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" - \"hexdump\" the packet's bytes"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"hexdump(pkt)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"0000 45 00 00 3D 00 01 00 00 40 11 7C AD 7F 00 00 01 E..=....@.|.....\n",
"0010 7F 00 00 01 00 35 00 35 00 29 B6 D3 00 00 01 00 .....5.5.)......\n",
"0020 00 01 00 00 00 00 00 00 03 77 77 77 07 65 78 61 .........www.exa\n",
"0030 6D 70 6C 65 03 63 6F 6D 00 00 01 00 01 mple.com.....\n"
]
}
],
"prompt_number": 18
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" - dump the packet, layer by layer, with the values for each field"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"pkt.show()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"###[ IP ]###\n",
" version = 4\n",
" ihl = None\n",
" tos = 0x0\n",
" len = None\n",
" id = 1\n",
" flags = \n",
" frag = 0\n",
" ttl = 64\n",
" proto = udp\n",
" chksum = None\n",
" src = 127.0.0.1\n",
" dst = 127.0.0.1\n",
" \\options \\\n",
"###[ UDP ]###\n",
" sport = domain\n",
" dport = domain\n",
" len = None\n",
" chksum = None\n",
"###[ DNS ]###\n",
" id = 0\n",
" qr = 0\n",
" opcode = QUERY\n",
" aa = 0\n",
" tc = 0\n",
" rd = 1\n",
" ra = 0\n",
" z = 0\n",
" ad = 0\n",
" cd = 0\n",
" rcode = ok\n",
" qdcount = 1\n",
" ancount = 0\n",
" nscount = 0\n",
" arcount = 0\n",
" \\qd \\\n",
" |###[ DNS Question Record ]###\n",
" | qname = 'www.example.com'\n",
" | qtype = A\n",
" | qclass = IN\n",
" an = None\n",
" ns = None\n",
" ar = None\n"
]
}
],
"prompt_number": 11
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" - render a pretty and handy dissection of the packet"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"pkt.canvas_dump()"
],
"language": "python",
"metadata": {
"scrolled": true
},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"png": "iVBORw0KGgoAAAANSUhEUgAAAr4AAAJ7CAIAAACKwAUlAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAA\nHXRFWHRTb2Z0d2FyZQBHUEwgR2hvc3RzY3JpcHQgOS4xOeMCIOUAACAASURBVHic7L1/VBt5duB7\nbbAFwhIuGtoGaYxbjD3I9ExmWgSl09A7E0sTJxOOB+Xht3tOGzrZPJFHP/ymX84ZOO095/XuWRzw\nvrPTA6e9z8xLFmhnZwKJnF4ySXtUTiY2nRkpVPd0ukVp7KbaovUDj7CrKVkl5JbM++MLZSFVlUoq\nCbBdn+PjI331/d6631KhunW/93vvrrW1NVBQUFBQUFBQkEbpdiugoKDwaNB744Y7Eslj4JtHjpi1\nWgB4/fXmPIYfPvzrL7/83wCAYqjJm5N5SJBP15Eug9YAAK83E9uiwAtdB6xn9ABwo3cy4qa2RQfT\n3OsA4Hf6b797O4/hB144oLfqAWCyd5LKawqvz72+/uL1bfoWXjhgteoBYPyX//ute/+yLTpsOy8c\n6LLqzyimg4KCglQ0AIZc+scBvJtb9lY37q2sly4hFiLuP0imtqjh0G7Yk4sWsngAn7OwmNqytxH2\n5jCDAnDvyqa38Wo1bardSgXUvhWtdzm1ZakhNwkHFza9LalWl+cyhfu+lfubFdhbDXsrc9NBJvdS\npnA/mSyB6nIwbakGO4B7sH4tKqaDgoKCVLQA5o3XPpIknM42m61Gr0ctBI775ufRa3Vl5Ynubn+m\n6VBZrz1mAwDS5SJdrvpjx0wWC/dpZuP9FV+aDuVQp4Ia6RKEiDLM9cuX2ZUVdWVlW0dHhVab+tGV\n8fEavb7NZotDON10qAetTfBY0hUQ6Uy6XNU6HXdiM02HgO2Y+HCJOkicgs4xn2Y6BI49fJ15JWRK\nyDQdtLZjvJPlPQmMYz7ddKgEehdJOJ2pjUaz2Wg2Q4FIvQxgs+kAACVQTZNGcQXQeeAkCFGkC6ko\nl+KG6bA7q0QFBQWFTC4NDkYjkeVAgGu55nBEpa1oEDjuGB01ms1zTqdjZESksXgSzp0+za6sGM1m\ndmXl3OnTUYZJn+C5c1s/BQAI+/1jAwOpJzan4dJ1kD8FRNqVkJMEoclKOQmk2+3zPjRNr0xOsnkt\nqIkjchmIK0C6XI7R0Rq9/p2JCRH5O+FvIY9LUfE6KCgo5IxjZMTY0pLZ3myxSHnsc4yOfufNN2v0\neqPZfPbkSfTAyttYPAmHGhtRB6PZ7PN6F0mS07xCq7WdOXNlUiyuokhTuO5wXHM41CkukJyGi7QX\nYwrAdyVIlyA0WYkn4UR394nubvT6Yn9/R1+flMdr6WS9DMQVIF0uY0tLm80m7nLYCX8LeVyKpQCA\n47jIxESwFPR7UlBQeCQI+/0+r/fVCxcyn2aWAwHS5QIAEQPCR5KpLlDT8eNet5uNRDIbhX7s5EsA\ngJ7hYfSCdLmWAwGkMHr8Ums04vetIk0BebbbbLbv9faKHF1kuFB7MaYAfFeCdAUAQGiyEk8CB4Hj\nbCTC3cWjDOMYHfW63dU63SJJvvbWWyJTeGtwcJEk1VotyzAvnT1bbzSC5MtASAECx8cGBtDrK5OT\nrR0dp8+e5R24E/4W8rsUSwHgpT/4g6/+1m8JnhUBfvEP/7D06ae5jlJQUHjUuXTuHPcrmcYcjtc3\nNkYjkXcmJl69cIG3D5uxNBD2+6t1usxGIQXkS0Cg56pFr7ejrw/1vzQ4iO4070xMeN3u4inAK0Fc\nWynDpYstyDnMvBJkzis/3hkft/X1cW+vjI9XaDSDb78NAO9MTIjf/g8bjSe6u2v0egLH3xkf7xke\nln4ZCClgslguzs0hc8p25ozIwJ3wt5DfV1YKAAe/8AXx6fGydPNmrkMUFBQeda47HOgF6XJFIxGf\n18s9stj6+tBDGwC8NTh43eF4RtRVu+2g56oow7zR21uj0/nm59s6OtBcTnR3Xx4d3W4FdzS8V8LW\nq+EjSdjs5bp++fJ/fvtt9FrIxuV4zmK5fvkycpWtD3c4croMMhV4EkgPk7zucHAn8brD4RgZ4f5t\ngfGooKDwSPDOxAR6ICOcTnQLCfv9ZMrzWYVGI/SLcchoTA28CgcC9ceO8TYKHV2+hLDfz935KrTa\nEy+/zO0NQWRGTW7BFESOKHG4dLHyp4BIuxJkzisPSLc7LdiCjUQqpC00RBnm3OnTFRrNS6+9xusk\nE78MhBSQzk74W8jvK0s3HeZwnDMd5nA8Gomg3SY1ev2506e5PzYFBYUnkzab7dULF9A/0/Hjtr4+\n5LNkI5Er4+OoT5RhSLdb6DmsQqttbGlBYec+klz0ek0WC2+jkA7yJSx6vddSfs3mnE60uEtcvYru\nFtxctnIKIkeUOFy6WPlT4L0SZM4rD9iVlbRV/NaOjrcGB9Frx8gIIRzMtxwIqDUaFBWInAcAIP0y\nEFJAOjvhbyG/ryzLDosKjYb7+1drtZcGB8WDRRUUFJ4QvtfbuxwIkG73i4FAm81WbzS2dXScPXmy\nWqdbDgROdHcbzWYhR6Wtr++N3l7S5WIZxj40JNIohEwJJovFNz/PaWs6fhz9sp3o7j53+rRaozlk\nNKI59l74j1s5BQLHrzkciyTpGB1VC8eLiBxLug7yp4BIuxKkSxCarPSTAADhjRDX1Hk5RkfPnjyJ\n4hx/++WXhcbWG43GlhZ0GbAMw0Yigy+99NLZs5mXgYgOmQog/dGjvM/rfdFmE7kZb/vfglBn8W9h\n19ra2ld/8zd7N5ZzvtfbW9/YiB4jUl8DQNjv/w/f/vbFuTn09kJf3y/++Z/FFVJQUHhs6L1x4xeR\nSE22bp+R5P6NiIc4wPLmRNQl6uoSdTX6NLB4R3foqbThaY2fryzW1jba/3AMNhJR74HKXRvZJAPk\nos54KF0CX6MQH7u8XzQ3inRYg88/h5XURNQl1VBSvXGsO6TuKWO6AnyNQkjpfN+7KRE1/dGn7KH1\nTIo37iwefYpnskLtEnumNaqWWdUym5qImqkGcW4skkcPPZyXdnlTIupPPvp0z6EckkEml9nkMpua\niLpEDSVqAIAYy5SrJS1PyOT+8sNE1GPz9lDMuwcOAUCMiZdrVTKFB8g7OmPG3wJf4xZLSOM+eHNO\nRP3OxERrR4f0/goKCo8TjWo1b3vw/n1/PH5YpXp6714A0G1e9zUAaEtK1l8bNn2kf3b9Fz8U8sZi\nTH39cyUlpVzjOtpna2vXb+1lJWUGzaZE2PqWZzP1yWz8/MHnn0Y/1ezR1JSlWz56i5RV6qfKSsrW\np9Ci2TRcyzOct3Hxg3sPEmtf+LV9JaW7snZOpwWq9Os3J3VjLQBgG5/otDxngLc9vnjn/tKK6nD1\n3qe14j15GrVaLgO5qkqlMWg0GUPWkmv3fPdKykvUtWoA0D27eV5aUFWtT6G2kT8FNfMrZvnWcvXh\nau3TGaZAigIAYDCkHF+bqYsgi4v3HjxYO3w4hyEpB4KqjSk8o322rHTj7pmXsDT0LTzWD29jiPXG\nkswXKr6yZ3dZfhJy0iGDliqVHrIuWBBXr6JsWcinJ+L5UVBQeLw5w7egyyQSJz/6CAAaysuHG7IU\nNujq4vH6UpRrcvIVAPjKV06YTGLroXUVdV1Hu3LQeIMZ38yn0U8/f/B5fsNT6bpwNI9RlIuZfOUm\nAHzlRJXJltVxI4b+jDWPUQkm9tHJ7wNAecPTDcOn5ChQY6qpMfFMwe/03/PdS8aS9e31KkzsKdwq\nMIXvn/w+AOwp29N1IcvX1NWVz7fg8dAUFQGApibMxDcF6Vj1Oe9JLAh03P/9j74NAF/a/29aD2bZ\nPFJUsiSiNh0/jqJgBt9+G4XAbI1aCgoKjwTjS0uRZBIArn72GZNI5CFhdnY9Te/Cgku8Z37QcZpY\nJgBgNblKhLen4uLsxBJ6seDKHrFfDGh8PhlZBYDPrs4nmFjB5SdiiWVivcYE7aHzkEA4CDpAA0DI\nG6L9+UjIfggijF4sLGzPtyCf
"prompt_number": 15,
"text": [
"<pyx.canvas.canvas instance at 0x7f2c2e228bd8>"
]
}
],
"prompt_number": 15
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scapy has a `traceroute()` function, which basically runs a `sr(IP(ttl=(1..30))` and creates a `TracerouteResult` object, which is a specific subclass of `SndRcvList()`."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ans, unans = traceroute('www.secdev.org', maxttl=15)"
],
"language": "python",
"metadata": {
"scrolled": true
},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"Begin emission:\n",
"Finished to send 15 packets.\n",
"\n",
"Received 17 packets, got 15 answers, remaining 0 packets\n",
" 217.25.178.5:tcp80 \n",
"1 192.168.46.254 11 \n",
"2 172.28.0.1 11 \n",
"3 80.10.115.251 11 \n",
"4 10.123.205.82 11 \n",
"5 193.252.98.161 11 \n",
"6 193.252.137.74 11 \n",
"7 193.251.132.183 11 \n",
"8 130.117.49.41 11 \n",
"9 154.25.7.150 11 \n",
"10 154.25.7.150 11 \n",
"11 149.6.166.166 11 \n",
"12 149.6.166.166 11 \n",
"13 217.25.178.5 SA \n",
"14 217.25.178.5 SA \n",
"15 217.25.178.5 SA \n"
]
}
],
"prompt_number": 22
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The result can be plotted with `.world_trace()` (this requires GeoIP module and data, from [MaxMind](https://www.maxmind.com/))"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ans.world_trace()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "display_data",
"png": "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\n+vri4cOHWLRoEXr16gUAOHbs
"text": [
"<matplotlib.figure.Figure at 0x7f2c2c0b2fd0>"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 23,
"text": [
"[[<matplotlib.lines.Line2D at 0x7f2c23b62850>]]"
]
}
],
"prompt_number": 23
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `PacketList.make_table()` function can be very helpful. Here is a simple \"port scanner\":"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"ans = sr(IP(dst=[\"scanme.nmap.org\", \"nmap.org\"])/TCP(dport=[22, 80, 443, 31337]), timeout=3, verbose=False)[0]\n",
"ans.extend(sr(IP(dst=[\"scanme.nmap.org\", \"nmap.org\"])/UDP(dport=53)/DNS(qd=DNSQR()), timeout=3, verbose=False)[0])\n",
"ans.make_table(lambda (x, y): (x[IP].dst, x.sprintf('%IP.proto%/{TCP:%r,TCP.dport%}{UDP:%r,UDP.dport%}'), y.sprintf('{TCP:%TCP.flags%}{ICMP:%ICMP.type%}')))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
" 45.33.32.156 45.33.49.119 \n",
"tcp/22 SA SA \n",
"tcp/31337 SA RA \n",
"tcp/443 RA SA \n",
"tcp/80 SA SA \n",
"udp/53 dest-unreach - \n"
]
}
],
"prompt_number": 29
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Implementing a new protocol"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Scapy can be easily extended to support new protocols.\n",
"\n",
"The following example defines DNS over TCP. The `DNSTCP` class inherits from `Packet` and defines two field: the length, and the real DNS message. The `length_of` and `length_from` arguments link the `len` and `dns` fields together. Scapy will be able to automatically compute the `len` value."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"class DNSTCP(Packet):\n",
" name = \"DNS over TCP\"\n",
" \n",
" fields_desc = [ FieldLenField(\"len\", None, fmt=\"!H\", length_of=\"dns\"),\n",
" PacketLenField(\"dns\", 0, DNS, length_from=lambda p: p.len)]\n",
" \n",
" # This method tells Scapy that the next packet must be decoded with DNSTCP\n",
" def guess_payload_class(self, payload):\n",
" return DNSTCP"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 119
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This new packet definition can be direcly used to build a DNS message over TCP."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Build then decode a DNS message over TCP\n",
"DNSTCP(str(DNSTCP(dns=DNS())))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 120,
"text": [
"<DNSTCP len=12 dns=<DNS id=0 qr=0L opcode=QUERY aa=0L tc=0L rd=0L ra=0L z=0L ad=0L cd=0L rcode=ok qdcount=0 ancount=0 nscount=0 arcount=0 |> |>"
]
}
],
"prompt_number": 120
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Modifying the previous `StreamSocket` example to use TCP allows to use the new `DNSCTP` layer easily."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import socket\n",
"\n",
"sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # create an TCP socket\n",
"sck.connect((\"8.8.8.8\", 53)) # connect to 8.8.8.8 on 53/TCP\n",
"\n",
"# Create the StreamSocket and gives the class used to decode the answer\n",
"ssck = StreamSocket(sck)\n",
"ssck.basecls = DNSTCP\n",
"\n",
"# Send the DNS query\n",
"ssck.sr1(DNSTCP(dns=DNS(rd=1, qd=DNSQR(qname=\"www.example.com\"))))"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"\n",
"Received 1 packets, got 1 answers, remaining 0 packets\n",
"Begin emission:\n",
"Finished to send 1 packets.\n"
]
},
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 122,
"text": [
"<DNSTCP len=49 dns=<DNS id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L ad=0L cd=0L rcode=ok qdcount=1 ancount=1 nscount=0 arcount=0 qd=<DNSQR qname='www.example.com.' qtype=A qclass=IN |> an=<DNSRR rrname='www.example.com.' type=A rclass=IN ttl=12101 rdata='93.184.216.34' |> ns=None ar=None |> |>"
]
}
],
"prompt_number": 122
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Scapy as a module"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"So far, Scapy was only used from the command line. It is also a Python module than can be used to build specific network tools, such as ping6.py:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
" from scapy.all import *\n",
" import argparse\n",
"\n",
" parser = argparse.ArgumentParser(description=\"A simple ping6\")\n",
" parser.add_argument(\"ipv6_address\", help=\"An IPv6 address\")\n",
" args = parser.parse_args()\n",
"\n",
" print sr1(IPv6(dst=args.ipv6_address)/ICMPv6EchoRequest(), verbose=0).summary()"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Answering machines"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A lot of attack scenarios look the same: you want to wait for a specific packet, then send an answer to trigger the attack.\n",
"\n",
"To this extent, Scapy provides the `AnsweringMachine` object. Two methods are especially useful:\n",
"1. `is_request()`: return True if the `pkt` is the expected request\n",
"2. `make_reply()`: return the packet that must be sent\n",
"\n",
"The following example uses Scapy Wi-Fi capabilities to pretend that a \"Scapy !\" access point exists.\n",
"\n",
"Note: your Wi-Fi interface must be set to monitor mode !"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Specify the Wi-Fi monitor interface\n",
"#conf.iface = \"mon0\" # uncomment to test\n",
"\n",
"# Create an answering machine\n",
"class ProbeRequest_am(AnsweringMachine):\n",
" function_name = \"pram\"\n",
"\n",
" # The fake mac of the fake access point\n",
" mac = \"00:11:22:33:44:55\"\n",
"\n",
" def is_request(self, pkt):\n",
" return Dot11ProbeReq in pkt\n",
"\n",
" def make_reply(self, req):\n",
"\n",
" rep = RadioTap()\n",
" # Note: depending on your Wi-Fi card, you might need a different header than RadioTap()\n",
" rep /= Dot11(addr1=req.addr2, addr2=self.mac, addr3=self.mac, ID=RandShort(), SC=RandShort())\n",
" rep /= Dot11ProbeResp(cap=\"ESS\", timestamp=time.time())\n",
" rep /= Dot11Elt(ID=\"SSID\",info=\"Scapy !\")\n",
" rep /= Dot11Elt(ID=\"Rates\",info='\\x82\\x84\\x0b\\x16\\x96')\n",
" rep /= Dot11Elt(ID=\"DSset\",info=chr(10))\n",
"\n",
" OK,return rep\n",
"\n",
"# Start the answering machine\n",
"#ProbeRequest_am()() # uncomment to test"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 129
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Cheap Man-in-the-middle with NFQUEUE"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"NFQUEUE is an iptables target than can be used to transfer packets to userland process. As a nfqueue module is available in Python, you can take advantage of this Linux feature to perform Scapy based MiTM.\n",
"\n",
"This example intercepts ICMP Echo request messages sent to 8.8.8.8, sent with the ping command, and modify their sequence numbers. In order to pass packets to Scapy, the following `iptable` command put packets into the NFQUEUE #2807:\n",
"\n",
"$ sudo iptables -I OUTPUT --destination 8.8.8.8 -p icmp -o eth0 -j NFQUEUE --queue-num 2807"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
" from scapy.all import *\n",
" import nfqueue, socket\n",
"\n",
" def scapy_cb(i, payload):\n",
" s = payload.get_data() # get and parse the packet\n",
" p = IP(s)\n",
"\n",
" # Check if the packet is an ICMP Echo Request to 8.8.8.8\n",
" if p.dst == \"8.8.8.8\" and ICMP in p:\n",
" # Delete checksums to force Scapy to compute them\n",
" del(p[IP].chksum, p[ICMP].chksum)\n",
" \n",
" # Set the ICMP sequence number to 0\n",
" p[ICMP].seq = 0\n",
" \n",
" # Let the modified packet go through\n",
" ret = payload.set_verdict_modified(nfqueue.NF_ACCEPT, str(p), len(p))\n",
" \n",
" else:\n",
" # Accept all packets\n",
" payload.set_verdict(nfqueue.NF_ACCEPT)\n",
"\n",
" # Get an NFQUEUE handler\n",
" q = nfqueue.queue()\n",
" # Set the function that will be call on each received packet\n",
" q.set_callback(scapy_cb)\n",
" # Open the queue & start parsing packes\n",
" q.fast_open(2807, socket.AF_INET)\n",
" q.try_run()"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Automaton"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When more logic is needed, Scapy provides a clever way abstraction to define an automaton. In a nutshell, you need to define an object that inherits from `Automaton`, and implement specific methods:\n",
"- states: using the `@ATMT.state` decorator. They usually do nothing\n",
"- conditions: using the `@ATMT.condition` and `@ATMT.receive_condition` decorators. They describe how to go from one state to another\n",
"- actions: using the `ATMT.action` decorator. They describe what to do, like sending a back, when changing state"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following example does nothing more than trying to mimic a TCP scanner:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"class TCPScanner(Automaton):\n",
"\n",
" @ATMT.state(initial=1)\n",
" def BEGIN(self):\n",
" pass\n",
"\n",
" @ATMT.state()\n",
" def SYN(self):\n",
" print \"-> SYN\"\n",
"\n",
" @ATMT.state()\n",
" def SYN_ACK(self):\n",
" print \"<- SYN/ACK\"\n",
" raise self.END()\n",
"\n",
" @ATMT.state()\n",
" def RST(self):\n",
" print \"<- RST\"\n",
" raise self.END()\n",
"\n",
" @ATMT.state()\n",
" def ERROR(self):\n",
" print \"!! ERROR\"\n",
" raise self.END()\n",
" @ATMT.state(final=1)\n",
" def END(self):\n",
" pass\n",
" \n",
" @ATMT.condition(BEGIN)\n",
" def condition_BEGIN(self):\n",
" raise self.SYN()\n",
"\n",
" @ATMT.condition(SYN)\n",
" def condition_SYN(self):\n",
"\n",
" if random.randint(0, 1):\n",
" raise self.SYN_ACK()\n",
" else:\n",
" raise self.RST()\n",
"\n",
" @ATMT.timeout(SYN, 1)\n",
" def timeout_SYN(self):\n",
" raise self.ERROR()\n",
"\n",
"TCPScanner().run()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"-> SYN\n",
"<- SYN/ACK\n"
]
}
],
"prompt_number": 6
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"TCPScanner().run()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"-> SYN\n",
"<- RST\n"
]
}
],
"prompt_number": 7
2016-07-07 15:01:29 +00:00
},
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Pipes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pipes are an advanced Scapy feature that aims sniffing, modifying and printing packets. The API provides several buildings blocks. All of them, have high entries and exits (>>) as well as low (>) ones.\n",
"\n",
"For example, the `CliFeeder` is used to send message from the Python command line to a low exit. It can be combined to the `InjectSink` that reads message on its low entry and inject them to the specified network interface. These blocks can be combined as follows:"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"# Instanciate the blocks\n",
"clf = CLIFeeder()\n",
"ijs = InjectSink(\"enx3495db043a28\")\n",
"\n",
"# Plug blocks together\n",
"clf > ijs\n",
"\n",
"# Create and start the engine\n",
"pe = PipeEngine(clf)\n",
"pe.start()"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"Packet can be sent using the following command on the prompt:\n",
"\n",
"clf.send(\"Hello Scapy !\")"
]
2016-06-02 08:31:22 +00:00
}
],
"metadata": {}
}
]
2016-07-07 15:01:29 +00:00
}