Post

Instant Walkthrough - HackTheBox

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:

web app running on port 80

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:

android app traffic on burpsuite http history

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:

jadx-gui decompiler

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:

API Swagger doc

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:

jadx-gui decompiler

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:

leaked JWT token test

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:

LFI exploit

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:

LFI exploit

SSH as shirohige

Now we can access the machine via SSH using the private key for the user shirohige:

SSH to the machine

Root Flag

Enumeration

After running linpeas i found an interesting backup file located at /opt/backups/Solar-PuTTY directory:

Linpeas output

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:

Solar PuTTY sessions backup file

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:

Password Recovery and Decryption of sessions file

Shell as root

Now let’s switch to the root user account and grab the flag:

Password Recovery and Decryption of sessions file

This post is licensed under CC BY 4.0 by the author.