$ cat writeup.md…
$ cat writeup.md…
HackTheBox
Target: http://154.57.164.69:31599
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
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.
/ — 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 cookieThe 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.
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