GET /blog
Bandit OverTheWire Walkthrough: Levels 0 to 32
A complete OverTheWire Bandit walkthrough with commands and passwords for levels 0 to 32. Learn Linux, SSH, cron jobs, git, and shell escapes step by step.
Index
- Level 0 — SSH in and read the
readmefile - Level 1 — Read a file named
- - Level 2 — Read a file with spaces in its name
- Level 3 — Find a hidden dotfile
- Level 4 — Spot the human-readable file among binaries
- Level 5 — Find a file by size and permissions
- Level 6 — Locate a file by owner, group, and size
- Level 7 —
grepa keyword indata.txt - Level 8 — Find the line that occurs only once
- Level 9 — Pull readable strings preceded by
= - Level 10 — Decode base64 data
- Level 11 — Undo a ROT13 rotation
- Level 12 — Unwind repeated compression from a hexdump
- Level 13 — Log in with a private SSH key
- Level 14 — Submit the password over
ncto port 30000 - Level 15 — Talk to port 30001 over SSL/TLS
- Level 16 — Scan ports 31000–32000 and find the SSL server
- Level 17 —
diffthe changed line between two files - Level 18 — Bypass a
.bashrcthat logs you out - Level 19 — Use a setuid binary to read the password
- Level 20 — Answer a setuid binary with a local server
- Level 21 — Read a cron job’s output
- Level 22 — Reproduce a cron job’s target filename
- Level 23 — Drop a script for cron to execute
- Level 24 — Brute-force a 4-digit pincode over
nc - Level 25 — Escape a restricted
more/vimshell - Level 26 — Run commands via
:terminsidevim - Level 27 — Clone a git repo and read the README
- Level 28 — Recover a password from git history
- Level 29 — Find the password on a
devbranch - Level 30 — Read a git tag with
git show - Level 31 — Push a file past
.gitignoreand a pre-receive hook - Level 32 — Break out of an uppercase-only shell
Level 0
ssh into the machine ssh bandit0@bandit.labs.overthewire.org -p 2220 with password as bandit0 -> on the home dir you can see a readme file. Inside that the password will be there for Level 2
pass: ZjLjTmM6FvvyRnrb2rfNWOZOTa6ip5If
Level 1
ssh using bandit1 as username and password from level 0
found a file called - in the home directory. This was a bit tricky because traditional cli tools will interpret it as a command when passed as an argument so
cat ./- -> 263JGJPfgU6LtdEvgfWU1XP5yac29mFx
Level 2
bandit2@bandit:~$ ls
--spaces in this filename--
bandit2@bandit:~$ cat ./"--spaces in this filename--"
MNk8KNH3Usiio41PRUEoDFPqfxLPlSmx
Level 3
bandit3@bandit:~$ ls
inhere
bandit3@bandit:~$ cd inhere/
bandit3@bandit:~/inhere$ ls
bandit3@bandit:~/inhere$ ls -la
total 12
drwxr-xr-x 2 root root 4096 Oct 14 09:26 .
drwxr-xr-x 3 root root 4096 Oct 14 09:26 ..
-rw-r----- 1 bandit4 bandit3 33 Oct 14 09:26 ...Hiding-From-You
bandit3@bandit:~/inhere$ cat ...Hiding-From-You
2WmrDFRmJIq3IPxneAaMGhap0pFhF3NJ
Level 4
Inside inhere dir there are 10 files with garbage, except in file07
bandit4@bandit:~/inhere$ cat ./-file07
4oQYVPkxZOOEOO5pTW81FB8j8lxXGUQw
Level 5
Description
The password for the next level is stored in a file somewhere under the inhere directory and has all of the following properties:
- human-readable
- 1033 bytes in size
- not executable
bandit5@bandit:~/inhere$ for i in {0..9}; do echo $i; ls -la "maybehere0$i" | grep 1033; done
0
1
2
3
4
5
6
7
-rw-r----- 1 root bandit5 1033 Oct 14 09:26 .file2
8
9
bandit5@bandit:~/inhere/maybehere07$ ls -la
total 56
drwxr-x--- 2 root bandit5 4096 Oct 14 09:26 .
drwxr-x--- 22 root bandit5 4096 Oct 14 09:26 ..
-rwxr-x--- 1 root bandit5 3663 Oct 14 09:26 -file1
-rwxr-x--- 1 root bandit5 3065 Oct 14 09:26 .file1
-rw-r----- 1 root bandit5 2488 Oct 14 09:26 -file2
-rw-r----- 1 root bandit5 1033 Oct 14 09:26 .file2
-rwxr-x--- 1 root bandit5 3362 Oct 14 09:26 -file3
-rwxr-x--- 1 root bandit5 1997 Oct 14 09:26 .file3
-rwxr-x--- 1 root bandit5 4130 Oct 14 09:26 spaces file1
-rw-r----- 1 root bandit5 9064 Oct 14 09:26 spaces file2
-rwxr-x--- 1 root bandit5 1022 Oct 14 09:26 spaces file3
bandit5@bandit:~/inhere/maybehere07$ cat .file2
HWasnPhtq9AVKe0dmk45nxy20cvUa6EG
Level 6
The password for the next level is stored somewhere on the server and has all of the following properties:
- owned by user bandit7
- owned by group bandit6
- 33 bytes in size
bandit6@bandit:~$ find / -user bandit7 -group bandit6 -size 33c 2>/dev/null
/var/lib/dpkg/info/bandit7.password
bandit6@bandit:~$ cat /var/lib/dpkg/info/bandit7.password
morbNTDkSW6jIlUc0ymOdMaLnOlFVAaj
Level 7
The password for the next level is stored in the file data.txt next to the word millionth
bandit7@bandit:~$ cat data.txt | grep millionth
millionth dfwvzFQi4mU0wfNbFOe9RoWskMLg7eEc
Level 8
The password for the next level is stored in the file data.txt and is the only line of text that occurs only once
bandit8@bandit:~$ sort data.txt | uniq -u
4CKMh1JI91bUIZZPXDqGanal4xvAg0JM
bandit8@bandit:~$
Level 9
The password for the next level is stored in the file data.txt in one of the few human-readable strings, preceded by several ‘=’ characters.
bandit9@bandit:~$ strings data.txt | grep =
FB`=
c\5D=
========== the
?/=l
=Uc1
=vG*2P
========== password
k=ezG
E========== is
=%r_
.?=Dm
O&A=n
5========== FGUW5ilLVJrxX9kMYMmlN4MgbpfMiqey
=*^Y
=L3jT
q<=,
'QHE=
+=NBf
bandit9@bandit:~$
Level 10
The password for the next level is stored in the file data.txt, which contains base64 encoded data
bandit10@bandit:~$ cat data.txt
VGhlIHBhc3N3b3JkIGlzIGR0UjE3M2ZaS2IwUlJzREZTR3NnMlJXbnBOVmozcVJyCg==
bandit10@bandit:~$ cat data.txt | base64 -d
The password is dtR173fZKb0RRsDFSGsg2RWnpNVj3qRr
Level 11
The password for the next level is stored in the file data.txt, where all lowercase (a-z) and uppercase (A-Z) letters have been rotated by 13 positions
bandit11@bandit:~$ cat data.txt
Gur cnffjbeq vf 7k16JArUVv5LxVuJfsSVdbbtaHGlw9D4
bandit11@bandit:~$ cat data.txt | tr 'a-z' 'n-za-m' | tr 'A-Z' 'N-ZA-M'
The password is 7x16WNeHIi5YkIhWsfFIqoognUTyj9Q4
Level 12
The password for the next level is stored in the file data.txt, which is a hexdump of a file that has been repeatedly compressed. For this level it may be useful to create a directory under /tmp in which you can work. Use mkdir with a hard to guess directory name. Or better, use the command “mktemp -d”. Then copy the datafile using cp, and rename it using mv (read the manpages!)
Create a temp dir using mktemp -d and copy the data.txt there
bandit12@bandit:/$ mktemp -d
/tmp/tmp.SCAtjsjIci
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ cp ~/data.txt .
The datafile is a hexdump, so we have to convert it back to its original format using xxd
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ xxd -r data.txt > data
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ file data
data: gzip compressed data, was "data2.bin", last modified: Tue Oct 14 09:26:00 2025, max compression, from Unix, original size modulo 2^32 572
The original file is a gzip file, we can now unzip this file to get the source
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ mv data data.gz
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ gzip -d data.gz
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ ls
data data.txt
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ file data
data: bzip2 compressed data, block size = 900k
After unzipping, it gave us a new file which is compressed with bzip2.
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ bzip2 -d data
bzip2: Cant guess original name for data -- using data.out
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ mv data.out data.bz2
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ bzip2 -d data.bz2
bzip2: data.bz2 is not a bzip2 file.
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ ls
data.bz2 data.txt
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ file data.bz2
data.bz2: gzip compressed data, was "data4.bin", last modified: Tue Oct 14 09:26:00 2025, max compression, from Unix, original size modulo 2^32 20480
Uncompressing that will give another gzip file
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ mv data.bz2 data.gz
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ gzip -d data.gz
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ ls
data data.txt
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ file data
data: POSIX tar archive (GNU)
Uncompressing gives us a tar file
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ mv data data.tar
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ tar -xf data.tar
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ ls
data5.bin data.tar data.txt
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ file data5.bin
data5.bin: POSIX tar archive (GNU)
This goes on for a few cycle and in the end we get
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ file data8.bin
data8.bin: gzip compressed data, was "data9.bin", last modified: Tue Oct 14 09:26:00 2025, max compression, from Unix, original size modulo 2^32 49
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ gzip -d data8.bin
gzip: data8.bin: unknown suffix -- ignored
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ mv data8.bin data8.gz
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ gzip -d data8.gz
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ ls
data5.tar data7.tar data8 data.tar data.txt
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ file data8
data8: ASCII text
bandit12@bandit:/tmp/tmp.SCAtjsjIci$ cat data8
The password is FO5dwFsc0cbaIiH0h8J2eUks2vdTDwAn
Level 13
The password for the next level is stored in /etc/bandit_pass/bandit14 and can only be read by user bandit14. For this level, you don’t get the next password, but you get a private SSH key that can be used to log into the next level. Look at the commands that logged you into previous bandit levels, and find out how to use the key for this level.
ssh into the machine, copy the contents from sshkey.private file into a local file. Set appropriate permissions for the file and then ssh into the next level
## THIS IS LOCAL MACHINE - NOT INSIDE SSH
~/dev/bandit ⌚ 21:21:17
$ vi sshkey.private # copy and paste the contents here
~/dev/bandit ⌚ 21:22:19
$ ls
sshkey.private
~/dev/bandit ⌚ 21:22:53
$ chmod 600 sshkey.private
~/dev/bandit ⌚ 21:22:20
$ ssh -i sshkey.private bandit14@bandit.labs.overthewire.org -p 2220
## AFTER LOGGING INTO THE MACHINE - GETTING THE PASSWORD JUST IN CASE
bandit14@bandit:~$ cat /etc/bandit_pass/bandit14
MU4VWeTyJk8ROof1qqmcBPaLh7lDCPvS
Level 14
The password for the next level can be retrieved by submitting the password of the current level to port 30000 on localhost.
bandit14@bandit:~$ nc localhost 30000
MU4VWeTyJk8ROof1qqmcBPaLh7lDCPvS
Correct!
8xCjnmgoKbGLhHFAZlGE5Tmu4M2tKJQo
^C
bandit14@bandit:~$
Level 15
The password for the next level can be retrieved by submitting the password of the current level to port 30001 on localhost using SSL/TLS encryption.
Helpful note: Getting “DONE”, “RENEGOTIATING” or “KEYUPDATE”? Read the “CONNECTED COMMANDS” section in the manpage.
bandit15@bandit:~$ socat stdio OPENSSL:localhost:30001,verify=0
8xCjnmgoKbGLhHFAZlGE5Tmu4M2tKJQo
Correct!
kSkvUpMQ7lBYyCM4GBPvCvT1BfWRy0Dx
bandit15@bandit:~$
Level 16
The credentials for the next level can be retrieved by submitting the password of the current level to a port on localhost in the range 31000 to 32000. First find out which of these ports have a server listening on them. Then find out which of those speak SSL/TLS and which don’t. There is only 1 server that will give the next credentials, the others will simply send back to you whatever you send to it.
bandit16@bandit:~$ nmap -p 31000-32000 localhost
Starting Nmap 7.94SVN ( https://nmap.org ) at 2026-03-06 18:12 UTC
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00036s latency).
Not shown: 996 closed tcp ports (conn-refused)
PORT STATE SERVICE
31046/tcp open unknown
31518/tcp open unknown
31691/tcp open unknown
31790/tcp open unknown
31960/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 0.09 seconds
bandit16@bandit:~$ socatsocat stdio OPENSSL:localhost:30001,verify=0^C
bandit16@bandit:~$ socat stdio OPENSSL:localhost:31046,verify=0
2026/03/06 18:13:23 socat[18] E SSL_connect(): error:0A0000F4:SSL routines::unexpected message
bandit16@bandit:~$ socat stdio OPENSSL:localhost:31518,verify=0
kSkvUpMQ7lBYyCM4GBPvCvT1BfWRy0Dx
kSkvUpMQ7lBYyCM4GBPvCvT1BfWRy0Dx
^Cbandit16@bandit:~$ socat stdio OPENSSL:localhost:31691,verify=0
2026/03/06 18:13:53 socat[20] E SSL_connect(): error:0A0000F4:SSL routines::unexpected message
bandit16@bandit:~$ socat stdio OPENSSL:localhost:31790,verify=0
kSkvUpMQ7lBYyCM4GBPvCvT1BfWRy0Dx
Correct!
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAvmOkuifmMg6HL2YPIOjon6iWfbp7c3jx34YkYWqUH57SUdyJ
imZzeyGC0gtZPGujUSxiJSWI/oTqexh+cAMTSMlOJf7+BrJObArnxd9Y7YT2bRPQ
Ja6Lzb558YW3FZl87ORiO+rW4LCDCNd2lUvLE/GL2GWyuKN0K5iCd5TbtJzEkQTu
DSt2mcNn4rhAL+JFr56o4T6z8WWAW18BR6yGrMq7Q/kALHYW3OekePQAzL0VUYbW
JGTi65CxbCnzc/w4+mqQyvmzpWtMAzJTzAzQxNbkR2MBGySxDLrjg0LWN6sK7wNX
x0YVztz/zbIkPjfkU1jHS+9EbVNj+D1XFOJuaQIDAQABAoIBABagpxpM1aoLWfvD
KHcj10nqcoBc4oE11aFYQwik7xfW+24pRNuDE6SFthOar69jp5RlLwD1NhPx3iBl
J9nOM8OJ0VToum43UOS8YxF8WwhXriYGnc1sskbwpXOUDc9uX4+UESzH22P29ovd
d8WErY0gPxun8pbJLmxkAtWNhpMvfe0050vk9TL5wqbu9AlbssgTcCXkMQnPw9nC
YNN6DDP2lbcBrvgT9YCNL6C+ZKufD52yOQ9qOkwFTEQpjtF4uNtJom+asvlpmS8A
vLY9r60wYSvmZhNqBUrj7lyCtXMIu1kkd4w7F77k+DjHoAXyxcUp1DGL51sOmama
+TOWWgECgYEA8JtPxP0GRJ+IQkX262jM3dEIkza8ky5moIwUqYdsx0NxHgRRhORT
8c8hAuRBb2G82so8vUHk/fur85OEfc9TncnCY2crpoqsghifKLxrLgtT+qDpfZnx
SatLdt8GfQ85yA7hnWWJ2MxF3NaeSDm75Lsm+tBbAiyc9P2jGRNtMSkCgYEAypHd
HCctNi/FwjulhttFx/rHYKhLidZDFYeiE/v45bN4yFm8x7R/b0iE7KaszX+Exdvt
SghaTdcG0Knyw1bpJVyusavPzpaJMjdJ6tcFhVAbAjm7enCIvGCSx+X3l5SiWg0A
R57hJglezIiVjv3aGwHwvlZvtszK6zV6oXFAu0ECgYAbjo46T4hyP5tJi93V5HDi
Ttiek7xRVxUl+iU7rWkGAXFpMLFteQEsRr7PJ/lemmEY5eTDAFMLy9FL2m9oQWCg
R8VdwSk8r9FGLS+9aKcV5PI/WEKlwgXinB3OhYimtiG2Cg5JCqIZFHxD6MjEGOiu
L8ktHMPvodBwNsSBULpG0QKBgBAplTfC1HOnWiMGOU3KPwYWt0O6CdTkmJOmL8Ni
blh9elyZ9FsGxsgtRBXRsqXuz7wtsQAgLHxbdLq/ZJQ7YfzOKU4ZxEnabvXnvWkU
YOdjHdSOoKvDQNWu6ucyLRAWFuISeXw9a/9p7ftpxm0TSgyvmfLF2MIAEwyzRqaM
77pBAoGAMmjmIJdjp+Ez8duyn3ieo36yrttF5NSsJLAbxFpdlc1gvtGCWW+9Cq0b
dxviW8+TFVEBl1O4f7HVm6EpTscdDxU+bCXWkfjuRb7Dy9GOtt9JPsX8MBTakzh3
vBgsyi/sN3RqRBcGU40fOoZyfAMT8s1m/uYv52O6IgeuZ/ujbjY=
-----END RSA PRIVATE KEY-----
bandit16@bandit:~$
Loggedin to level17 with this key
bandit17@bandit:~$ cat /etc/bandit_pass/bandit17
EReVavePLFHtFlFsjn3hyzMlvSuSAcRD
Level 17
There are 2 files in the homedirectory: passwords.old and passwords.new. The password for the next level is in passwords.new and is the only line that has been changed between passwords.old and passwords.new
bandit17@bandit:~$ ls
passwords.new passwords.old
bandit17@bandit:~$ wc -l *
100 passwords.new
100 passwords.old
200 total
bandit17@bandit:~$ diff passwords.new passwords.old
42c42
< x2gLTTjFwMOhQ8oWNbMN362QKxfRqGlO
---
> BMIOFKM7CRSLI97voLp3TD80NAq5exxk
bandit17@bandit:~$
Level 18
The password for the next level is stored in a file readme in the homedirectory. Unfortunately, someone has modified .bashrc to log you out when you log in with SSH.
~/dev/bandit ⌚ 23:55:10
$ ssh -t bandit18@bandit.labs.overthewire.org -p 2220 'sh -l'
_ _ _ _
| |__ __ _ _ __ __| (_) |_
| '_ \ / _` | '_ \ / _` | | __|
| |_) | (_| | | | | (_| | | |_
|_.__/ \__,_|_| |_|\__,_|_|\__|
This is an OverTheWire game server.
More information on http://www.overthewire.org/wargames
backend: gibson-0
bandit18@bandit.labs.overthewire.org's password:
bash: warning: setlocale: LC_ALL: cannot change locale (en_IN.UTF-8)
sh: 1: /etc/profile.d/colon.sh: Syntax error: Bad function name
$ ls
readme
$ cat readme
cGWpMaKXVwDUNgPAVJbWYuGHVn9zl3j8
Level 19
To gain access to the next level, you should use the setuid binary in the homedirectory. Execute it without arguments to find out how to use it. The password for this level can be found in the usual place (/etc/bandit_pass), after you have used the setuid binary.
bandit19@bandit:~$ ls
bandit20-do
bandit19@bandit:~$ ./bandit20-do
Run a command as another user.
Example: ./bandit20-do whoami
bandit19@bandit:~$ ./bandit20-do whoami
bandit20
bandit19@bandit:~$ ./bandit20-do cat /etc/bandit_pass/bandit20
0qXahG8ZjOVMN9Ghs7iOWsCfZyXOUbYO
bandit19@bandit:~$
Level 20
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).
This one is interesting, we would need to create one more session - either a new ssh session or a tmux session. I am going to use a new ssh session. The idea is create a local server and connect to that server from the suid binary. Once the connection is established send the current password from the server and suid binary will send the password as response
Server setup - using a new ssh session
bandit20@bandit:~$ nc -l -v 1234
Listening on 0.0.0.0 1234
Now go the other session and try to connect to this session
bandit20@bandit:~$ ./suconnect 1234
Once the connection establish message is received on server (Connection received on localhost 60686) - send the password from the server (paste and enter in server) - you should receive the new password back
bandit20@bandit:~$ nc -l -v 1234
Listening on 0.0.0.0 1234
Connection received on localhost 60686
0qXahG8ZjOVMN9Ghs7iOWsCfZyXOUbYO
EeoULMCra2q0dSkYj561DX7s1CpBuOBt
bandit20@bandit:~$
Level 21
A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed.
bandit21@bandit:~$ cd /etc/cron.d
bandit21@bandit:/etc/cron.d$ ls
behemoth4_cleanup clean_tmp cronjob_bandit22 cronjob_bandit23 cronjob_bandit24 e2scrub_all leviathan5_cleanup manpage3_resetpw_job otw-tmp-dir sysstat
bandit21@bandit:/etc/cron.d$ cat cronjob_bandit22
@reboot bandit22 /usr/bin/cronjob_bandit22.sh &> /dev/null
* * * * * bandit22 /usr/bin/cronjob_bandit22.sh &> /dev/null
bandit21@bandit:/etc/cron.d$ cat /usr/bin/cronjob_bandit22.sh
#!/bin/bash
chmod 644 /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv
cat /etc/bandit_pass/bandit22 > /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv
bandit21@bandit:/etc/cron.d$ cat /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv
tRae0UfB9v0UzbCdn9cY0gQnds9GF58Q
Level 22
A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed.
bandit22@bandit:~$ cd /etc/cron.d
bandit22@bandit:/etc/cron.d$ ls
behemoth4_cleanup clean_tmp cronjob_bandit22 cronjob_bandit23 cronjob_bandit24 e2scrub_all leviathan5_cleanup manpage3_resetpw_job otw-tmp-dir sysstat
bandit22@bandit:/etc/cron.d$ cat cronjob_bandit23
@reboot bandit23 /usr/bin/cronjob_bandit23.sh &> /dev/null
* * * * * bandit23 /usr/bin/cronjob_bandit23.sh &> /dev/null
bandit22@bandit:/etc/cron.d$ cat /usr/bin/cronjob_bandit23.sh
#!/bin/bash
myname=$(whoami)
mytarget=$(echo I am user $myname | md5sum | cut -d ' ' -f 1)
echo "Copying passwordfile /etc/bandit_pass/$myname to /tmp/$mytarget"
cat /etc/bandit_pass/$myname > /tmp/$mytarget
Lets find the value of $mytarget
bandit22@bandit:/etc/cron.d$ myname=bandit23
bandit22@bandit:/etc/cron.d$ mytarget=$(echo I am user $myname | md5sum | cut -d ' ' -f 1)
bandit22@bandit:/etc/cron.d$ echo $mytarget
8ca319486bfbbc3663ea0fbe81326349
bandit22@bandit:/etc/cron.d$ cat /tmp/8ca319486bfbbc3663ea0fbe81326349
0Zf11ioIjMVN551jX3CmStKLYqjk54Ga
Level 23
A program is running automatically at regular intervals from cron, the time-based job scheduler. Look in /etc/cron.d/ for the configuration and see what command is being executed.
bandit23@bandit:/etc/cron.d$ cat cronjob_bandit24
@reboot bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null
* * * * * bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null
bandit23@bandit:/etc/cron.d$ cat /usr/bin/cronjob_bandit24.sh
#!/bin/bash
shopt -s nullglob
myname=$(whoami)
cd /var/spool/"$myname"/foo || exit
echo "Executing and deleting all scripts in /var/spool/$myname/foo:"
for i in * .*;
do
if [ "$i" != "." ] && [ "$i" != ".." ];
then
echo "Handling $i"
owner="$(stat --format "%U" "./$i")"
if [ "${owner}" = "bandit23" ] && [ -f "$i" ]; then
timeout -s 9 60 "./$i"
fi
rm -rf "./$i"
fi
done
bandit23@bandit:/etc/cron.d$
We need to create a script in /var/spool/bandit24/foo so that it will be executed in all the cron runs
#!/bin/bash
cat /etc/bandit_pass/bandit24 > /tmp/bandit24
Add this script and lets wait for it to get executed
bandit23@bandit:/var/spool/bandit24$ cat /tmp/bandit24
gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8
Level 24
A daemon is listening on port 30002 and will give you the password for bandit25 if given the password for bandit24 and a secret numeric 4-digit pincode. There is no way to retrieve the pincode except by going through all of the 10000 combinations, called brute-forcing. You do not need to create new connections each time
bandit24@bandit:~$ for i in {0000..9999}; do echo "gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8 $i"; done | nc localhost 30002
...
...
...
The password of user bandit25 is iCi86ttT4KSNe1armKiwbQNmB3YJP3q4
Level 25
Logging in to bandit26 from bandit25 should be fairly easy… The shell for user bandit26 is not /bin/bash, but something else. Find out what it is, how it works and how to break out of it.
After logging into bandit25, we can see private key for bandit26 in home dir. After using that to login to bandit26, the connection is closed immediately. To investigate this, based on the hint from the level description, lets check what the default shell is used for bandit 26
$ cat /etc/passwd | grep bandit26
bandit26:x:11026:11026:bandit level 26:/home/bandit26:/usr/bin/showtext
showtext is not a standard shell, lets investigate what that is.
$ cat /usr/bin/showtext
#!/bin/sh
export TERM=linux
more ~/text.txt
exit 0
It is using a cli command more to display a file and exiting after. For more if the screen size is very small instead of cating the out, it will only show a section which also supports command input. So minimize the terminal to a very small size and try sshing again, we should see the connection not yet cut out. Then using the v key, we can toggle vim mode.
Once vim mode is toggled we can open the password file using :e /etc/bandit_pass/bandit26
s0773xxkk0MXfdqOfPRVr9L3jJBUOgCZ
Level 26
Good job getting a shell! Now hurry and grab the password for bandit27!
Use the methods above to get into vim - after that we can use :term to execute commands to shell from vim.
> :term ls
> bandit27-do text.txt
> :term ./bandit27-do
> Run a command as another user.
Example: ./bandit27-do id
> :term ./bandit27-do cat /etc/bandit_pass/bandit27
> upsNCc7vzaRDx6oZC6GiR6ERwe1MowGB
Level 27
There is a git repository at ssh://bandit27-git@bandit.labs.overthewire.org/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. This needs git installed locally on your machine.
In local machine clone the repo by
~/dev/bandit ⌚ 12:43:37
$ git clone ssh://bandit27-git@bandit.labs.overthewire.org:2220/home/bandit27-git/repo
Cloning into 'repo'...
_ _ _ _
| |__ __ _ _ __ __| (_) |_
| '_ \ / _` | '_ \ / _` | | __|
| |_) | (_| | | | | (_| | | |_
|_.__/ \__,_|_| |_|\__,_|_|\__|
This is an OverTheWire game server.
More information on http://www.overthewire.org/wargames
backend: gibson-0
bandit27-git@bandit.labs.overthewire.orgs password:
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
~/dev/bandit ⌚ 12:44:21
Inside the downloaded repo, there is a readme file with the password for next level
~/dev/bandit ⌚ 12:46:09
$ cd repo
~/dev/bandit/repo on master ⌚ 12:46:14
$ ls
README
~/dev/bandit/repo on master ⌚ 12:46:14
$ cat README
The password to the next level is: Yz9IpL0sBcCeuG7m9uQFt8ZNpS4HZRcN
Level 28
There is a git repository at ssh://bandit28-git@bandit.labs.overthewire.org/home/bandit28-git/repo via the port 2220. The password for the user bandit28-git is the same as for the user bandit28.
From your local machine (not the OverTheWire machine!), clone the repository and find the password for the next level. This needs git installed locally on your machine.
# CLONE IT USING SAME STEPS AS BANDIT27
~/dev/bandit/bandit-28 on master ⌚ 13:01:28
$ cat README.md
# Bandit Notes
Some notes for level29 of bandit.
## credentials
- username: bandit29
- password: xxxxxxxxxx
Looks like the password is not there. Lets check the git history
~/dev/bandit/bandit-28 on master ⌚ 13:01:30
$ git log
commit b0354c7be30f500854c5fc971c57e9cbe632fef6 (HEAD -> master, origin/master, origin/HEAD)
Author: Morla Porla <morla@overthewire.org>
Date: Tue Oct 14 09:26:19 2025 +0000
fix info leak
commit d0cf2ab7dd7ebc6075b59102a980155268f0fe8f
Author: Morla Porla <morla@overthewire.org>
Date: Tue Oct 14 09:26:19 2025 +0000
add missing data
commit bd6bc3a57f81518bb2ce63f5816607a754ba730d
Author: Ben Dover <noone@overthewire.org>
Date: Tue Oct 14 09:26:18 2025 +0000
initial commit of README.md
Lets switch to an older commit and check the file
~/dev/bandit/bandit-28 on master ⌚ 13:03:36
$ git switch --detach d0cf2ab7dd7ebc6075b59102a980155268f0fe8f
HEAD is now at d0cf2ab add missing data
~/dev/bandit/bandit-28 on d0cf2ab ⌚ 13:03:40
$ cat README.md
# Bandit Notes
Some notes for level29 of bandit.
## credentials
- username: bandit29
- password: 4pT1t5DENaYuqnqvadYs1oE4QLCdjmJ7
To move back to head - use git switch master
Level 29
There is a git repository at ssh://bandit29-git@bandit.labs.overthewire.org/home/bandit29-git/repo via the port 2220. The password for the user bandit29-git is the same as for the user bandit29.
Cloned the repo and checked out the Readme file
~/dev/bandit/bandit-29 on master ⌚ 13:06:12
$ ls
README.md
~/dev/bandit/bandit-29 on master ⌚ 13:06:13
$ cat README.md
# Bandit Notes
Some notes for bandit30 of bandit.
## credentials
- username: bandit30
- password: <no passwords in production!>
Checked git log and previous commits, nothing was there
With the clue from Readme file, checked to see if there are any branches and found a dev branch
~/dev/bandit/bandit-29 on master ⌚ 13:08:01
$ git branch -a
*master
remotes/origin/HEAD -> origin/master
remotes/origin/dev
remotes/origin/master
remotes/origin/sploits-dev
~/dev/bandit/bandit-29 on master ⌚ 13:08:19
$ git checkout dev
branch 'dev' set up to track 'origin/dev'.
Switched to a new branch 'dev'
~/dev/bandit/bandit-29 on dev ⌚ 13:08:24
$ ls
code README.md
~/dev/bandit/bandit-29 on dev ⌚ 13:08:26
$ cat README.md
# Bandit Notes
Some notes for bandit30 of bandit.
## credentials
- username: bandit30
- password: qp30ex3VLz5MDG1n91YowTv4Q8l7CDZL
Level 30
There is a git repository at ssh://bandit30-git@bandit.labs.overthewire.org/home/bandit30-git/repo via the port 2220. The password for the user bandit30-git is the same as for the user bandit30.
Checked all the previous methods - history and branches, didn’t gave us anything
~/dev/bandit/bandit-30 on master ⌚ 14:53:33
$ cat README.md
just an epmty file... muahaha
~/dev/bandit/bandit-30 on master ⌚ 14:53:40
$ git log
~/dev/bandit/bandit-30 on master ⌚ 14:53:45
$ git branch -a
Checked out if there are any tags
~/dev/bandit/bandit-30 on master ⌚ 14:54:03
$ git tag -l
secret
Lets see whats in that tag
~/dev/bandit/bandit-30 on master ⌚ 14:54:09
$ git checkout secret
fatal: unable to read tree (84368f3a7ee06ac993ed579e34b8bd144afad351)
~/dev/bandit/bandit-30 on master ⌚ 14:54:22
$ git show secret
fb5S2xb7bRyFmAvQYQGEqsbhVyJqhnDy
Level 31
There is a git repository at ssh://bandit31-git@bandit.labs.overthewire.org/home/bandit31-git/repo via the port 2220. The password for the user bandit31-git is the same as for the user bandit31.
~/dev/bandit/bandit-31 on master ⌚ 14:56:59
$ cat README.md
This time your task is to push a file to the remote repository.
Details:
File name: key.txt
Content: 'May I come in?'
Branch: master
~/dev/bandit/bandit-31 on master ⌚ 14:57:01
$ echo 'May I come in?' > key.txt
~/dev/bandit/bandit-31 on master ⌚ 14:57:32
$ cat key.txt
May I come in?
Lets commit and push
$ git add key.txt
The following paths are ignored by one of your .gitignore files:
key.txt
hint: Use -f if you really want to add them.
hint: Disable this message with "git config set advice.addIgnoredFile false"
There is a .gitignore file ignoring the .txt files, lets remove that
~/dev/bandit/bandit-31 on master ⌚ 14:58:52
$ ls -la
drwxr-xr-x ashishkhuraishi staff 192 B Sat Mar 7 14:57:32 2026 .
drwxr-xr-x ashishkhuraishi staff 256 B Sat Mar 7 14:56:48 2026 ..
drwxr-xr-x ashishkhuraishi staff 480 B Sat Mar 7 14:58:52 2026 .git
.rw-r--r-- ashishkhuraishi staff 6 B Sat Mar 7 14:56:55 2026 .gitignore
.rw-r--r-- ashishkhuraishi staff 15 B Sat Mar 7 14:57:32 2026 key.txt
.rw-r--r-- ashishkhuraishi staff 147 B Sat Mar 7 14:56:55 2026 README.md
~/dev/bandit/bandit-31 on master ⌚ 14:59:03
$ cat .gitignore
*.txt
~/dev/bandit/bandit-31 on master ⌚ 14:59:10
$ vi .gitignore
# REMOVE THE *.txt
Now that it is done, lets add the file again and push
~/dev/bandit/bandit-31 on master! ⌚ 14:59:37
$ git add .gitignore
~/dev/bandit/bandit-31 on master! ⌚ 14:59:44
$ git add key.txt
~/dev/bandit/bandit-31 on master! ⌚ 14:59:49
$ git commit -m "Added key.txt"
[master 5f36d12] Added key.txt
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 key.txt
~/dev/bandit/bandit-31 on master ⌚ 14:59:52
$ git push origin master
_ _ _ _
| |__ __ _ _ __ __| (_) |_
| '_ \ / _` | '_ \ / _` | | __|
| |_) | (_| | | | | (_| | | |_
|_.__/ \__,_|_| |_|\__,_|_|\__|
This is an OverTheWire game server.
More information on http://www.overthewire.org/wargames
backend: gibson-0
bandit31-git@bandit.labs.overthewire.org's password:
Enumerating objects: 6, done.
Counting objects: 100% (6/6), done.
Delta compression using up to 14 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 341 bytes | 341.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
remote: /bin/bash: warning: setlocale: LC_ALL: cannot change locale (en_IN.UTF-8)
remote: ### Attempting to validate files... ####
remote:
remote: .oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.
remote:
remote: Well done! Here is the password for the next level:
remote: 3O9RfhqyAlVBEZpVb6LYStshZoqoSx5K
remote:
remote: .oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.oOo.
remote:
To ssh://bandit.labs.overthewire.org:2220/home/bandit31-git/repo
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'ssh://bandit.labs.overthewire.org:2220/home/bandit31-git/repo'
Level 32
After all this git stuff, it’s time for another escape. Good luck!
WELCOME TO THE UPPERCASE SHELL
>> LS
sh: 1: LS: Permission denied
>> HELP
sh: 1: HELP: Permission denied
>> ?
sh: 1: ?: Permission denied
>> man
sh: 1: MAN: Permission denied
>> whomai
sh: 1: WHOMAI: Permission denied
>> echo
sh: 1: ECHO: Permission denied
>> -c "/bin/bash"
sh: 1: -C: Permission denied
>> hi; hi;hh
sh: 1: HI: Permission denied
sh: 1: HI: Permission denied
sh: 1: HH: Permission denied
>> f`whoami`hh
sh: 1: WHOAMI: Permission denied
sh: 1: FHH: Permission denied
>> $0
$ ls
uppershell
$ cat upppershell
cat: upppershell: No such file or directory
$ whoami
bandit33
$ cat /etc/bandit_pass/bandit33
tQdtbs5D5i2vJwkO8mEyYEyTL8izoeJ0
WHY DID IT WORK - https://www.reddit.com/r/hacking/comments/dxe2c2/bandit_level_32_explained_pls_overthewire/
Long story short - giving $0 meant that giving sh as the input
sh -c $0 -> sh -c sh