webfreemedium

TornadoService

HackTheBox

Target: http://154.57.164.69:31599

$ ls tags/ techniques/
python_class_pollutionpostmessage_xssiframe_origin_bypasstornado_cookie_forgeryprototype_pollution_python

$ cat /etc/rate-limit

Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.

TornadoService — HackTheBox

Description

You have found a portal of the recently arising tornado malware, it appears to have some protections implemented but a bet was made between your peers that they are not enough. Will you win this bet?

Target: http://154.57.164.69:31599

Python Tornado web application with multiple vulnerabilities: Python class pollution in recursive dict merge function, DOM XSS via innerHTML + postMessage, and a Selenium bot that visits user-controlled URLs. The goal is to chain these to overwrite the cookie secret and forge a valid session cookie to access the flag.

Analysis

Application Structure

  • / — Index page (dashboard)
  • /get_tornados — Returns list of tornado objects as JSON
  • /update_tornado — Updates a tornado object. RESTRICTED TO LOCALHOST ONLY
  • /report_tornado?ip=X — Makes a Selenium bot visit http://{ip}/agent_details
  • /login — Login with username/password (passwords are random 32-byte hex)
  • /stats — Returns flag if user has valid Tornado secure cookie

Vulnerability 1: Python Class Pollution

The update_tornados() function recursively merges a dict into an object using setattr:

def update_tornados(tornado, updated): for index, value in tornado.items(): if hasattr(updated, "__getitem__"): if updated.get(index) and type(value) == dict: update_tornados(value, updated.get(index)) else: updated[index] = value elif hasattr(updated, index) and type(value) == dict: update_tornados(value, getattr(updated, index)) else: setattr(updated, index, value)

By traversing __class____init____globals__, we can reach module-level globals and overwrite APP.settings.cookie_secret.

Vulnerability 2: DOM XSS via innerHTML + postMessage

Frontend JS renders tornado data with innerHTML (XSS sink):

machineIdValue.innerHTML = tornado.machine_id; ipAddressValue.innerHTML = tornado.ip_address; status.innerHTML = tornado.status;

And listens for postMessage events without origin validation (XSS source):

window.addEventListener("message", (event) => { const tornado = event.data; if (!tornado.machine_id && !tornado.ip_address && !tornado.status) return; const listItem = createListItem(tornado); // renders with innerHTML tornadoList.appendChild(listItem); });

...

$ grep --similar

Similar writeups