~$ skillshelf
← OverTheWire Bandit

Bandit 20 → 21: be the other side

banditnetworkingnetcatsetuidtmux

The home directory holds one binary, and it will hand over the next password if you can prove you’re bandit20. The catch is the direction of travel: it doesn’t listen for you, it connects to you. So before anything can happen there has to be something for it to connect to — and building that, in a shell that only does one thing at a time, is the entire level.

the goal

There is a setuid binary in the homedirectory that does the following: it makes a connection to localhost on the port you specify as a commandline argument. It then reads a line of text from the connection and compares it to the password in the previous level (bandit20). If the password is correct, it will transmit the password for the next level (bandit21).

The binary also explains itself, which is worth doing before anything else:

./suconnect
Usage: ./suconnect <portnumber>
This program will connect to the given port on localhost using TCP. If it
receives the correct password from the other side, the next password is
transmitted back.

From the other side. That phrase is the whole level.

the approach

Permissions first, same as last time:

-rwsr-x---   1 bandit21 bandit20 15604 Jun 24 14:59 suconnect

s where the owner’s x belongs, owner bandit21. It runs as bandit21 no matter who launches it, which is how it reads /etc/bandit_pass/bandit21 when I can’t. The difference from bandit20-do is scope: that one ran any command handed to it, this one runs exactly one hardcoded routine.

Two things bite before the level proper starts. suconnect alone gives command not found — . isn’t in PATH, deliberately, since otherwise a hostile file named ls dropped into a directory you cd into would shadow the real one. ./ means this exact file, no searching. And then:

./suconnect 4444
Could not connect

That reads like failure and is the answer. Nothing was listening on 4444, so the connection had nowhere to land. suconnect is a client. It knocks. Something else has to be the door.

two shells

A listener blocks — it holds the terminal until someone connects — so this needs two live shells. That’s why tmux and job control are on the level’s command list.

Two shell facts worth having straight before you start. & is not “and then”: it backgrounds the thing on its left and hands the prompt straight back, so tmux & <password> reads as two separate commands — start tmux in the background, then execute the password as a program name. That produces a command not found with a password inside it, plus a stray session. && is the one that chains.

And Ctrl-B c makes a new window, Ctrl-B " makes a new pane. Windows are tabs; panes are splits inside one tab. Reaching for c gets you panes scattered across windows you can only see one of at a time. The keys that matter:

Ctrl-B "      split the current window into two panes
Ctrl-B o      jump to the other pane
Ctrl-B z      zoom one pane fullscreen, press again to restore
Ctrl-B [      scroll back through output, q to exit

Ctrl-B is a prefix, so pressing it does nothing visible on its own — it arms tmux to interpret the next key. Nothing happening is what success looks like.

building the door

nc again — same tool as 14 → 15, opposite role. The grammar takes two tries:

nc 4444        # nc: missing port number
nc -l          # nc: missing port number

The first fails because nc takes [destination] [port] as two positional arguments; with only one, 4444 is read as the hostname and nothing follows it. The second fails because listen mode still has to be told which port to bind. The port is mandatory either way — -l only decides whether that port is somewhere you’re going or somewhere you’re sitting.

nc -l 4444

No output, no prompt, terminal held. That’s it working. Other pane:

./suconnect 4444

And then nothing — the exact trap from 14 → 15 arriving from the opposite direction. Back then I was the one connecting to a silent port, waiting for a prompt that was never coming. This time I was the silent port. suconnect had connected and was waiting for a line of text, and no amount of waiting on my end was going to produce one.

Typing the bandit20 password into the nc pane and pressing Enter:

nc -l 4444
<bandit20 password>
<bandit21 password>

And in the other pane:

./suconnect 4444
Read: <bandit20 password>
Password matches, sending next password

The prize comes back in the listener’s pane, not the one running suconnect. A single TCP connection carries both directions, so the reply surfaces wherever the socket lives — easy to miss if you’re only watching the binary you just launched.

the takeaway

Client and server are roles, not tools. Same nc, one flag apart: without -l it knocks, with -l it waits. Once the question stops being “what command solves this” and becomes “which end am I”, the level is three commands.

Two more worth keeping:

  • The side that waits still has to talk first. A listening socket is not a prompt. Nothing announces the connection and nothing asks for input — if the protocol says the client speaks first, silence is the protocol working.
  • A connection is one two-way pipe. The answer comes back where you’re listening, not where you launched the thing that triggered it.