What was the challenge?
LeakyJar stored private recipes in a user vault. Its sharing feature accepted a username
at /share and granted that account access to the current user's vault. The
goal was to make an administrator share the protected vault with the attacker's account.
The two conditions
The attack depended on two behaviors being present at the same time:
- The session cookie used
SameSite=None, allowing it to accompany a cross-site request. - The
/shareendpoint accepted a state-changing POST without requiring a CSRF token.
Neither condition required reading the administrator's response. The browser only needed to send a valid authenticated request containing the attacker's username.
Proof of concept
An attacker-controlled page could submit a form to the challenge endpoint as soon as the administrator visited it:
<form id="share-form"
action="https://leakyjar.intigriti.io/share"
method="POST">
<input name="username" value="guestx">
</form>
<script>
document.getElementById("share-form").submit();
</script>
The value guestx represented the attacker's challenge account. When the
administrator opened the hosted page, the browser sent the POST request with the
administrator's session cookie. The application then shared the administrator's vault
with the attacker-controlled account.
Impact
The forged request changed access to private data. After the vault was shared, the attacker could open the administrator's recipes and retrieve the challenge flag. The weakness was submitted as cross-site request forgery at medium severity and was later archived as resolved.
Remediation
- Require a server-generated CSRF token for the sharing action.
- Use
SameSite=LaxorSameSite=Strictwhen cross-site session use is unnecessary. - Validate the request's
OriginorRefererheader as an additional defense. - Consider confirmation or reauthentication before granting another account access to private data.
Key lessons
- Cookie policy and endpoint protection must be evaluated together.
- A POST request is not protected from CSRF simply because JavaScript cannot read its response.
- Sharing and permission changes deserve stronger safeguards than ordinary navigation.
- A short proof of concept is strongest when it clearly connects browser behavior to the resulting state change.