~$ skillshelf
← OverTheWire Bandit

Bandit 27 → 28: localhost is wherever you're standing

banditlinuxgitssh

After a run of levels that all lived inside the box, this one pushes you back out to your own machine. The password is in a git repository you clone over SSH — and the level’s own URL is the thing that breaks it, because it’s written from the box’s point of view and you’re told to run it from yours.

the goal

There is a git repository at ssh://bandit27-git@localhost/home/bandit27-git/repo via the port 2220. The password for the user bandit27-git is the same as for the user bandit27. From your local machine (not the OverTheWire machine!), clone the repository and find the password for the next level.

the approach

I scripted the clone rather than typing it, so the working invocation would still exist after the terminal was gone. First version, straight off the level page — localhost, port in the URL, cloning into a scratch dir:

WORKDIR=$(mktemp -d /tmp/b27.XXXXXX)
cd "$WORKDIR"
git clone ssh://bandit27-git@localhost:2220/home/bandit27-git/repo
Cloning into 'repo'...
ssh: connect to host localhost port 2220: Connection refused
fatal: Could not read from remote repository.

Ran it again in case it was a blip. Same refusal, instantly — and instantly is the tell. A firewall or a wrong port on a remote host makes you wait for a timeout; Connection refused came back immediately because something local answered and said no. Nothing is listening on port 2220 of my laptop.

Which is the whole level. localhost isn’t a place, it’s a pronoun — it means this machine, resolved fresh wherever the command runs. The URL on the level page is correct as written on the game server, where port 2220 is the box’s own sshd. The instructions then tell you to run it from your local machine, and the moment you do, localhost quietly re-points at you.

So the fix is just to say which host out loud. I already had a bandit entry in my ssh config from every previous level, and it carries the port, so the URL collapses to:

git clone ssh://bandit27-git@bandit/home/bandit27-git/repo
bandit27-git@bandit.labs.overthewire.org's password:
remote: Enumerating objects: 3, done.
Receiving objects: 100% (3/3), done.

The password it prompts for is bandit27’s — the level says so, and it’s the one I already had. Three objects come down: a commit, a tree, and one file. The clone lands a single README, and the password for the next level is sitting in it in plain text (redacted here).

Worth noting what isn’t needed. There’s no history to dig through, no deleted file, no branch hiding anything — one commit, one file, password in the working tree. Git is the transport here, not the puzzle. That comes later.

the takeaway

localhost is resolved by whoever runs the command, not by whoever wrote it. Copying a connection string from documentation into a different machine is one of the easiest ways to waste ten minutes, and the error is unhelpful precisely because it’s honest — nothing is listening on your own port 2220. When a host in a URL was written from somewhere else’s perspective, replace it before you replace anything else.

Two smaller bricks:

A git remote carries its port inside the URL. It’s ssh://user@host:2220/path, not a -p flag — git clone has nowhere to put one, because the port belongs to the SSH URL scheme, not to git. And if the host has an ssh config entry, that entry’s Port applies here too, which is why the working version has no port in it at all.

Connection refused and a hang mean different things. Refused is a machine that answered and declined; a hang is a packet that went nowhere. The one that comes back instantly is telling you that you reached something — usually yourself.