← all walkthroughs

Dog

Linux· Easy
owned
2026-07-06
time to own
4m48s
milestone
root-owned
user.txt
✓ captured
root.txt
✓ captured

Summary

I scanned the internet-facing Apache server and identified a Backdrop CMS site. The web root had its .git directory left publicly accessible, allowing a full download of the application source tree.

That source tree contained the CMS configuration file committed in plain text with a MySQL password. That same password had been reused as the SSH login for the johncusack OS account, granting an interactive shell and the first flag with no exploitation required.

On the system, a sudo rule permitted johncusack to run the Backdrop CMS command-line tool bee — which includes an eval subcommand that executes arbitrary PHP — as root. One sudo command calling PHP's system() function read the root flag and completed full system compromise.

Command conventions

The commands below refer to the target by variable rather than by address. Bind them in your shell before running anything; recovered credentials are withheld and shown as [REDACTED: recovered credential].

export TARGET="<retired-instance-ip>"

Attack path — how the box was taken

1ReconnaissanceNetwork service and version enumeration (T1046)
Scanned open ports and fingerprinted the Backdrop CMS application
An Nmap service scan confirmed two open ports: SSH on 22 and Apache HTTP on 80. The HTTP response included the header X-Generator: Backdrop CMS 1, immediately identifying the CMS brand and version family. This directed I to look for Backdrop-specific configuration paths and its known credential file locations.
Exact commands 2
Identify open ports and grab service banners; default scripts capture HTTP headers including X-Generator.
nmap -sV -sC -p 22,80 --open $TARGET
Confirm the Backdrop CMS fingerprint directly from response headers.
curl -si http://$TARGET/ | grep -i 'x-generator\|server'
2ExploitationExposed version-control metadata — unauthenticated source disclosure (T1552.001)
Downloaded the entire application source code via an exposed .git directory
The Apache web server served the application's .git directory over plain HTTP with no access restrictions. Using the git-dumper tool, I reconstructed the complete source tree locally — including every committed file and the full commit history. The initial commit was authored as root <dog@dog.htb>, confirming this was the live production codebase checked into a web-accessible location.
0000...0000 8204779c... Root <dog@dog.htb> 1738963331 +0000 commit (initial): todo: customize url aliases.
Exact commands 3
Probe for .git exposure; a valid HEAD response (e.g. 'ref: refs/heads/main') confirms the repository is readable.
curl -s http://$TARGET/.git/HEAD
Recursively download and reconstruct the full git repository to the local ./dog-src directory.
git-dumper http://$TARGET/.git ./dog-src
Review commit history for context on what was committed and by whom.
git -C ./dog-src log --oneline
FixBlock web server access to the .git directoryHigh
WeaknessThe Apache server delivered the application's entire .git directory over HTTP without authentication, allowing anyone on the internet to download the complete source tree including all committed configuration and secrets.
FixAdd an Apache directive to deny requests to any dot-directory. In the site's VirtualHost block or root .htaccess, add: <DirectoryMatch "(?:^|/)\.+"> Require all denied </DirectoryMatch>. Confirm with curl -s -o /dev/null -w "%{http_code}" http://$TARGET/.git/HEAD returning 403. More broadly, restructure the deployment so the web root contains only public assets — never the application's .git folder, source code, or configuration files. Use a CI/CD pipeline that builds and copies only the output artifact to the web root.
3Credential HarvestCredentials in files committed to source control (T1552.001)
Extracted a plaintext database password from the committed CMS configuration file
Browsing the recovered source tree, I found sites/default/settings.php — Backdrop's database configuration file — committed in full. Line 15 contained a hardcoded MySQL connection string with the password [REDACTED: recovered credential] in plain text. No authentication, brute-forcing, or decryption was needed: the secret was delivered along with the source code.
Exact commands 2
Read the Backdrop database configuration file from the recovered repository.
cat ./dog-src/sites/default/settings.php
Broader sweep for any additional credentials in the config directory.
grep -r 'mysql\|password\|BackDrop' ./dog-src/sites/default/
FixRemove plaintext credentials from version-controlled configuration filesCritical
WeaknessThe Backdrop settings.php file containing a plaintext MySQL password was committed to git. Anyone who obtained the repository — which in this case required only an HTTP request — received working database credentials instantly.
FixAdd sites/default/settings.php to .gitignore immediately and verify it is excluded. Rotate the compromised MySQL password [REDACTED: recovered credential] now. Store all secrets as environment variables or in a secrets manager (HashiCorp Vault, AWS Secrets Manager, or a deployment-only .env file that is git-ignored) and read them at application startup. Audit the full git history for any other committed secrets using truffleHog or gitleaks and rotate everything found.
4Initial AccessValid accounts — credential reuse across services (T1078)
Authenticated over SSH by reusing the CMS database password
The recovered password [REDACTED: recovered credential] was sprayed against OS usernames derived from the site's commit author (dog@dog.htb) and the application context. It authenticated immediately as johncusack (uid=1001, group 'dog'), granting a full interactive shell. The user flag was readable directly from the home directory.
Exact commands 2
Log in with the recovered CMS database password; sshpass supplies it non-interactively.
sshpass -p '[REDACTED: recovered credential]' ssh -o StrictHostKeyChecking=no johncusack@$TARGET
Confirm the active user identity and read the user flag (<user.txt>).
id && cat /home/johncusack/user.txt
FixUse unique passwords for every service and OS accountCritical
WeaknessThe MySQL database password was identical to the SSH login password for the johncusack operating system account. A single leaked secret gave an unauthorised user a full interactive shell on the host.
FixImmediately change the johncusack SSH password and the MySQL root password to strong, unique values that are not shared with any other system. Enforce a policy prohibiting password reuse across services, databases, and OS accounts. Disable password-based SSH authentication entirely and require SSH key pairs: set PasswordAuthentication no and PubkeyAuthentication yes in /etc/ssh/sshd_config, then restart sshd.
5Privilege EscalationSudo misconfiguration — unrestricted CMS CLI with PHP eval (T1548.003)
Executed arbitrary commands as root via sudo access to the bee CLI eval subcommand
Listing johncusack's sudo rights revealed permission to run /usr/local/bin/bee — the Backdrop CMS command-line administration tool — as root. The bee eval subcommand passes its argument directly to the PHP runtime for execution. This is functionally identical to having sudo access to PHP itself: a single call to PHP's built-in system() function executed any OS command as root. The bee binary must be invoked from the Backdrop web root (/var/www/html) for it to bootstrap correctly.
Cd /var/www/html && sudo -S /usr/local/bin/bee eval 'system("id; hostname; cat /root/root.txt");' returned root output.
Exact commands 3
List johncusack's sudo entitlements; look for an entry permitting /usr/local/bin/bee.
sudo -l
Verify code execution as root; bee requires the Backdrop web root as the working directory.
cd /var/www/html && printf '%s\n' '[REDACTED: recovered credential]' | sudo -S /usr/local/bin/bee eval 'system("id");'
Read the root flag (<root.txt>).
cd /var/www/html && printf '%s\n' '[REDACTED: recovered credential]' | sudo -S /usr/local/bin/bee eval 'system("cat /root/root.txt");'
FixRemove sudo access to the bee CLI eval subcommandCritical
Weaknessjohncusack was permitted to run /usr/local/bin/bee as root via sudo. The bee eval subcommand is an unrestricted PHP interpreter, so this sudo rule was functionally equivalent to granting the account a root shell.
FixRemove the bee entry from /etc/sudoers and all files under /etc/sudoers.d/ immediately (use visudo to edit safely). If specific Backdrop administrative commands genuinely require elevated privileges, create a narrowly scoped rule for only those exact subcommands (e.g., bee updatedb) and never permit eval or any subcommand that accepts arbitrary code input. Audit all sudoers rules for any other dangerous entries — php, python, perl, ruby, node, bash, and any CMS or framework CLI tool with an eval-equivalent feature should be treated as equivalent to unrestricted root access.

Attack patterns used

The transferable techniques behind this compromise.

Password / Credential ReuseCredential Access · Lateral MovementT1078

What it is

A password recovered from one place — a config file, a database, a cracked hash, a service account — is tried against other accounts and services (SSH, SMB, WinRM, sudo, the database, the next host). Reuse turns a single leaked secret into broad access.

Why it works

Humans and deployments reuse passwords across accounts and tiers, and lateral movement thrives on it. Remediate with unique credentials per account/service, a password manager/vault, and MFA on remote-access services.

Read more

Exposed services

22/tcp
80/tcp