Tuesday, December 8, 2015

TCP 103: Port Scanning with Scapy

In TCP 101 and TCP 102, we learned how to manipulate TCP with a RAW_SOCKET. Welp, doing it that way all the time would suck. Thankfully, there's a module for that: scapy! We used pure socket before because I wanted you to understand what's under the hood when dealing with TCP/IP, or else the Scapy module would be kind of hard to wrap your head around.

Scapy is not a default module on Ubuntu or the like. You may need to install it on your distribution:
sudo apt-get install python-scapy

The Scapy Overview

Scapy is a great module for manipulating interface traffic. I recommend grabbing the PDF documentation for it here and here for reference. It can be used from everything to building sniffers to crafting your own custom packets. If you're getting into security, scapy is a must-know module. Take a look through the documentation at the link provided to get a glimpse at what's under Scapy's skirt. 

In this post we will be testing scapy on some port scanning concepts to familiarize you with the module.

The Half-Open Scan (Stealth Scan)

A half-open scan, as the name suggests is a type of SYN scan where we don't complete a full TCP handshake. It is also called a stealth scan (option -Ss in NMAP). You might be wondering why exactly it is called a stealth scan. Remember our little client and server application from TCP 101? You may have noticed our server did not actually record the IPv4 address until the TCP handshake was completed. That is exactly why a half-open scan is called a stealth scan. Let's see the code in action.

tcp_half.py


server.py (from TCP 101)



Fire up 2 VMs again like in TCP 101. Take our server.py code and launch it on our server and fire off a half-open TCP connection.
Figure 1: We fire off our half_open scan.
Figure 2: Not a peep from our server.py script.
Figure 3: We can clearly see a SYN, RST packet from our Ubuntu VM at 10.0.2.5 - indicators of a stealth scan.
In figure 1, we can see we receive back a SYN-ACK from our server (flags = SA). This indicates that port 12345 is open. If we had received back a RST-ACK from the server (flags = RA), it would indicate the port is closed.

So what is happening here?

When we fire off tcp_half.py it sends a single SYN packet. Our server replies with the SYN-ACK to try to finish it's TCP handshake in order to establish a complete connection. Instead we stop the connection and send a RST. The server will then drop the connection, hence why this is a half-open scan. We only connect half way and stop the connection. Our server never records our IP and just assumes we failed to connect because of network gremlins.  

Let's do a walkthrough of the tcp_half.py script, since it's new to you:

Line 1: We import scapy. You may have gotten an error if you tried to run this: "there ain't no damn scapy module bro." Or something like that. If that's the case:
sudo apt-get install python-scapy
Line 4: IP(args): We craft our IPv4 header. We set the source and destination address. In the link provided, it lists out the available arguments, data type, and default values for the IP() object. Much easier than dealing with struct and packing all the damn data up for the built in socket module. 

Line 5: TCP(args): Here is where we craft the TCP header. We set the source port to 1024, our destination port to 12345, and our flags value to "S" for "SYN". Lastly we set the sequence id to 12345, which is important to make sure our packets send and receive in order. A list of arguments for TCP: 
Figure 4: To find a list of arguments you can open a python console and import scapy. run ls(arg) to print out a list of commands.
Scapy documentation can be a bit daunting to trove through, but it's still pretty intuitive. How do you think you'd set "flags" for a FIN? flags="F" What about a FIN-ACK? flags="FA"

Line 7: Here we create our packet. We add in an IPv4 header then a TCP header. We do not need to craft an ethernet header since we are not manipulating any information at the data link layer. 

Line 9: sr1(args) [page35 & 36]: We set p to the returned object of sr1(). SR1 sends our packet "packet" and will only capture and return the first answer it receives. It is best used for single packet probes like we are doing for port scanning. We only care to know what the server's response is, which will come as a reply to our packet. The inter=1 command is the time in seconds to wait in between each packet.

Line 10: p.show(): Print out the parsed data from our packets so we can view it. In this case it will be the single packet captured from sr1 in line 9. 

Lines 12 thru 15: Same difference as crafting our SYN. The difference is we fire off our RST right away after the SYN waits 1 second. 

The FIN Scan:

Modern day security equipment like various firewalls and IDS/IPS systems can easily detect SYN scans. A FIN scan is a good way to do a port scan around most firewalls and IDS systems. We send the remote host a FIN packet. If there is no response from the remote host, there is a good probability the port is actually open. If we receive a RST-ACK, the port is closed.

Figure 5: We fire off our FIN and get no response. Yay! The port is open.

Figure 6: Our server is as oblivious as always.
Figure 7: We can see the FIN reached our server. Our server does nothing when it receives it.
To play around with it, firewall off your servers listening port with an IP tables rule. You'll receive a RST-ACK when you send the FIN to port 12345.

The ACK Flag Scan:

An ACK flag scan is best suited to probe if firewalls, IPS or other related network security controls are between you and your target host. With this method we send an ACK packet with a random sequence number. No response means the port is either filtered or open. On the contrary, if we receive a RST-ACK, the port is closed.  


For this example I will probe telnet, which is pretty much firewalled off most of the internet.
Figure 8: We send an ACK and get back a RST. The port is closed by a firewall.

Figure 9: We receive the RST-ACK by our application. 

XMAS Scan:

This involves sending basically a bunch of flags all at once (FPU) and seeing what bounces back. You could say the packet was 'lit up like a christmas tree.' Typically if the port is closed, you'll get an RST. If the port is filtered you will get nothing back.


Figure 10: We Send our FIN-PSH-URG and receive back a RST-ACK. Telnet is closed on the remote host.

Figure 11: Packet capture of a successful XMAS Scan.

Reinforcing the Concepts:

If you need something to do to reinforce these concepts, build yourself a little port scanner with scapy. You could pretty much wrap all the code here into some functions, that way you'll have them for later on. Play around with scapy, a VM, IPTables and Wireshark. It's a very powerful tool for packet manipulation! Merry XMAS scanning!


No comments:

Post a Comment