{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# Scapy"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"##### Suraj Deshmukh (surajssd009005@gmail.com)\n",
"##### http://deshmukhsuraj.wordpress.com/\n",
"#### @surajssd009005"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## What is Scapy?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"- An interactive tool; that lets you send, sniff, craft and manipulate packets\n",
"- Craft and decode packets of wide number of protocols\n",
"- You can do network scans, traceroutes, arpspoofs, so almost all tasks of nmap and tcpdump\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Why use Scapy(and not other tools)?"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"- Scapy is a very flexible tool to use(you'll know when you do hands-on).\n",
"- Uses Python as a tool usage language, hence easier to use.\n",
"- Break open tool designer's perspective."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Tool designer's perspective"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"- Computers are good at decoding but not so good at interpreting.\n",
"- So a programmer while designing tool makes his tool mimic interpretation.\n",
"- e.g. When a packet is sent on a TCP port with SYN flag and response comes back as SYN-ACK then port is open\n",
"- They show the result in a way what original author thought was appropriate, though it is helpful for beginners.\n",
"- So much information is lost in the process."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"- But computer networks has so many protocols, so permutation of doing things increase\n",
"- So somebody would like to play in some unique way with protocols what nobody has ever thought about\n",
"- But playing with networks is not as easy job\n",
"- Need to write 100's of lines of C code"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"#### enter Scapy..."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"- just a single line to send packet of your choice"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## In a Nutshell"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Lets start some packets rolling..."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "slide"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"WARNING: No route found for IPv6 destination :: (no default route?)\n",
"WARNING:scapy.runtime:No route found for IPv6 destination :: (no default route?)\n"
]
}
],
"source": [
"from scapy.all import *"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Basics"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"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 = hopopt\n",
" chksum = None\n",
" src = 127.0.0.1\n",
" dst = 127.0.0.1\n",
" \\options \\\n"
]
}
],
"source": [
"ip = IP()\n",
"ip.show()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"###[ IP ]###\n",
" version = 4\n",
" ihl = None\n",
" tos = 0x0\n",
" len = None\n",
" id = 1\n",
" flags = \n",
" frag = 0\n",
" ttl = 100\n",
" proto = hopopt\n",
" chksum = None\n",
" src = 127.0.0.1\n",
" dst = 127.0.0.1\n",
" \\options \\\n"
]
}
],
"source": [
"ip.ttl = 100\n",
"ip.show()"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"###[ IP ]###\n",
" version = 4\n",
" ihl = None\n",
" tos = 0x0\n",
" len = None\n",
" id = 1\n",
" flags = \n",
" frag = 0\n",
" ttl = 100\n",
" proto = hopopt\n",
" chksum = None\n",
" src = 192.168.0.60\n",
" dst = 192.168.0.1\n",
" \\options \\\n"
]
}
],
"source": [
"ip.dst = '192.168.0.1'\n",
"ip.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Encapsulating Layers"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"data": {
"text/plain": [
">"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ip = IP(dst='192.168.0.1')\n",
"icmp = ICMP()\n",
"pkt = ip/icmp\n",
"pkt"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"data": {
"text/plain": [
">"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pkt = IP(dst='www.google.com')/TCP(dport=80)\n",
"pkt"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"data": {
"text/plain": [
">>"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pkt = Ether()/IP()/TCP()\n",
"pkt"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Send and Receive Packets"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Sent 1 packets.\n"
]
}
],
"source": [
"send(IP()/ICMP())"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Received 21 packets, got 1 answers, remaining 0 packets\n",
"Begin emission:\n",
"Finished to send 1 packets.\n"
]
},
{
"data": {
"text/plain": [
">"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"p = sr1(IP(dst='192.168.0.1')/ICMP())\n",
"p"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Received 29 packets, got 2 answers, remaining 0 packets\n",
"0000 IP / TCP 192.168.0.60:ftp_data > 192.168.0.1:domain S ==> IP / TCP 192.168.0.1:domain > 192.168.0.60:ftp_data RA\n",
"0001 IP / TCP 192.168.0.60:ftp_data > 192.168.0.1:http S ==> IP / TCP 192.168.0.1:http > 192.168.0.60:ftp_data SA\n",
"Begin emission:\n",
"Finished to send 2 packets.\n"
]
}
],
"source": [
"ans, unans = sr(IP(dst='192.168.0.1')/TCP(dport=[53, 80]))\n",
"ans.show() "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Ping"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"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 = hopopt\n",
" chksum = None\n",
" src = 127.0.0.1\n",
" dst = 127.0.0.1\n",
" \\options \\\n"
]
}
],
"source": [
"ip = IP()\n",
"ip.show()"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"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 = hopopt\n",
" chksum = None\n",
" src = 192.168.0.60\n",
" dst = 192.168.0.1\n",
" \\options \\\n"
]
}
],
"source": [
"# default gateway\n",
"ip.dst = '192.168.0.1'\n",
"ip.show()"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"###[ ICMP ]###\n",
" type = echo-request\n",
" code = 0\n",
" chksum = None\n",
" id = 0x0\n",
" seq = 0x0\n"
]
}
],
"source": [
"icmp = ICMP()\n",
"icmp.show()"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Received 62 packets, got 1 answers, remaining 0 packets\n",
"Begin emission:\n",
"Finished to send 1 packets.\n"
]
},
{
"data": {
"text/plain": [
">"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# send and receive, stops after receiving a single reply\n",
"# store the reply in variable named 'gateway'\n",
"gateway = sr1(ip/icmp)\n",
"gateway"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"###[ IP ]###\n",
" version = 4L\n",
" ihl = 5L\n",
" tos = 0x0\n",
" len = 28\n",
" id = 1\n",
" flags = \n",
" frag = 0L\n",
" ttl = 30\n",
" proto = icmp\n",
" chksum = 0x1b53\n",
" src = 192.168.0.1\n",
" dst = 192.168.0.60\n",
" \\options \\\n",
"###[ ICMP ]###\n",
" type = echo-reply\n",
" code = 0\n",
" chksum = 0xffff\n",
" id = 0x0\n",
" seq = 0x0\n"
]
}
],
"source": [
"gateway.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Traceroute"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Received 41 packets, got 1 answers, remaining 0 packets\n",
"###[ IP ]###\n",
" version = 4L\n",
" ihl = 5L\n",
" tos = 0x0\n",
" len = 56\n",
" id = 0\n",
" flags = \n",
" frag = 0L\n",
" ttl = 30\n",
" proto = icmp\n",
" chksum = 0x1b38\n",
" src = 192.168.0.1\n",
" dst = 192.168.0.60\n",
" \\options \\\n",
"###[ ICMP ]###\n",
" type = time-exceeded\n",
" code = ttl-zero-during-transit\n",
" chksum = 0x9111\n",
" unused = 8634536\n",
"###[ IP in ICMP ]###\n",
" version = 4L\n",
" ihl = 5L\n",
" tos = 0x0\n",
" len = 28\n",
" id = 1\n",
" flags = \n",
" frag = 0L\n",
" ttl = 0\n",
" proto = udp\n",
" chksum = 0x5dad\n",
" src = 192.168.0.60\n",
" dst = 216.58.196.4\n",
" \\options \\\n",
"###[ UDP in ICMP ]###\n",
" sport = domain\n",
" dport = domain\n",
" len = 8\n",
" chksum = 0xa250\n",
"Begin emission:\n",
"Finished to send 1 packets.\n"
]
}
],
"source": [
"# Traceroute works by sending packets each time with decreasing 'ttl' value\n",
"reply = sr1(IP(dst='www.google.com', ttl=1)/UDP())\n",
"reply.show()"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"data": {
"text/plain": [
"'192.168.0.1'"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"reply.src"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Received 33 packets, got 22 answers, remaining 38 packets\n",
" 106.10.139.246:tcp80 204.79.197.200:tcp80 216.58.220.4:tcp80 \n",
"1 192.168.0.1 11 192.168.0.1 11 192.168.0.1 11 \n",
"2 172.31.28.250 11 - 172.31.28.250 11 \n",
"12 - 204.79.197.200 SA - \n",
"13 - 204.79.197.200 SA - \n",
"14 - 204.79.197.200 SA - \n",
"15 - 204.79.197.200 SA - \n",
"16 106.10.139.246 SA 204.79.197.200 SA - \n",
"17 106.10.139.246 SA 204.79.197.200 SA - \n",
"18 106.10.139.246 SA 204.79.197.200 SA 216.58.220.4 SA \n",
"19 106.10.139.246 SA 204.79.197.200 SA 216.58.220.4 SA \n",
"20 106.10.139.246 SA 204.79.197.200 SA 216.58.220.4 SA \n",
"Begin emission:\n",
"Finished to send 60 packets.\n"
]
}
],
"source": [
"ans, unans = traceroute([\"www.google.com\",\"www.yahoo.com\",\"www.bing.com\"],maxttl=20)"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 106.10.139.246:tcp80 204.79.197.200:tcp80 216.58.220.4:tcp80 \n",
"1 192.168.0.1 11 192.168.0.1 11 192.168.0.1 11 \n",
"2 172.31.28.250 11 - 172.31.28.250 11 \n",
"12 - 204.79.197.200 SA - \n",
"13 - 204.79.197.200 SA - \n",
"14 - 204.79.197.200 SA - \n",
"15 - 204.79.197.200 SA - \n",
"16 106.10.139.246 SA 204.79.197.200 SA - \n",
"17 106.10.139.246 SA 204.79.197.200 SA - \n",
"18 106.10.139.246 SA 204.79.197.200 SA 216.58.220.4 SA \n",
"19 106.10.139.246 SA 204.79.197.200 SA 216.58.220.4 SA \n",
"20 106.10.139.246 SA 204.79.197.200 SA 216.58.220.4 SA \n"
]
}
],
"source": [
"ans.show()"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"ans.graph()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Sniff"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ether / IP / UDP / DNS Qry \"fonts.googleapis.com.\" \n",
"Ether / IP / UDP / DNS Qry \"fonts.googleapis.com.\" \n",
"Ether / IP / UDP / DNS Qry \"fonts.googleapis.com.\" \n",
"Ether / IP / UDP / DNS Qry \"fonts.googleapis.com.\" \n",
"Ether / IP / UDP / DNS Qry \"fonts.googleapis.com.\" \n",
"Ether / IP / UDP / DNS Qry \"tiles.services.mozilla.com.\" \n",
"Ether / IP / UDP / DNS Qry \"tiles.services.mozilla.com.\" \n",
"Ether / IP / UDP / DNS Qry \"tiles.services.mozilla.com.\" \n",
"Ether / IP / UDP / DNS Ans \"tiles.r53-2.services.mozilla.com.\" \n",
"Ether / IP / TCP 192.168.0.60:37661 > 52.25.165.135:https S\n"
]
}
],
"source": [
"a = sniff(iface='wlan0', count=10, filter='ip')\n",
"a.summary()"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:5b:b6:95 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw\n",
"Ether / IP / UDP / DNS Ans \"216.58.196.3\" \n",
"Ether / IP / TCP 192.168.0.60:46113 > 216.58.196.3:http S\n",
"Ether / IP / UDP / DNS Ans \"216.58.196.3\" \n"
]
},
{
"data": {
"text/plain": [
""
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sniff(iface=\"wlan0\", prn=lambda x: x.summary(), count=10)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## Capture and Store Packets"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 14:dd:a9:b0:f9:75 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 1c:65:9d:94:e7:c8 / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n",
"802.3 c4:12:f5:04:1b:82 > 90:21:81:67:f5:fb / LLC / Raw\n"
]
}
],
"source": [
"pkts = sniff(iface=\"wlan0\", prn=lambda x: x.summary(), count=20)"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"collapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"wrpcap('temp.pcap', pkts)"
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"collapsed": false,
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"pkts = rdpcap(\"temp.pcap\")"
]
}
],
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"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.6"
}
},
"nbformat": 4,
"nbformat_minor": 0
}