Scapy, TCP Handshakes, and Snort

My previous post briefly went over how you might test snort rules with Scapy. This may be useful if there is an already existing rule, and you don't have any captured traffic to test it against. It may also be useful if you want to learn how snort is looking at each packet so that you might write better snort signatures.

Some of the Snort keywords that can be used for pattern matching, require a full TCP handshake to take place before it can match the pattern. This is a perfect scenario for Scapy. Take the following python script for example:

--------

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

#Set the client and server IPs
client = "192.168.1.1"
server = "192.168.1.75"

#Set the client source port and the server destination port
client_port = 12346
server_port = 80

#Set random initial sequence numbers for the handshake
client_isn = 1954
server_isn = 5018

#Create the SYN Packet
syn_p = Ether()/IP(src=client, dst=server)/TCP(flags="S", sport=client_port, \
dport=server_port, seq=client_isn)

#Create the servers syn-ack response packet
synack_p = Ether()/IP(src=server, dst=client)/TCP(flags="SA", \
sport=server_port, dport=client_port, seq=server_isn, ack=syn_p.ack+1)

#Create our ack for the servers syn-ack
ack_p = Ether()/IP(src=client, dst=server)/TCP(flags="A", sport=client_port, \
dport=server_port, seq=syn_p.seq+1, ack=synack_p.seq+1)

#Now we should have completed the tcp handshake
#We can now craft a packet that would be sent
#From the client to the server as part of the stream

#Lets build our HTTP payload
data = "GET / HTTP/1.1\r\nHost: www.malforge.com\r\n\r\n"

#Build the packet, and throw the http payload right on the end
get_p = Ether()/IP(src=client, dst=server)/TCP(flags="PA", \
sport=client_port, dport=server_port, seq=ack_p.seq, ack=ack_p.ack)/data

p_list = [syn_p, synack_p, ack_p, get_p]

wrpcap("handshake.pcap", p_list)

-------

The above packet capture can now be tested by many more snort rules. One example would be the http_method identifier.

alert tcp $HOME_NET any -> $EXTERNAL_NET 80 (msg:"HTTP GET Alert!"; content:"GET"; http_method; sid:10000000; rev:1;)

You could of course get much more complicated than the rule above, and would not want to alert on GET requests alone.