root@thehacksparrow:~/writeups$ SYSTEM ONLINE
root@sparrow:~/writeups$ cat dog.md
// writeups

Dog

8 Mar 2025 · 11 min read
Dog - maquina de Hack The Box

Executive summary — Dog is an Easy Linux box built on a chain of failures that's extremely realistic and depressingly common in sloppy CMS deployments: an exposed .git repository on the web server lets you reconstruct the full source of a Backdrop CMS install (a Drupal 7 fork). Inside that source sit the CMS's database credentials, which turn out to be reused as the system user's SSH password. Once in, sudo -l reveals full permission on bee, Backdrop's official admin CLI, whose eval subcommand runs arbitrary PHP — and since it runs via sudo, that's instant root. Path: Web → exposed .git → credentials → SSH → bee eval → Root.

PlatformHack The Box
Operating systemLinux
DifficultyEasy
StatusRetired
Target IP10.129.231.223

Attack map

[80] Backdrop CMS  (fuzzing → /core /modules /themes /sites /files)
   │  exposed .git/ → git-dumper → repository dump
   ▼
[CREDS]  password in the CMS config  → reused
   ▼
[SSH]  johncusack   (user.txt)
   │  sudo -l → NOPASSWD /usr/local/bin/bee
   ▼
[BEE]  bee eval "system('/bin/bash')"  (as root)
   ▼
[ROOT]

1. Reconnaissance

We always start with a full port sweep, not just the top-1000: on web-focused "Easy" boxes the attack surface is often deliberately small, so confirming there are only two services already points the rest of the assessment toward HTTP.

nmap -sC -sV -p- 10.129.231.223
22/tcp  open  ssh     OpenSSH
80/tcp  open  http    Apache

Two open ports: SSH (likely the final destination of a foothold, not the entry point) and HTTP, which is where we focus.

2. Web enumeration

We browse the site and run directory fuzzing to map it out:

http://10.129.231.223/
wfuzz -c -z file,/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt \
  --hc 404 http://10.129.231.223/FUZZ
/core
/modules
/themes
/sites
/files

Why this layout matters: /core, /modules, /themes and /sites are the classic directory layout of the Drupal family (and its forks). We don't yet know the exact CMS, but we already know which family it belongs to — which tells us where to look next (e.g. sites/default/settings.php is where Drupal/Backdrop keep the database config).

3. Exposed .git repository

We check whether the version-control directory was left reachable over HTTP — a classic mistake when a deploy is done by git clone-ing straight into the server's DocumentRoot:

http://10.129.231.223/.git/
HTTP/1.1 200 OK
Content-Type: text/plain

Why it's serious: the .git directory doesn't just hold the current state of the code, it holds its entire history: objects, blobs and past commits. If a secret was ever committed and then "removed" in a later commit, that secret is still in the history. With read-only HTTP access to the directory (no directory listing, but the internal files themselves are served) it's possible to reconstruct the whole repository from the outside.

4. Dumping the repository

There's no directory listing at /.git/, but Git's internal object files (HEAD, index, objects/, refs/...) live at predictable paths. git-dumper exploits exactly that: it requests the known internal Git files one by one, discovers the object hashes they reference, and recursively downloads until it can reconstruct a complete, working .git.

git clone https://github.com/internetwache/GitTools.git
cd GitTools/Dumper
python3 git_dumper.py http://10.129.231.223/.git/ ~/dog_repo

With the repository reconstructed locally, a simple git log also lets us see the commit history and its authors — useful for spotting candidate system usernames.

5. Code analysis

With the full source now local, we grep it recursively for secrets:

grep -R -i "password" .
grep -R -i "database" .

Database credentials show up in the CMS configuration (sites/default/settings.php, the standard config file across the Drupal/Backdrop family). That database password later turns out to be the key to system access.

Why a CMS stores the DB password in plaintext inside a code file: that's the normal way Drupal/Backdrop resolve their MySQL connection — the problem isn't that the file exists, it's that it ended up tracked in Git, and that Git repo was exposed to the public.

6. CMS fingerprinting

The directory layout and the dumped code confirm Backdrop CMS, a Drupal 7 fork aimed at simplifying administration for small teams. The installed version is 1.27.1, already outdated at the time of this box.

Backdrop, like the rest of the Drupal family, lets an authenticated administrator install/enable modules — and a module is nothing more than PHP code the CMS executes. That means the admin panel of this CMS family is, in general, a code-execution primitive in itself once you have an admin session. This box doesn't require going that route: the leaked credential takes us straight in over SSH, so that alternate path is only mentioned as context.

7. SSH access

With candidate usernames (pulled from the repository's commit authors, e.g. johncusack) and the password leaked in settings.php, we try credential reuse against SSH — a mistake as simple as it is common: the same password serves both the CMS's database and the developer's system account.

ssh johncusack@10.129.231.223
johncusack@dog:~$

8. User flag

cat ~/user.txt

9. Privilege enumeration

As always right after landing, we check what the user can run as root:

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

A single line, but decisive: johncusack can run /usr/local/bin/bee as any user (including root) and without a password. We still need to know exactly what bee does to know if that's a road to root.

10. Analysing bee

bee is Backdrop CMS's official admin CLI (the equivalent of Drush in Drupal): it manages modules, themes, cache, users, and more from the command line. Among its subcommands are eval and php-script, meant to let an admin run PHP snippets inside the CMS's already-"bootstrapped" context — with access to its whole internal API.

This isn't a bug in bee: it's by design. Any tool whose explicit job is to evaluate arbitrary code is, by definition, an interpreter running with the privileges of whoever invokes it. Allowing that via sudo with no argument restriction is the same as handing out a root shell: PHP's system() calls straight into the OS shell.

11. CMS location

To run eval, bee needs to locate a valid Backdrop install and complete its bootstrap process; without one, it fails with an insufficient-bootstrap error. We locate the install:

ls /var/www/html
core  files  index.php  LICENSE.txt  modules  README.md  robots.txt  settings.php  sites  themes

bee resolves the install root two ways: from the current directory (cd into it, as we do here) or via an explicit --root=/var/www/html flag. We move into the directory so the automatic detection works:

cd /var/www/html

12. Privilege escalation

With bee now able to bootstrap the CMS, we invoke eval through sudo so it runs as root, and from PHP we spawn a system shell:

sudo bee eval "system('/bin/bash');"
whoami
root

Interactive shell as root, with no additional password and no CVE in the CMS itself required: the entire privesc rests solely on that NOPASSWD sudoers line.

13. Root flag

cat /root/root.txt

System fully compromised. 🏴‍☠️

Vulnerability summary

PhaseComponentVulnerabilityImpact
ReconWeb serverDirectory layout typical of Drupal/Backdrop revealed by fuzzingCMS family identification
FootholdWeb server / Git.git/ reachable over HTTP → full repo dump with git-dumperComplete source code and history, incl. committed secrets
Credentialssites/default/settings.phpPlaintext database password inside the repositoryReusable credential discovered
UserSSH / credential reuseThe CMS DB password is also valid for the developer's system accountInteractive access as johncusack
PrivEscbee (Backdrop CLI) via sudo NOPASSWDThe eval subcommand runs arbitrary PHP (and thus shell commands) as rootFull root

Lessons learned

  • Never deploy with .git reachable in the DocumentRoot: ship only the built/exported artifacts, never the version-control repository.
  • Git history never forgets: "deleting" a secret in a later commit doesn't remove it from history; if it ever leaked, rotate it and scrub the history (git filter-repo / BFG), not just delete it from HEAD.
  • Never reuse passwords across services: an application database password should never also work for a system account over SSH.
  • Audit what you allow via sudo: any binary that can evaluate code (PHP, Python, Perl, Lua...) granted with NOPASSWD is equivalent to handing out a root shell, CVE or not.
  • Keep the CMS updated: an outdated version (here, Backdrop 1.27.1) widens the attack surface even though, in this specific case, the exploitation path didn't rely on a CMS-specific CVE.