SCAPY packet-crafting reference

Primitives

List possible parameters for a protocol ls(IP)

ls(TCP)

ls(UDP)

ls(ICMP)

ls(Dot1Q) 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 pkt (Displays what nondefault values pkt contains)

ls(pkt) (Displays all of the parameters in use as well as their defaults)

Ways to set parameters

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

tt=TCP(dport=25)

send(i/tt)

pack2=i/tt

send(pack2)

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 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 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 e=Ether()

i=IP()

t=TCP()

pay="payload"

i.dst="2.3.4.5"

t.dport=445

t.flags="S"

myframe=e/i/t/pay

myframe

srp1(myframe) Build a L3 frame containing a SYN e=Ether() (creates an Ethernet header named 'e')

t=TCP() (creates a TCP header named 't')

ii=IP() (creates an IP header named 'ii')

ii.dst="10.10.1.1"

ii.ttl=3

t.sport=1035

t.dport=445

t.flags="S" 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 = _ Inspect traffic in a pcap file recs=rdpcap("/tmp/file/pcap")

for r in recs Replay a pcap file

Reasons to build a Layer 2 frame

need to set a layer 2 parameter, such as a MAC address

choose which interface the packet comes out on

send a frame multiple times





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:

A SYN going out

A SYN/ACK coming back (if the dest is responding)

A RST/ACK going out

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.