The June Bonus challenge, called Leaky Jar, was prepared by ProxyNomadd and hosted at https://leakyjar.intigriti.io.
The goal of the challenge was to find the flag in the INTIGRITI{.*} format. The solution could not require user interaction, and it could not involve a self-XSS or MiTM attack. Source code of the web application was not provided, resembling a black-box engagement.
My solution exploited a CSRF vulnerability on the /share endpoint, used for sharing recipes with other users.
Let's visit the challenge's URL, and click around to get an idea of the web app's functionality, and how it works.
The Leaky Jar is an app for bakers to share and review cookie recipes. You can browse and search public recipes, read and leave reviews on any recipe's page, and view a listing of all bakers. After signing in, you can manage your recipe box - you can create new recipes, share them with other users, and view the recipes that others have shared with you. There is also a page where you can send a URL for the admin to review.
The web app's text ("cookies", "leaky jar") hints that this challenge may involve cookies, perhaps leaking them somehow. Another strong clue is the "Report a recipe" feature - it suggests that the challenge is about tricking a privileged bot into visiting an attacker-controlled page, potentially leaking its session cookie or triggering some privileged action.
HTTP responses show that this web app runs without any client-side JavaScript code. Instead of fetching data via API calls, it relies entirely on Server-Side Rendering (SSR): the server returns fully-rendered HTML with all data already embedded.
An important observation is that the server uses HTML entity encoding for some special characters, such as <, >, " and '. This limits the possibility of XSS. It's also worth noting that the server's responses don't have a Content-Security-Policy header, nor any CORS-related headers.
The web app allows signing up. When signing in, the server issues a session cookie, with the following attributes: Secure; HttpOnly; Path=/; SameSite=None.
The HttpOnly attribute prevents JavaScript code from accessing the cookie, so even if we found an XSS, we wouldn't be able to read this cookie's value (the XSS could still be impactful though!)
The SameSite attribute controls whether the cookie will be sent with cross-site requests. The None value is the most permissive - it tells the browser to send the cookie on all requests, including cross-site ones initiated from a different domain. While SameSite=None certainly has its use cases, it may enable CSRF attacks under some circumstances.
As for the cookie's value, it appears to be made up of 3 parts, separated by the . character, similar to a JWT. The first part is a JSON like {"user":"username"}, encoded as base64. The second and third parts appear random, or at least not easily forgeable.
Even though XSS felt unlikely because of the HTML entity encoding of special characters like < and >, I still tested every endpoint - encoding is often applied inconsistently, and a single page missing it would be enough to attack the admin bot. I checked each sink where untrusted input is reflected (such as the review's name and text fields, my recipes' name and content fields, etc...), but got to the conclusion that encoding is applied consistently on every page.
Since the first part of the session cookie is just a base64-encoded JSON, I have tried modifying the JSON's "user" field to another user's username, hoping to bypass authentication. I have also attempted changing and removing the second and third parts of the session cookie, but all of these attempts failed.
As the previous tests were not successful, I went back to the observation about SameSite=None attribute on session cookie. Because of SameSite=None, the browser will send the session cookie with all requests to https://leakyjar.intigriti.io, even if the request is initiated from a third-party origin, such as an attacker's website. Sounds promising, but can we actually cause the admin bot to visit our own website?
Let's test the validations on the "Report a recipe" page. After sending a few URLs, I concluded that it requires either http:// or https:// scheme, but the domain can be arbitrary, which confirms that we can indeed cause the admin bot to visit our website. ✅
Some interesting tests that I have tried here were URLs with javascript: scheme. If such a URL was allowed, it could potentially lead to XSS when bot's browser navigated to it.
On its own, SameSite=None attribute on a session cookie does not mean that the web app is vulnerable to CSRF. Other conditions must be met too. Let's analyze if CSRF attack is viable on the Leaky Jar web app.
First of all, let's check if there is a state-changing action within the application, that an attacker would have a reason to induce. When testing the app, we could see that users can review recipes, create new recipes, as well as share their recipes with other users. The sharing functionality looks promising - with CSRF attack, the attacker could share victim's recipes with an arbitrary user. ✅
Now let's see if the endpoint for sharing recipes has additional protections against CSRF. A very common defense mechanism relies on so-called CSRF tokens - unpredictable, server-generated values that are embedded in each form or request, that must be sent back and validated before the server accepts a state-changing action. When looking at the POST /share requests, we can see that the only parameter is "username". No sign of CSRF tokens. ✅
CSRF can also be restricted by the CORS preflight mechanism. CORS distinguishes between "simple" and "preflight" requests. Simple requests must meet specific criteria, that you can find here. For "non-simple" requests - such as those using Content-Type: application/json, custom headers, or methods like PUT/DELETE/PATCH - the browser first sends an OPTIONS preflight request, and won't send the real request unless the server returns the right Access-Control-Allow-* headers.
The /share endpoint accepts POST requests with Content-Type set to application/x-www-form-urlencoded. It doesn't require any custom headers. As such, it qualifies as a simple request - the browser sends it right away, without requiring any CORS headers in server's response. ✅
To be thorough, we can also test if the server validates the value of Origin and Referer headers - this is another possible defense against CSRF. When resending the /share POST requests after modifying the Origin and Referer headers, we can notice that it works just fine. ✅
So, we appear to meet all the conditions required for CSRF. Let's verify if we can indeed exploit it.
To sum it up, the idea of the attack is to cause the admin bot to visit our malicious website, using the "Report a recipe" feature. JavaScript code running on our website will exploit the CSRF vulnerability on the Leaky Jar web app by sending a POST request to https://leakyjar.intigriti.io/share, with the "username" parameter set to our own username. This way, we trick the admin bot into sharing his recipes with us. Afterwards, we can view the admin's recipes by navigating to https://leakyjar.intigriti.io/vault, and clicking "View" next to the admin's recipe box.
In a similar fashion, this could be used to attack any other user - the only user interaction required is visiting the attacker's website.
- Sign up on https://leakyjar.intigriti.io/register
- On a server that you control (let's call it https://attacker.com), host the following
index.htmlfile (change the "value" of "username" input field to your own username):
<!DOCTYPE html>
<html>
<body>
<form id="poc" action="https://leakyjar.intigriti.io/share" method="POST">
<input type="hidden" name="username" value="CHANGE_ME">
</form>
<script>
document.getElementById('poc').submit();
</script>
</body>
</html>- Navigate to https://leakyjar.intigriti.io/submit
- In the "URL" field, paste the URL to the
index.htmlfile that you hosted on your server, i.e. https://attacker.com/index.html - Click "Send to the Master Baker" button - after a few seconds, the admin bot visits your website, triggering the exploit
- Visit https://leakyjar.intigriti.io/vault
- Scroll down to the "Shared with you" section - you can see "admin's recipe box" on the list
- Click "View" to view admin's recipes - the flag,
INTIGRITI{019ef404-1e44-7748-bdcf-ca7b12dbfee0}, is among the recipes 🥳



