How To: Build an FTP Password Sniffer with Scapy and Python

Build an FTP Password Sniffer with Scapy and Python

Welcome back everyone. I apologize for the lack of training articles, but I've been rater busy lately. I've recently picked up a second job and my college courses are now back in session, so I'm a bit strapped for time.

FTP is a very important protocol. It's not only important for large corporations, but for us hackers as well. If a hacker can crack an FTP server password, they can upload whatever files they want, including a payload to compromise the server. Today we'll be building a password sniffer that will sniff FTP login information as it's transmitted! First, let's cover how our sniffer will work.

Image via

When using regular FTP, both ends of the communication don't use any sort of encryption. This means that any data sent between them can be intercepted and read in transit. As hackers, we can watch this information go back and forth, and we can detect and capture a successful login. Now that we know how this sniffer will work, let's get to the code!

Note: We'll be defining multiple functions in order to use them later, the order in which we make the functions is not the order that they take place.

Step 1: Import Modules and Setting Interpreter Path

As always, the first things we'll need to do is import the needed modules and set the interpreter path. The interpreter path will mark this file as a python script, and the modules contain the code needed for our scanner. Let's take a look at this snippet:

We can see here that we've placed the importation of scapy within a try/except block. This is in case the user doesn't have scapy installed on their computer. We've also set a variable to the value of the first command line argument. This will be the interface the attacker would like to sniff packets off of. We've also made two lists, each with a single element. These will contain any usernames and passwords that we intercept. Now that we have these things set, let's move on!

Step 2: Checking for a 230 Response Packet

We're going to have multiple functions for checking our packets, and one of them will be to detect a successful log in. We'll be naming this function check_login(). Let's take a look at this function now:

We can see that this function takes three arguments, a packet to check, a username, and a password. First if checks the packet to see if it contains the string "230". 230 is the response code for a successful login. If we sniff a 230 response from the server, that means that the last username and password we caputured are valid credentials! If the packet is a 230, we tell the user that we found valid credentials, and we present them in a clean format.

Now that we have a function to check for a valid login, let's move on to the next step and make a function to check a packet for FTP.

Step 3: Checking for the File Transfer Protocol

When we sniff packets, we're going to see all the traffic going by us, not just FTP traffic. In order to weed out non-FTP traffic, we can build a simple function to check for common FTP traits and pass on packets that meet these traits. Let's take a look at the simple, yet crucial function:

This function is simple. It only takes one argument, a packet to check. First it will test for the TCP header and a raw data header. If these headers are present in our packet, it will probe the TCP header to see if either the source or destination port is port 21. This is the port accociated with server side FTP services. It will return True if all requirements are met, and False other wise.

Now that we have a function to weed out all non-FTP traffic, let's move on to our final function. This function will check for usernames and passwords, and call our two previous functions.

Step 4: Handle the Packets

This final function will actually be handling the packets and feeding them into our other functions. We will name it check_pkt(). Let's take a look at this final function and then we'll dissect it:

First, we call our check_for_ftp() function and pass the sniffed packet to it. If the result is true, the function continues, if it is false, the function stops investigating the packet.

Then, it will extract the data from inside the raw data header and assign it to the data variable. It then checks for the strings 'USER ' and 'PASS ' within this data. If either of these strings are present, it will split the data wherever the string is detected, and will append the second element of that resulting list to the end of the usernames or passwords lists. The reason we're using split() is because strip() has a bad habit of stripping characters out of the usernames and passwords.

If the packets doesn't contain either a username or a password, it will call the check_login() function and will pass it the packet, along with the last username and the last password in the lists.

Now that we have our final function, we can actually start sniffing!

Step 5: Start the Sniffing

Now we can actually start sniffing, let's quickly dissect this code snippet and start sniffing:

Before we start sniffing, we print that sniffing has started on the specified interface. We then call scapy's sniff() function. Inside the function we specify the interface to sniff on, and the function to pass all packets through (our check_pkt() function). We also expicilty tell it not to store any of the sniffed packets as we won't need them for anything else.

We place the sniff() function inside a try/except block just in case it fails to start sniffing on the given interface. This could be caused by the user giving a false interface. Once the user stops the sniffing, we print the sniffing has stopped.

There we go, our script (Pastebin or Null Byte Suite) is complete, now let's test it out!

Step 6: Test It Out

I've named the script, and I've used the chmod command to make it executable. My college classmate Skrub has volunteered to be our victim today. We'll be sniffing for a login to an FTP repository mentioned in this post. We'll be routing the traffic through the attacking system using a man in the middle attack.

First, let's initialize the sniffing by executing our script, I'll be sniffing off my wireless interface, wlan0:

Now let's move to our victim machine running windows 7 and log into the FTP server:

Now let's go back to our attacking machine and see if we've captured these credentials:

There we have it! We were able to successfully capture the login credentials of the vicitm! Let's wrap this up shall we?

Wrapping It Up

There we go. By passing sniffed packet through various header checks, and extracting key data when present, we were able to intercept the FTP login credentials!

Thank you for reading!


Just updated your iPhone? You'll find new features for Podcasts, News, Books, and TV, as well as important security improvements and fresh wallpapers. Find out what's new and changed on your iPhone with the iOS 17.5 update.


check_for_ftp(...) has some redundant code, in that two else ... return False are not needed. Or, could you tell me why this wouldn't work?

Since there are layered if statements that ultimately return true or false, if the first statement is not met then the second is never evaluated, and the return would be nothing. It garuntees that no matter the contents of the packet everything either returns true Or false.


Nice post as usual.

May I ask you don't use a filter with sniff?. Your code will be shorter and more efficient.... Didactic reasons?

Using filter with sniff has some very awkward keywords that I'm not very familiar with, so I opted for my own function instead


Thanks Defalt.

It looks like sniff uses standard BFP filters... in case you are interested.

I am new to scapy, i would like to know , how can i create a ftp packet using scapy.
Scapy is not receiving the SYN/ACK , but i see Linux box has got the SYN/ACK response.

Share Your Thoughts

  • Hot
  • Latest