Bandit 20 → 21: be the other side
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.