Why I did this
I wanted a low stakes way to actually get comfortable with Linux instead of just reading about it. Bandit felt like the right entry point since every level is small enough to finish in one sitting but still forces you to understand what you are typing instead of copy pasting a command and hoping it works.
Going in, I was already comfortable with the basics, ssh, navigating a filesystem, reading man pages, so this was less about learning Linux from zero and more about stacking up small, practical skills and getting used to thinking like an attacker instead of just a user.
Setup
Nothing fancy. A terminal, ssh into the Bandit server on the port they give you, and a text editor for the levels where I ended up writing scripts. I kept a scratch notes file open the whole time so I could jot down what worked and what did not, which ended up being more useful than I expected once the levels got harder and I needed to look back at earlier tricks.
The early levels: 0 to 15
This stretch is mostly about building the habit of looking closer at things instead of taking them at face value. A lot of it comes down to:
- Checking hidden files and directories instead of assuming a listing is complete
- Reading file permissions carefully, since a lot of these levels hinge on who can read or execute what
- Not trusting a file extension, since a file that looks like text can be something else entirely once you actually inspect it
- Getting comfortable digging through encoded or compressed data, since more than one level is really just "decode this, then decode what that gives you, then decode that too"
None of these individually are hard, but the stretch trains you to slow down and actually verify instead of guessing. By the end of it, checking permissions and file types before doing anything else had become automatic.
ls -la
file suspicious_file
find / -perm -4000 2>/dev/null
Where it actually got interesting: levels 16 to 26
This was the stretch I enjoyed the most, and honestly where most of the real learning happened.
Nmap showed up here properly for the first time, not just running it blind but actually reading the output and using it to figure out which port and which service I should even be talking to next. It sounds simple, but there is a real difference between running nmap because a guide told you to and running it because you actually need the answer it gives you to move forward.
nmap -p 31000-32000 localhost
Ncat was the other big one. Using it to open raw connections and manually push data through, instead of leaning on ssh for everything, changed how I thought about what "connecting to a service" even means. It stopped feeling like magic once I had to construct the interaction myself, line by line, and actually understand the protocol I was talking to instead of letting a tool abstract it away.
ncat --ssl <host> <port>
Cron also showed up around here, and that was a good reminder that not everything happens because you typed a command. Some things happen quietly, on a schedule, in the background, and if you are not specifically looking for them you will miss them completely. That shifted how I think about a machine now. It is never just "what is running," it is also "what is scheduled to run."
But the part I liked most, by far, was writing my own scripts. Some levels basically force you to automate something because doing it by hand one attempt at a time is not realistic. My first few attempts did not work. Wrong quoting, wrong assumptions about what a command would actually output, forgetting that a script running non interactively does not behave the same way as typing the same commands manually one at a time. I rewrote the same handful of lines more times than I want to admit before it finally ran clean.
#!/bin/bash
# an early, broken attempt, kept here on purpose
for i in $(seq 1 100); do
result=$(some_command $i)
# this is where it kept failing silently, no error, just wrong output
done
Getting that loop to actually behave correctly was more satisfying than most of the earlier levels combined. It is one thing to find a password by poking around manually, it is another thing to write something that finds it for you reliably, every time, without you babysitting it.
Dead ends I hit
I want to be honest about this part, because writeups that only show the clean solution never match what actually happens while you are in it.
- I got stuck on a level where I needed to
sshback into the machine locally, from inside the machine itself. I kept assuming there was some other trick I was missing, some clever workaround, when really I was overcomplicating a step that just needed a normal, boring local connection. Sometimes the answer really is the obvious one. - More than one of my scripts failed quietly instead of loudly. No error thrown, just wrong output, which is the worst kind of bug to chase down because there is nothing pointing you to the problem. I ended up adding print statements at nearly every step just to see what the script actually thought was happening, which is a debugging habit I am keeping going forward.
- A couple of times I was technically doing the right thing but in the wrong order, running a step before its dependency was actually ready, and it took stepping away and coming back with fresh eyes to notice it.
- I also lost time once assuming a tool's default settings would just work, when the level actually needed a specific flag to behave the way I expected. Lesson learned: read the manual page before assuming.
Levels 26 to 27: the one that got me
This is the level that genuinely got me. My login shell was not a normal shell, and getting a real one required manipulating my terminal window itself, specifically resizing it in a very deliberate way, combined with an editor trick, to get everything to line up correctly.
I remember just staring at my terminal thinking "why would resizing my screen matter" before it clicked that the dimensions of the window itself were part of the puzzle. That is not something you expect to matter when you are used to thinking of the terminal as just a text box you type into. Once it worked, it was one of those moments where you laugh out loud a little at how unexpected the solution turned out to be. It is still the level I bring up first when people ask about Bandit.
Levels 27 to 33
The later levels leaned more on combining everything from earlier rather than introducing something completely new, which felt like a fair test of whether the habits from the first 26 levels had actually stuck. Version control concepts, more careful permission reading, and a couple of levels that punished me for not double checking my own assumptions before running a command. By this point the biggest shift was less about learning new tools and more about trusting the process: check permissions, check file types, check what is scheduled, and do not skip a step just because it worked that way last time.
Tools I actually used
nmapfor figuring out what is even listeningncatfor talking to services directlysshconstantly, including the local connection that tripped me upfind,file, and permission checks as a first move on nearly every level- Basic decoding and encoding tools for the levels that layered encodings on top of each other
- Bash scripting once doing things by hand stopped being realistic
What I took from it
Nmap and ncat stopped being commands I run because a tutorial told me to, and became tools I reach for because I understand why they are useful and what they are actually telling me. Cron reminded me to think about what might be running in the background, not just what is directly in front of me. But the scripting was the real win. Debugging my own broken loops taught me more than any single level did, because it forced me to actually understand what my code was doing instead of just hoping it worked and moving on.
The biggest overall shift was patience. Early on I wanted the fast answer. By the end I was comfortable slowing down, checking my assumptions, and rewriting something three or four times if that is what it took to actually understand why it was not working in the first place.
Levels 34 onward are next. Leaving this one here as a checkpoint before going further.