Custom Attacks Part 3:

Elsaid Salem
7 min readJan 20, 2021
Photo by Dmitry Ratushny on Unsplash

Welcome back to the 19th iteration of this blog series. In this series, we’re growing our cybersecurity knowledge starting from the very basics using the overthewire.org challenges as a guide. First, I’d like to thank everyone for their feedback based on the last post! I’ll do my best to implement it and as always, more feedback is always welcome.

In this post, we’ll continue the process of writing our own custom program to crack passwords. We’ll take it in steps and I’ll do my best to explain the choices we’ll make. We’ll also address some issues and discuss some security concepts that relate to programming and password cracking. Let’s get started!

We last left off with the following code:

# send password to binary using subprocess# we don't need ALL of subprocess, so to keep our program efficient we'll just import the part that we need:
from subprocess import check_output as checkoutput
# this will take a password and the "path" to send to and do the magic
def shellGuesser(guess, path):
attempt = "echo " + guess + " | " + path
return checkoutput(attempt, shell=TRUE)
# be able to test the password guessing and command sending
def testGuesser(guess):
gotIt = ""
if guess == "testPassword":
gotIt = "Found the password! It's " + guess
else:
gotIt = "Whoops! Try again...."
return gotIt
# iterate over password possibilities without using nested "for loops"
def iterator():
# be able to keep guessing longer passwords if original length doesn't work
# make it interactive

We left off with a question: How should we iterate over possibilities but without using “for loops”?

Let’s just treat any possibility as a number. It lends itself to letting us use lists that use indexes (numbers) to iterate through them. We start that by creating a few lists to use in the program; this is called initializing variables:

# iterate over password possibilities without using nest "for loops"
guessCharacters = []

To make sure that our program doesn’t run into errors, we should initialize any and all variables before they’re required for use. To keep it simple and keep it easy to read for others, it’s good to keep the initialized variables close to the functions that need them. Next, we need to create a function that will make use of those variables:

# iterate over password possibilities without using nested "for loops"
guessCharacters = []
def parameterGenerator():
pass

What does “pass” do? It’s a simple placeholder so that the function is not “empty” and throws errors in our IDE. We’re just telling the IDE “do nothing with this, I’ll fix it later”. We have to step back here and create a list of the characters that we’ll use for the program to use. Let’s start simple and just use numbers (again initialize variables before they’re necessary):

# iterate over password possibilities without using nested "for loops"
guessCharacters = []
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
def parameterGenerator():
pass

Great! We have our first list of password possibilities. Now we have to make our function use that. We’re going to break our rule here for a bit and we’ll come back to see how we can fix it later:

# iterate over password possibilities without using nested "for loops"
guessCharacters = []
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
def parameterGenerator():
for number in numbers:
guessCharacters.append(number)

Now our function will generate another list that also has the same exact members as our “numbers” list. Why didn’t we just set it so:

guessCharacters = numbers

Programming languages have two main types of variables: basic and reference. A variable creates a memory address and stores that address under the variable name. When it comes to a basic type variable, the variable (even though it knows the address) stores the value stored at the address location. A reference type variable, on the other hand, stores the actual memory address instead of the value. In python, lists are reference types and if we set “guessCharacters = numbers”, what we’re really doing here is just setting guessCharacters to equal the memory address of the list numbers. In that way, if we change either list, both lists will change.

Given that our “numbers” list is a master list for us to work from but not actually change, we can instead make that a tuple. Tuples are another form of lists but they’re immutable; meaning that they’re unchangeable. Once an immutable variable is initialized, it can no longer be changed from within the program execution. Make it a tuple:

# iterate over password possibilities without using nested "for loops"
guessCharacters = []
numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
def parameterGenerator():
for number in numbers:
guessCharacters.append(number)

Great! Now this is coming together nicely, but so far we’re only guessing numbers. Let’s expand it a bit:

# iterate over password possibilities without using nested "for loops"
guessCharacters = []
numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
upper = ("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z")
lower = ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z")
def parameterGenerator():
for number in numbers:
guessCharacters.append(number)

That’s a nice selection of options that cover most passwords. But now our function is still only working with numbers. We need to give the function some parameters so it can know what to add from. A parameter is defining an input for a function. The function then uses this input to do something. In this case, we want to give the function some options from which to create another list of character possibilities:

# iterate over password possibilities without using nested "for loops"
guessCharacters = []
numbers = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
upper = ("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z")
lower = ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z")
def parameterGenerator(possibilities):
for number in numbers:
guessCharacters.append(number)

So far so good but our function still doesn’t know what to do with “possibilities”. We’ll rename the function now to make it more appropriate to what to does:

def possibilityGenerator(possibilities):
for possibility in possibilities:
guessCharacters.append(possibilities)

Great! Now we can do something like “parameterGenerator(upper)” and it’ll fille our “guessCharacters” list with the upper case letters. We can run the function multiple times like this to get all of our desired character possibilities.

We’re doing great! Our functions are very simple and do one thing and they do it well. We can also put our tuples of possible characters into a dictionary and use that as a way to make it interactive later:

passwordCharacters = { \
"upper" : ("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"), \
"lower" : ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"), \
"numbers" : (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) \
}

Notice the backslashes? For python (and most languages), the backslash represents an escape character. In this case, we’re escaping the “carriage return” character (an invisible character) that lets the computer know to write the next data on a new line. We can also just write it into one super long line but it becomes difficult to read. We have to update our function to use the dictionary:

def possibilityGenerator(possibilities):
for possibility in passwordCharacters[possibilities]:
guessCharacters.append(possibilities)

This will now lookup the appropriate tuple from the dictionary and populate our list of characters to guess with. Now we need a function to let us create the password attempts. We need to pass the attempt as a string to the command line so we try using a string. But strings, much like tuples, are immutable. So we’ll just create another list and use that to create a string later:

guess = []def guessLength(length):
for i in range(0, length):
guess.append(0)

Why 0? Since we’re going to be using indexes, it makes sense to use 0 as the placeholder. We can then increment the numbers at each member of the “guess” list and eventually make a string to use as a guess. Let’s make that function:

def guessMaker(guessList):
guessWord = ""
for character in guessList:
guessWord += guessCharacters[character]
return guessWord

Awesome! This is really coming along well. We now have the majority of the necessary functions that we need to make our program work. So far, the section we’ve been working on should look like:

# iterate over password possibilities without using nested "for loops"
guessCharacters = []
passwordCharacters = { \
"upper" : ("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"), \
"lower" : ("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"), \
"numbers" : (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) \
}
def possibilityGenerator(possibilities):
for possibility in passwordCharacters[possibilities]:
guessCharacters.append(possibilities)
guess = []def guessLength(length):
for i in range(0, length):
guess.append(0)
def guessMaker(guessList):
guessWord = ""
for character in guessList:
guessWord += guessCharacters[character]
return guessWord

Next week we should be able to finish up the rest of the program and start our testing process. If you were following along up till now, see if you can go ahead and finish it yourself. Feel free to change the functions, variable names, and types, and whatever else you’d like to make it work with your method. Also, why not go ahead and add a new tuple of symbol characters?

--

--