Scapy - Part 1

Creating a custom local ICMP rule is one of the easiest ways to make sure Snort is capturing traffic in the way you assume it should be. The issue though is sometimes we want to create our own custom rules, and in my opinion the only way to test those rules is to create a custom packet. One of my favorite applications for this is Scapy. Scapy is a python application that allows you to create any type of packet you want right from your terminal allowing you to add specific strings or hex characters in your payloads to assist in triggering your snort rules. This may sound a bit more difficult than it is, but hopefully these series of posts will make this a bit easier.

Trying to remember everything about flag and field of a packet can sometimes be overwhelming so for right now since we are testing just basic Snort rules, the only thing you really need to worry about is a source and destination IP, source and destination port, and the payload itself. Because Scapy is developed with Python, Python will be required to have been installed, as well as Scapy having been downloaded. I'm going to assume you have already gotten this far. To begin with an example, we are going to look at this simple rule that triggers when a DNS query is made containing the string "teamviewer". Here is the snort rule below:

alert udp $HOME_NET any -> $EXTERNAL_NET 53 (msg:"APP-DETECT Teamviewer control server ping"; flow:to_server; content:"teamviewer"; fast_pattern:only; metadata:service dns; reference:url,en.wikipedia.org/wiki/TeamViewer; classtype:policy-violation; sid:24094; rev:1;)

As you can see, the above snort rule simply alerts when a UDP packet is sent to destination port 53, and contains the string "teamviewer". In this instance it may be easier to just open a browser and go to teamviewer.com but this will not always be the case. To test this rule with Scapy the following will need to be executed to create our custom packet:

  1. ip=IP(src="192.168.2.3")
  2. ip.dst="8.8.8.8"
  3. UDP=UDP(sport=23432,dport=53)
  4. data="teamviewer"
  5. packet=ip/UDP/data
  6. send(packet)

Below is an explanation of each command above (All the variable names below can be changed, I just like to keep it uniform like this):

  1. A variable called IP was created and the IP function was called and set the source IP of our created packet to 192.168.2.3.
  2. The IP object is called and is passed the destination function which sets the destination IP address to 8.8.8.8.
  3. A variable called UDP is created and the UDP function is called and the source port is set to 23432 and the destination port is set to 53.
  4. A variable called data is created and the string "teamviewer" is assigned to the variable.
  5. The final variable packet is created and the ip packet, UDP datagram, and essentially the payload are all assigned the packet variable to create your packet.
  6. Once the packet is created, the send function is called and the packet is passed as a parameter to the function and the packet is sent on it's way.

If the packet happens to pass your Snort sensor that has the above teamviewer rule enabled, an alert will be generated, letting you know that this rule was triggered.

Depending on how your rules are created, it is not always this easy, but as long as the Snort Rule does not have flow:established, this approach will trigger any type of rule similar to the above. If you have any questions let me know, The next post I plan on going a bit more in-depth to explain how to handle situations where a TCP connection is required as well as how to trigger rules with multiple recursive content triggers.