SCAPY packet-crafting reference

Primitives

List possible parameters for a protocol
Specify a list of ports, IPs, or something
t.dport=[135,139,445]
Specify a range of ports
t.dport=(110,120)
Specify a list of ranges
ic.type=[(0,7),(9,12)]
Different ways of looking at the packet or header you are building

Ways to set parameters

On the fly
send(IP(dst="1.2.3.4")/TCP(dport=25)
A layer at a time

Use cases

Send a SYN to port 80, watch for response
sr1(IP(dst="1.2.3.4")/TCP(dport=80, flags="S"))
Snipe a TCP connection
  1. Using tcpdump, observe the traffic on the connection you are going to snipe. Learn the following:
    • source IP
    • dest IP
    • source TCP port
    • dest TCP port
    • last TCP seq number sent from source to dest
  2. Now use Scapy to craft a packet from that src IP and port to that dest IP and port with flags RST/ACK and seq number being one more than the last one seen. You do have to get the sequence number correct or the RST will be ineffective.
Build a Layer 2 frame containing TCP SYN to port 445 and send, displaying the first packet received in response
Build a L3 frame containing a SYN
Send a frame out a particular interface
sendp(fra, iface="eth2")
Send a layer 2 ping, watch for answer
sr1(Ether(dst="AA:BB:CC:DD:EE:FF")/IP(dst="10.1.2.3")/ICMP(type=8))
Sniff packets, examine
sniff(filter="tcp and host 192.168.3.1", count=4, iface="eth0")


ans = _         <-- sets ans equal to last result (which is an array)
ans.summary()   <-- displays summary info on the array contents

s=sniff(filter="ICMP", count=2, iface="eth0")

s.summary()
ls req[0]
print req[0]
Inspect traffic in a pcap file
Replay a pcap file

Reasons to build a Layer 2 frame

Example: send a frame "eee" out interface eth2 five times: sendp([eee]*5, iface="eth2")

Sniffing for a PING, crafting the reply

>>> s = sniff(filter="host 10.3.4.4 and icmp[0]=8, count=1)
>>> req = s[0]
>>> resp_ip = IP(src=req.payload.dst, dst=req.payload.src)
>>> resp_icmp = ICMP(type=0)
>>> send(resp_ip/resp_icmp)

Note that the kernel would also automatically respond to the PING if it thinks it should (use iptables to suppress the reponse if necessary).

Performing a TCP 3whs (three way handshake)

Cooked vs. Raw sockets

Normally, the Linux kernel takes care of setting up and sending and receiving network traffic. It automatically sets appropriate header values and even knows how to complete a TCP 3 way handshake. Uising the kernel services in this way is using a cooked socket.

Scapy does not use these kernel services. It creates a raw socket. The entire TCP/IP stack of the OS is circumvented. Because of this, Scapy give us compete control over the traffic. Traffic to and from Scapy will not be filtered by iptables. Also, we will have to take care of the TCP 3 way handshake ourselves.

Sending a TCP SYN

If we use Scapy to craft a TCP SYN and send it, watching traffic using TCPdump, we will see the following three packets:

Where did the RST come from, if we didn't make it in Scapy? Our kernel sent it automatically. It did not send the SYN (Scapy did using a raw socket), so when the kernel saw the SYN/ACK it responded with a RST.

Using iptables to suppress kernel responses to traffic we elicit

This behavior is probably undesirable. To stop it, we can use iptables on our host to suppress outbound RSTs to the dest we are working with. We'd have a similar problem with UDP; a UDP packet coming back to us would cause our kernel to generate an ICMP port unreachable message. even though we might be expecting the UDP packet in Scapy. So we might want to use iptables commands like the following:

iptables -A OUTPUT -p tcp --tcp-flags RST RST -s {our IP} -d {dest IP} -dport {port we're sending from} -j DROP
iptables -A OUTPUT -s {our IP} -d {dest IP} -p ICMP --icmp-type port-unreachable -j DROP

TCP 3 way handshake reference

Client and server variable names:

 c_ip = IP, c_port = port, c_seq = seq #, c_ack = ack #
 s_ip = IP, s_port = port, s_seq = seq #, s_ack = ack #

Handshake:
c_ip:c_port -> s_ip:s_port, SYN, c_seq, c_ack
s:ip:s_port -> c_ip:c_port, SYN/ACK, s_seq, s_ack=(c_seq + 1)
c_ip:c_port -> s_ip:s_port, ACK, c_seq + 1, c_ack=(s_seq + 1)

Note that from the client side we know everything in advance except the s_seq, which we must learn by listening for it.

If we send the SYN from Scapy using the sr1() method, we can have Scapy parse the s_seq out of the return packet for us:

synack=sr1(ip/syn)
my_ack=synack.seq + 1

A Python script to make the 3 way handshake

#!/usr/bin/python
from scapy.all import *

ip=IP(src="10..1.2.3", dst="10.2.3.4")
SYN=TCP(sport=1500, dport=80, flags="S", seq=100)
SYNACK=sr1(ip/SYN)

my_ack = SYNACK.seq + 1
ACK=TCP(sport=1050, dport=80, flags="A", seq=101, ack=my+ack)
send(ip/ACK)

payload="stuff"
PUSH=TCP(sport=1050, dport=80, flags="PA", seq=11, ack=my_ack)
send(ip/PUSH/payload)

Note that this kind of script could be used simply to do the handshake, leaving the connection open for whatever traffic needs to be sent manually or by other means.

Many thanks to Judy Novak and SANS for introducing me to Scapy.