Summary

As the name suggests this box had a instance of gitlab where the initial foothold involves getting credentials from obfuscated javascript and once logged into the gitlab instance we abuse webhooks to add our own code and execute it to get a reverse shell. Read on to see how I able to root the box.

Enumeration

As usual I start with a quick nmap scan to find open ports and then run a second scan for service and version detection.

nmap -sS -p- --min-rate=1000 10.10.10.114

Nmap scan report for 10.10.10.114
Host is up (0.21s latency).
Not shown: 65533 filtered ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Thorough scan:

nmap -Pn -sC -sV -p 22,80 -oA nmap/bitlab 10.10.10.114

Nmap scan report for 10.10.10.114
Host is up (0.47s latency).

PORT   STATE SERVICE VERSION
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🆎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-server-header: nginx
| http-title: Sign in \xC2\xB7 GitLab
|_Requested resource was http://10.10.10.114/users/sign_in
|_http-trane-info: Problem with XML parsing of /evox/about
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Gitlab

gitlab instance

After trying out some simple combinations of username and password that didn’t work I decided to click on links on the home page and see if I’ll get a version number. When I opened the help link I got this.

gitlab instance

In bookmarks.html the links were normal apart from the last one which was javascript.

gitlab instance

The deobfuscated javascript shows a username and password.

javascript: (function ()
{
	var _0x4b18 = ["value", "user_login", "getElementById", "clave", "user_password", "11des0081x"];
	document[_0x4b18[2]](_0x4b18[1])[_0x4b18[0]] = _0x4b18[3];
	document[_0x4b18[2]](_0x4b18[4])[_0x4b18[0]] = _0x4b18[5];
})()

After logging in there are two projects.

gitlab instance

My user has Developer role on the Profile repo and Reporter role on the Deployer repo. I can’t edit the deployer but looking at the files the readme has a link about gitlab webhooks.

gitlab instance

In the index.php the part that caught my eye is the if statement. If that condition is true, a command to change directory is run and then a git pull.

gitlab instance

Based on the name of the script we can guess it deploys changes made to the profile repo after a merge request is made. Since it pulls to /profile this is probably the directory where it’s hosted. We can verify this by going to /profile.

gitlab instance

Getting Shell

I’ll add a php file to the profile repo that will give me a reverse shell on the server. I will need to also create a pull request with my new file and merge it, this will create a webhook to the deployer script and it will download it on the web server, allowing me to execute it.

Adding reverse shell php file:

gitlab instance

Merging the request:

gitlab instance

The file is successfully added to master branch:

gitlab instance

Finally got my shell by visiting 10.10.10.114/profile/shell.php on my browser:

gitlab instance

Getting User

The user.txt is only readable the clave user. After a bit of local enumeration and getting stuck I decided to go back to the gitlab instance to see if I may have missed anything. I found a snippet mentioning postgresql and a db connection script.

gitlab instance

Checking on the server we indeed have a postgresql instance running locally on port 5432.

gitlab instance

After some tweaking I was able to get a script working that would read the db.

<?php

   $host        = "host = 127.0.0.1";
   $port        = "port = 5432";
   $dbname      = "dbname = profiles";
   $credentials = "user = profiles password=profiles";

   $db = pg_connect( "$host $port $dbname $credentials"  );

   if(!$db) {
      echo "Error : Unable to open database\n";
   } else {
      echo "Opened database successfully\n";
   }

   $sql =<<<EOF
      SELECT * from profiles;
EOF;

   $ret = pg_query($db, $sql);
   if(!$ret) {
      echo pg_last_error($db);
      exit;
   }

   while($row = pg_fetch_row($ret)) {
      echo "". $row[0] . "\n";
      echo "". $row[1] ."\n";
      echo "". $row[2] ."\n";
   }

   echo "Operation done successfully\n";
   pg_close($db);
?>

In the db I found a password which appears to be base64 encoded but the trick was to use it as it is and not decode it for it to work.

gitlab instance

Getting Root

This part was interesting as I had never heard of this technique before. The user www-data had sudo rights to run git pull.

gitlab instance

In git there are scripts called git hooks which can run automatically before or after git actions like commits. You can configure them in .git/hooks. For this case I’ll use post-merge since when git pull is run a merge action is done.

Since we can’t edit the current folder copy the profile repo to a world writeable directory like /tmp and create the post-merge script in hooks, the script will give a reverse shell.

cp -r /var/www/html/profile /tmp
gitlab instance

After making this file executable, we just have to make any change to the repo on the gitlab instance like creating a txt file and make sure we merge it to master. Once that is done, we can run sudo git pull and get a root reverse shell.

gitlab instance

As you may have noticed since we used the www-data user to get root, we would have skipped the getting user path using the postgresql db and read user.txt from the root user.