Bitlab - Hack The Box

I solved this gitlab box the unintended way by exploiting the git pull command running as root and using git post-merge hooks to execute code as root. I was able to get a root shell using this method but I still had to get an initial shell by finding the gitlab credentials in some obfuscated javascript and modifying PHP code in the repo to get RCE.


  • Find javascript obfuscated credentials in bookmarks.html
  • Use creds to gain access to the profile repo and modify it to get PHP RCE
  • Get root access using the unintended method of git post-merge hooks


The portscan shows SSH and HTTP ports open along with entries from robots.txt indicating this is a Gitlab service. I’ll check out a couple of the URIs mentioned below in the next section.

root@kali:~/htb/bitlab# nmap -sC -sV -T4
Starting Nmap 7.80 ( ) at 2019-09-08 09:49 EDT
Nmap scan report for bitlab.htb (
Host is up (0.022s latency).
Not shown: 998 filtered ports
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 a2:3b:b0:dd:28:91:bf:e8:f9:30:82:31:23:2f:92:18 (RSA)
|   256 e6:3b:fb:b3:7f:9a:35:a8:bd:d0:27:7b:25:d4:ed:dc (ECDSA)
|_  256 c9:54:3d:91:01:78:03:ab:16:14:6b:cc:f0:b7:3a:55 (ED25519)
80/tcp open  http    nginx
| http-robots.txt: 55 disallowed entries (15 shown)
| / /autocomplete/users /search /api /admin /profile
| /dashboard /projects/new /groups/new /groups/*/edit /users /help
|_/s/ /snippets/new /snippets/*/edit
| http-title: Sign in \xC2\xB7 GitLab
|_Requested resource was http://bitlab.htb/users/sign_in
|_http-trane-info: Problem with XML parsing of /evox/about
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 13.33 seconds

Gitlab enumeration

I already knew that the box was going to contain a Gitlab service based on the box name and the logo. The box was originally submitted as Gitlab but was renamed to Bitlab before launch.

I clicked the Explore link at the bottom of the page to look for repos but I didn’t see any repositories that are publicly accessible.

I checked a few links from the robots.txt file and found a profile page for Clave:

That’s not a default page in Gitlab so I’ll keep that in mind for later.

Hardcoded Gitlab credentials

I initially skipped the Help section but when I went back and clicked on the link, I got the following page:

The Gitlab Login link doesn’t link to an HTTP URL but contains obfuscated javascript:

javascript:(function(){ var _0x4b18=["\x76\x61\x6C\x75\x65","\x75\x73\x65\x72\x5F\x6C\x6F\x67\x69\x6E","\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64","\x63\x6C\x61\x76\x65","\x75\x73\x65\x72\x5F\x70\x61\x73\x73\x77\x6F\x72\x64","\x31\x31\x64\x65\x73\x30\x30\x38\x31\x78"];document[_0x4b18[2]](_0x4b18[1])[_0x4b18[0]]= _0x4b18[3];document[_0x4b18[2]](_0x4b18[4])[_0x4b18[0]]= _0x4b18[5]; })()

I executed the Javascript in NodeJS and found credentials for Clave:

Credentials: clave / 11des0081x

Also, if you copy/paste the entire Javascript code snippet in the Firefox dev console when you’re on the Gitlab login page it’ll auto populate both username and password field.

I can now log in to the Gitlab portal and I see two repositories that I have access to:

I have read/write access to the Profile repo but only read access to Deployer.

As per Gitlab’s documentation, these are the permissions available:

Guest - No access to code Reporter - Read the repository Developer - Read/Write to the repository Maintainer - Read/Write to the repository + partial administrative capabilities Owner - Read/Write to the repository + full administrative capabilities

Getting RCE through the Profile page

The Profile repository contains the webpage for the Profile page I found earlier. I see that it’s running PHP so if I can modify this page I should be able to gain remote code execution by adding a reverse shell on the page.

The Deployer repo code is a simple PHP script that expects a specific JSON message then does a git pull. I assume this’ll be used to deploy the Profile page when I commit changes to the repo.

The repo is deployed in the root of the directory and I can access it with /deployer:

I’ll add a PHP reverse shell in the Profile index.php page that triggers when I have a shell parameter present. Then I submit the merge request and merge it after.

I’ll craft the proper POST request with the Repeater function in Burp Suite. The JSON message has to match the exact format from the code I found in the repo.

Now that the profile page has been updated, I can trigger the reverse shell by sending a request with the shell parameter:

Unintended privilege escalation to root

The www-data user can execute git pull as root:

$ sudo -l
Matching Defaults entries for www-data on bitlab:
    env_reset, exempt_group=sudo, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on bitlab:
    (root) NOPASSWD: /usr/bin/git pull

Git has hooks that can be used to execute code after commit, push, merge, etc. I’ll use that to get remote execution as root through the git pull command. The documentation says:

This hook is invoked by git-merge[1], which happens when a git pull is done on a local repository.

First, I’ll create two local repos: foo will be merged into the bar repo. I’ll add a reverse shell in the post-merge hook of the bar repo where bar will be merged into.

Then I’ll do an initial commit in the foo repo and set up bar to pull from the foo repo:

And finally I’ll do a new commit in foo so I can initiate a merge from foo and trigger the reverse shell: