Bandit 28 → 29: the file was redacted, the history wasn't
Last level, git was only the transport — one commit, one file, password sitting in
the working tree. This is the same setup with the puzzle switched on. The clone
works first try, the README is right there, and the password field has been
replaced with xxxxxxxxxx. That looks like the level is broken. It’s the level
working.
the goal
There is a git repository at
ssh://bandit28-git@localhost/home/bandit28-git/repovia the port2220. The password for the userbandit28-gitis the same as for the userbandit28. From your local machine (not the OverTheWire machine!), clone the repository and find the password for the next level.
Same wording as 27 → 28 apart from the username — the page tells you nothing about
what’s different. localhost is still your own machine, so the host swap from last
level still applies.
the approach
One file comes down, README.md, with the password line blanked:
## credentials
- username: bandit29
- password: xxxxxxxxxx
The first theory worth ruling out is .gitignore, and it’s worth ruling out
explicitly: .gitignore decides which untracked files get added to the
repository. It has no power to blank a line inside a file that’s already tracked,
and anything that arrived in the clone is tracked by definition. Whatever emptied
that field, it wasn’t ignore rules.
dead end: grep
The obvious search doesn’t work:
grep -rniH -- password .
Two reasons. The working files genuinely don’t contain it. And with nothing
excluded it walks .git as well — git’s object store is zlib-compressed, so a
plain-text search across it finds nothing useful and prints binary noise for a
long time. Mine put close to 2 GB into a terminal log before I killed it.
That’s the real lesson of the dead end. grep reads files. The clone brought
down more than files — the working directory is one view of the repository, and
the whole thing was already on disk. The tool has to read the repository, not the
view.
dead end: git-log
git help -a is one flat alphabetical dump and hard to scan. git help everyday
groups commands by what you’re trying to do, and the standalone-developer list has
exactly one line matching “see what happened”:
git-log(1) to see what happened.
Typed literally, that fails:
git-log
zsh: command not found: git-log
The hyphen is a man-page naming convention — git-log(1) is the manual page for
the log subcommand, and (1) is the man section number, not something you type.
You run it as two words.
the history
git log
commit 83d77407b76c9f86ac4e691a47618641c9d527ba (HEAD -> master, origin/master, origin/HEAD)
fix info leak
commit 13bbc4d2414ffe0439b8ee4f5e5c2949780cf4b3
add missing data
commit f3334fbccbf9446a6af88a3c71021c2f57163322
initial commit of README.md
Three commits, and the messages give the whole game away. Someone added the data, then “fixed” the leak. The fix is the newest commit — the one I’m looking at in the working tree — and the thing it removed is still recorded in the commit that removed it.
git show with no argument shows HEAD, so it doesn’t even need the id:
git show
fix info leak
- username: bandit29
-- password: <redacted>
+- password: xxxxxxxxxx
The - line is the password. The + line is the xxxxxxxxxx from the working
tree. A diff shows both sides, so the commit that deleted the secret is also the
commit that displays it.
the takeaway
A clone is the whole repository, not the current files. Deleting a secret in a later commit doesn’t remove the secret — it removes it from the working tree while leaving it permanently recorded in the history, and anyone who can clone the repo can read it back. That’s not a Bandit trick; it’s the single most common way credentials leak out of real repositories. It’s also why the answer to a committed secret is rotate the credential, not push a follow-up commit. The follow-up commit is what tells people where to look.
Two smaller bricks:
Commit messages are a map. fix info leak isn’t a description, it’s an arrow.
Read the log before running anything else — the history is searchable prose
written by whoever made the change, and people describe what they did far more
honestly than they realise.
Man pages name subcommands with a hyphen; you type them with a space.
git-log(1) in documentation means you run git log. The (1) is the man
section. Same for every git-* page you’ll ever read.