August 11, 2026
How I Found a Hidden .git Repository and Leaked an Entire Website — TryHackMe Room 404
A beginner-friendly walkthrough of how directory enumeration exposed source code, Git history, and a secret left behind in an old commit.
By Souhardya
6 min read
Objective
TryHackMe's Hacker Holidays 2026 event dropped a beginner-friendly web enumeration challenge. The fictional Byte Lotus Hotel had rushed a new guest-experience platform to production, and the brief hinted at something specific: "the night-shift developer shipped more than the website."
The goal: figure out what got exposed alongside the website, and recover the flag hidden inside it.
If you're new to this kind of challenge, this write-up walks through not just what I ran, but why — so you can follow the same reasoning on a similar box.
Environment
- Attacker machine: Kali Linux, running inside VirtualBox as a VM
- Connectivity: TryHackMe's OpenVPN service, connected with
sudo openvpn --config <your-profile>.ovpn - Target: A TryHackMe-hosted lab machine, with a web service running on port 8080
If you're setting this up yourself: TryHackMe gives you a .ovpn configuration file from the room's access page. Kali ships with OpenVPN pre-installed, so no extra software is needed — just run the connect command above and watch for Initialization Sequence Completed in the output before moving on.
Passive Reconnaissance
Before running a single tool, it's worth just reading the challenge brief closely — a lot of the "recon" in beginner rooms is actually right there in the wording.
Two details stood out to me:
- Port 8080 was explicitly called out as open — that's where the actual application lives, separate from the standard web port 80
- "Shipped more than the website" is a strong, deliberate hint. In real-world web development, this phrase almost always points to one thing: a leftover deployment artifact — files that were never meant to be public but got copied to the live server anyway (backup files, config files, or version control folders)
This gave me a working hypothesis before I even touched the target: check for exposed version control (a .git folder) before anything else. Here's why that's a good first guess — when developers deploy code by literally copying their whole project folder onto a server (instead of using a proper build/deploy pipeline), the hidden .git folder — which stores the entire history of every change ever made to the code — often gets copied right along with it. If the web server doesn't explicitly block access to dotfiles/dotfolders, that .git folder becomes browsable to anyone who knows to look for it.
Active Reconnaissance
Step 1 — Confirm the service is actually live
curl -I http://<MACHINE_IP>:8080curl -I http://<MACHINE_IP>:8080curl -I sends a HEAD request and shows just the response headers, not the full page — a fast way to sanity-check a target before running heavier tools. This returned a clean 200 OK, and the Server header identified Werkzeug/Python (Flask) — Python's built-in development web server. That's a small but useful clue on its own: dev servers like this are meant for local testing, not production, and are commonly deployed "as-is" without the hardening a production server would have (like blocking access to hidden files).
Step 2 — Directory enumeration
This is the core technique for this whole challenge, so it's worth explaining properly. A website only shows you the pages it links to — but there are almost always other files and folders sitting on the server that aren't linked anywhere. Directory enumeration means systematically guessing common file/folder names and checking the server's response for each one, to find what's there but not advertised.
I used Gobuster, a fast wordlist-based enumeration tool:
gobuster dir -u http://<MACHINE_IP>:8080 \
-w /usr/share/wordlists/dirb/common.txt \
-x php,txt,zip,git,bak,envgobuster dir -u http://<MACHINE_IP>:8080 \
-w /usr/share/wordlists/dirb/common.txt \
-x php,txt,zip,git,bak,envBreaking this command down:
dir— tells gobuster to run in directory/file brute-force mode-u— the target URL-w— the wordlist to try;common.txt(built into Kali) is a solid general-purpose list of common file/folder names-x— file extensions to also test for each word (so it triesadmin, but alsoadmin.php,admin.bak,admin.env, etc.) — I specifically includedgit,bak, andenvsince those are exactly the kinds of accidental-exposure files this challenge was hinting at
The scan returned one especially interesting hit:
.git/HEAD (Status: 200) [Size: 21].git/HEAD (Status: 200) [Size: 21]A 200 OK response here is the key finding. .git/HEAD is a small internal file that every Git repository has — it just points to the current branch. On its own it's not very useful, but its presence with a 200 status proves the entire .git folder is publicly accessible, not just this one file. That's the whole vulnerability in a nutshell.
Step 3 — Independent verification with Nmap
I like to confirm a finding with a second tool before acting on it, so I also ran:
nmap -sC -sV -oN nmap/initial <MACHINE_IP>nmap -sC -sV -oN nmap/initial <MACHINE_IP>-sC runs Nmap's default script set, which includes a script specifically called http-git that checks for exactly this kind of exposure. It confirmed the same thing gobuster found — and as a bonus, it read the exposed repository's metadata directly, showing me the last commit message without me needing to download anything yet. This cross-check matters: a single tool's result could be a fluke or misconfigured wordlist match, but two independent tools agreeing gives real confidence.
Tool Selection Rationale
For readers building their own methodology, here's why I reached for each tool, not just that I used it:
Exploitation — Reconstructing the Repository
Knowing .git is exposed isn't the same as having the code. A Git repository stores its data as a set of compressed internal objects, not as plain readable files — so you need a tool that walks the exposed directory structure and rebuilds a working repository from those objects.
pip install git-dumper --break-system-packages
git-dumper http://<MACHINE_IP>:8080/.git/ ./bytelotus_repopip install git-dumper --break-system-packages
git-dumper http://<MACHINE_IP>:8080/.git/ ./bytelotus_repogit-dumper fetches every object it can find from the exposed .git/ path and reconstructs a real, usable local Git repository in the bytelotus_repo folder — complete with full history, not just the latest code.
cd bytelotus_repo
git log --all --onelinecd bytelotus_repo
git log --all --onelinegit log --all --oneline lists every commit across every branch in one line each — a quick overview of the repository's whole timeline.
The key idea: Git never really deletes anything
This is the concept that makes this whole class of vulnerability dangerous, and it's worth understanding properly rather than just memorizing the command. If a developer accidentally commits a secret — an API key, a password, a flag — and then "removes" it in a later commit, that secret is not gone. Git's entire design is built around keeping a permanent history of every version of every file. The old commit, secret and all, still exists in the repository's object database and is fully recoverable — unless someone deliberately rewrites history (with a tool like git filter-repo) and the exposed copy is deleted before anyone dumps it.
So instead of just looking at the current files, I searched the entire history:
git log -p --all | grep -iE "flag|secret|password|api_key"git log -p --all | grep -iE "flag|secret|password|api_key"Breaking this down:
git log -p --allprints every commit, across all branches, including the actual code changes (diffs) made in each one| grep -iE "flag|secret|..."filters that huge output down to just lines matching likely keywords, case-insensitive
This is how the flag was found — not in the live website, but sitting in an old commit that had since been "removed" from the current version of the code.
Results
The flag was recovered from commit history, not the live application — which is exactly the lesson this room was built to teach. A deployed app can look completely clean on the surface while its full development history — including anything ever committed by mistake — sits one accidental directory away from anyone who checks.
(The flag is omitted so readers can complete the room themselves.)
Security Analysis / Implications
It's worth being clear that this wasn't a clever exploit against a vulnerability in some software — it was a process and configuration failure, and that's exactly why it's worth writing up. This exact mistake happens constantly in real production environments:
- CI/CD pipelines that
rsyncorcp -ran entire project folder onto a server, instead of deploying a clean, built artifact that excludes.git - Missing deployment rules to explicitly exclude
.git,.env, and backup files from what gets copied to the live server - No web server configuration blocking access to dotfiles/dotfolders (e.g. in Nginx:
location ~ /\. { deny all; })
How a real team prevents this:
- Deploy only the built application artifact — never the raw project directory,
.gitincluded - Explicitly deny web access to dotfiles and dotfolders at the server config level
- Treat anything ever committed to Git as permanently sensitive — if a secret lands in a commit, rotate it immediately; deleting the commit afterward does not undo the exposure
- Periodically run the same recon an attacker would (gobuster/nmap sweeps of your own public assets) as a routine security check, not just during pentests
Conclusion
A two-command gobuster scan was all it took to go from "just a hotel booking website" to a full local copy of the source code and its entire commit history. For anyone starting out in security, this room is a great example of how some of the most damaging real-world findings come from simple configuration oversights, not advanced exploits — and why defenders should be enumerating their own infrastructure the same way an attacker would, before someone else does.
Skills Practiced
Directory Enumeration · Nmap NSE Scripting · Git Internals & Commit History Analysis · Web Reconnaissance Methodology · Secure Deployment Practices