Once again Bitbucket’s CVE . Don’t ask me why I do this guy. It’s simply easy to set up.
1. Read description
Here is Atlasssian’s description of this CVE
The vulnerability can be exploited via HTTP Request with read 1 repo permission. In addition, the bug fix version is 7.6.17, so I installed on local version 7.6.16 for easy diff.
2. Error location
Bitbucket uses the built-in command git on the server to run API-related APIs, including the archive API that calls the git archive command on the system.
The archive API is an API to download the zipped code file of the repo.
API: rest/api/latest/projects/<prj key>/repos/<repo name>/archive
3. Analysis
All API calls to the built-in command git are handled by the DefaultGitScmCommandBuilder in lib/bitbucke-git.jar . In which, the code trigger archive is
The DefaultGitArchiveBuilder class can take format and prefix parameters in the API:
Note to perform inserting 2 parameters, format and prefix, requires the code to have one, ie the repo must not be empty, otherwise it will look like this:
The other program does not check the format and prefix values before it is inserted into the command, so we can insert the following command: git archive –format=< format> –prefix=< prefix> … something to make it run.
However, I cannot exploit the format value, because it already has an enum that only allows one of the following formats:
At first I thought just insert prefix=abcd || whoami || or something like that, it will jump into the git command and execute the whoami command at the same time, but I try and it doesn’t work.
When I searched, I discovered 1 thing, this prefix guy still takes all strings (including ||whoami|| always) to put in the prefix, not insert it into the command directly. However, the system string can be escaped by string Null. Like string = “haha u0000 hehe” then if put in terminal it will be split into “haha” and “hehe”.
String null in terminal is u0000 and on URL is * %00*.
In short, I will have to insert prefix =%00 ;whoami; %00. I will try again like this
The command is still not executed. It has a “/” at the end, probably because the bitbucket set for prefix always has a / to separate the prefix and the file content. So I have to think about using another parameter that can use this / as part of it.
The git archive command has a parameter of –remote only the repo needs to be accessed, this –remote value can be combined with the / character to get the repo, because the repo here is the path to the folder:
Now let’s put prefix=a ;whoami; –remote=hehe, the result is:
At this point, I think, 1 must be a valid remote, 2 is the fact that the command has already been executed, but I don’t know, so I tried curling to my server port 8000, but trying curl failed. I tried a valid remote, the result is:
*** Means if a string has no argument in front, the command will accept that string as ref (default ref is HEAD), without ref ;whoami; this, so I had to add another argument before the ;whoami;***, and I tried –output , the result was
Still not working, while I try on terminal, the result is:
So the sign ** ;** may not be reasonable in this case (because I’m a chicken, I can’t distinguish when to use ; | && || ). I tried all the cases on the terminal first and found that only using , the output will create a file with the name = whoami command:
At this point, I think: the sign will take the value of the result of the statement running inside it as a string, so it is completely possible to insert the statement here and execute it. I tried on bitbucket:
Honestly, until this point, I still don’t understand why it doesn’t run :v I think it still receives output= whoami
.
But when I change from –output to –exec , it runs fine, and the result prints an error:
With bkcs as the result of his whoami command.
I figured out that the git command actually built by bitbucket had no errors, the fault was in the terminal’s mechanism. If the terminal where it printed the output contains <cmd>
, it will also execute this cmd.
In short, the key to exploiting this error is to make the terminal print the line ** <cmd>
** and it will execute the command inside.
And only * –exec* can let it show that error (although I honestly don’t know the effect of this argument, I’ll probably find out later). To –exec to display this error, I must first make sure the API doesn’t jump into another exception first, so I need to bypass the prefix and –remote as above.
What I learned through this hole:
Terminal will parse the string via string null u0000
Terminal will execute the command if the command is enclosed in when printing to the console