Course work 1, brute force
Introduction
Welcome to the documentation of brute_cambell, here you'll find out how to use the tool for cracking binaries.
There are with three distinct files:
- Basic
- Intermediate
- Hard
Each one uses their own custom algorithm for cracking of which I will go into details later.
For basic pin based passwords with a fixed length of 3, basic can be used.
Intermediate reads from a dictinary, and converts it to eilite speak (thats is words like, cool becoming c00l), adding 1-9 to the end, and using the regular password against the selected file.
Lastly, hard will be using what's known as a timmed attack (explained in the algorythims part in more detailed), of from a password of unknown length.
User guide
Start
Start by setting up a python3 enviorment, (this will be useful for using test later)
python3 -m venv venv
. ./venv/bin/activate
pip install -r requirements.txt
If pip fails, try running as sudo:
sudo pip install -r requirements.txt
To the neccisary binaries we want to crack, it will populate the targets folder,
./download.sh
Basic
Starting with, brute_basic.py. This will crack any basic file that you select.
To launch the script, run the following commands
python3 src/brute_basic.py
Note, cd into the src directory will break the code
You now have access to the brute forcing options,
Slecting an option not on the list will then close the script, and you would need to rerun.
Selecting the option you would want, will then compute the three didgit pin and return result as shown bellow:
Testing the file out by./targets/basic3
Upon this prompt enter the above guess, and:
Success
Intermediate
Going back to the src directory, we can now launch the intermediate brute forcer. This cracks passwords using a dictinary, then adding numbers 0-9 to the end of the word and will convert the plain words to elliet speak (test t35t) Running:
python3 src/brute_basic.py
Will give you access to,
Selecting an input that doesnt excist closes the code, However selecting 2 will start the cracking for intermediate3,where we get the output below.
Testing the guess against the file, we get:
Success
Advanced
This script takes advantage of the time differnce for correct characters, to incorrect ones.
To start, run the following command:
python3 src/brute_basic.py
Upon running the script youre introduced to the menue, as seen bellow.
Upon running the target you'd like to bruteforce, the script will start attempting to crack. Bellow shows the sucessful outcome of 0, Running the file, with the password shown result in the password being correct.
Success
Unit Tests
To start testing, run the command bellow:
pytest -v ./tests/
test_basic.py
Function | Test | Expected Result |
---|---|---|
test_unsuccessful() | Run against a file it cant crack | None |
test_guessGenerator() | Length of the reurn | 10000 |
intermediate_basic.py
Function | Test | Expected Result |
---|---|---|
test_unsuccessful() | Run against a file it cant crack | None |
test_blankdict() | Run against a file it can crack with an empty dictionary | None |
test_correctcrack() | Run against a file it can crack with a correct dictionary | String |
Algorithms
In this program, I needed to develop three Algorithms:
- Creating a list of three pins
- Creating a list of passwords via dictionary manlipulation
- Cracking a password via timming attack
Bellow is where I will go into further details on each task:
Creating a list of three pins,
By cycling through 0-1000, we generate all the codes that's required.
Then by checking for the numbers less then 10, we add 00 to the start, less than 100 we then add 0 to the start. Thus we get all three didgit passcodes.
With this generated list, we can now attack the binary with each nermircal pin!
(though I would prefer a function within three while loops where it would return once you get a code which is more efficent in python)
note by now I have ran out of time to develop for the advance module, though there is some error detection code within it.
Creating a list of passwords via dictionary manlipulation,
Firstly, we generate the list of words from the text file. Cycling through, 1-9 we add this to the end of each word, then assign it to a copy of the orginal list. We then take each word in the original list of words, splitting them into character. Then with each character we compair it to the eilite speak equivent then. Once the character loop is complete we have a new word that then gets added to that previous temp list. We coninue to loop untill all words in the dictionary is converted then attack the binary.
Source code
brute_basic.py
#!python3
from brutus import Binary
# This is a starting-point for your project
# Most of the work is done for the basic task
# Intermediate will require reading from a dictionary file and creating guesses based on the contents
# For the advanced task, see the supporting module "brute_time"
def generateGuesses():
""" Creates a list of PIN guesses. Needs finishing, testing and documenting... """
#Here, the guesses are listed explicitly. We *could* write out
#1000 guesses, but the point is to use the computer to do the
#brute force work, not our fingers
#For the basic task, the only functionality you need is to replace
#the line below with code that can generate all the potential
#guesses
output=[]
for x in range(1000):
if x< 10:
#Checks for digits 1-9
output.append("00"+str(x))
elif x< 100:
#checks for diget 10-99
output.append("0"+str(x))
else:
#three digits now, so no need for further checks
output.append(str(x))
return output
def breakBinary(target, promptText, failText):
"""" Break into the given target binary.
Assumes "basic" level binary, with PIN codes of 000-999
Args:
target: path to the binary. e.g. "./bins/basic1"
promptText: text to look for in the output that signals a password is required. e.g. "Password:"
failText: text that indicates an attempt failed. e.g. "Password Incorrect"
Returns:
None: if no successful attempt was made
string: a successful password"""
guesses=generateGuesses()
for g in guesses:
#The actual attempt
b=Binary(target)
b.run()
success=b.attempt(promptText,g, failText)
if success:
print(f"The Guess '{g}' appears to be correct")
return g #Return the answer. No need to "break" because the return exits the function
else:
pass
#rint(f"guess: {g} - Password incorrect!")
return None #If we get here, it means we didn't return earlier in the loop with a successful guess
if __name__=="__main__":
# Create a simple menu system to pick the binary we want to force
targets=[]
targets.append(["targets/basic1","Password:", "Password Incorrect"])
targets.append(["targets/basic2","Enter the secret code:", "ACCESS DENIED"])
targets.append(["targets/basic3","Got the PIN?", "NO"])
print("Basic Binary Breaker")
print("Which binary do you want to brute force?")
for c in range(len(targets)):
print(f"{c}: {targets[c][0]}")
selection=int(input("Enter the number of the binary to be forced: "))
if 0 <= selection < len(targets):
target=targets[selection]
breakBinary(target[0],target[1],target[2])
else:
print("Invalid selection")
brute_intermediate.py
#!python3
from brutus import Binary
def wordsFromFile(filePath):
""" Read lines from a file containing one word per line and return a list of the words
Args:
filePath: the absolute or relative path of the file to be read
Returns:
a list of the words from the file, stripped of whitespace
"""
f=open(filePath,"r")
out=[]
for l in f.readlines():
w=l.strip()
if len(w)>0:
out.append(w.lower())
f.close()
return out
def breakBinary(target, promptText, failText, guesses):
"""" Break into the given target binary.
Assumes "intermeduate level binary, with dictionary words
Args:
target: path to the binary. e.g. "./bins/basic1"
promptText: text to look for in the output that signals a password is required. e.g. "Password:"
failText: text that indicates an attempt failed. e.g. "Password Incorrect"
guesses: list of words to try as passwords
Returns:
None: if no successful attempt was made
string: a successful password"""
for g in guesses:
#The actual attempt
b=Binary(target)
b.run()
success=b.attempt(promptText,g, failText)
if success:
print(f"The Guess '{g}' appears to be correct")
return g #Return the answer. No need to "break" because the return exits the function
else:
pass
#print(f"guess: {g} - Password incorrect!")
return None #If we get here, it means we didn't return earlier in the loop with a successful guess
if __name__=="__main__":
#Load the dictionary
words=wordsFromFile("dictionaries/base.txt")
### YOUR CODE HERE
### Currently it passes in the plain words
### Change the line "words2=words" so that the list "words2" contains your guesses
### You need to create a word list that has the dictionary words in PLUS
### 1. Each word with all 0-9 digits appended (so 'swordfish' would be 'swordfish0', 'swordfish1' etc.
### 2. Each word turned into "l33t-5p34k"
### Each o becomes 0, each i becomes 1, each a becomes 4, each s becomes 5, each e becomes 3
### 'swordfish' becomes '5w0rdf15h', for example
### You can assume case (upper/lower) will not need to be changed
tempWord =[]
for word in words:
#Gets each word from disctionary and makes it into a string
for x in range(10):
#Counts zero to nine
tempWord.append(word+str(x))
#then add that number to the end
tester = list(word)
#splits the word into characters
newWord = ""
#Creates a tempory string
for character in tester:
#Cycles through each letter, then converts to leet speak
#would have prefered switch case, but you know. Python.
if(character == 'e'):
newWord+="3"
elif(character == 'o'):
newWord +="0"
elif(character == 's'):
newWord +="5"
elif(character == 'i'):
newWord += "1"
elif(character =='a'):
newWord += "4"
else:
newWord+=str(character)
#Adds leet speak onto the end of the new list
tempWord.append(newWord)
words2=words+tempWord
#Adds new dictionary to the old one
# Create a simple menu system to pick the binary we want to force
targets=[]
targets.append(["targets/intermediate1","Password:", "Password Incorrect"])
targets.append(["targets/intermediate2","Secret code:", "Auth Failure"])
targets.append(["targets/intermediate3","Enter Credentials:", "Invalid Credentials"])
print("Intermediate Binary Breaker")
print("Which binary do you want to brute force?")
for c in range(len(targets)):
print(f"{c}: {targets[c][0]}")
selection=int(input("Enter the number of the binary to be forced: "))
if 0 <= selection < len(targets):
target=targets[selection]
breakBinary(target[0],target[1],target[2], words2)
else:
print("Invalid selection")
test_intermediate.py
import pytest
import sys
sys.path.append("./src/")
import brute_intermediate
def test_blankDict():
test= brute_intermediate.best_intermediate.py("./test/testbin","", "",[""])
assert (isinstance(test, str) == False)
brute_advance.py
#!python3
from brutus import Binary
def maxPos(seq):
""" Given a list of numbers, return the **position** of the largest
Args:
seq: the list to be searched
Returns:
an integer position of the largest number in `seq`
"""
maxNum=seq[0]
maxPos=0
for i in range(len(seq)):
if seq[i]>maxNum:
maxNum=seq[i]
maxPos=i
return maxPos
# def averageTry(target, promptText, failText, guess, repeats=2):
# # Provided to assist. If you use it, document it properly... :)
# # Runs multiple attempts at cracking the binary, returning the
# # success AND the average length of time each try took
# results=[]
# success=False
# for i in range(repeats):
# b=Binary(target)
# b.run()
# result=b.timedAttempt(promptText,guess, failText)
# success=result[0]
# results.append(result[1])
# return (success,sum(results)/len(results))
def breakBinary(target, promptText, failText):
#Your code here
# Suggested algorithm:
#1. Use an accumulator for the current guess
#2. in a loop, try the current guess plus each letter of the alphabest and see which one takes longest
#3. If it is the correct password, end
#4. If not, add the current best letter to the guess and repeat...
accumulator =0
adverageTime=[]
foundPass = False
guess=""
numberOfLoops =0
while foundPass == False:
for currentCharacter in range(97, 123):
for trials in range(4):
temp=guess+str(chr(currentCharacter))
b=Binary(target)
b.run()
result=b.timedAttempt(promptText,temp, failText)
foundPass= result[0]
if (not foundPass):
if(numberOfLoops <20):
adverageTime.append(result[1])
else:
print("Password not found")
return False
else:
print("Guessing the password is " +temp)
return True
guess+=str(chr(96+sortTime(adverageTime)))
adverageTime=[]
return None
def sortTime(someData):
if(len(someData) == 104):
currentLow=0.0
foundIndex=0
adverageTimePerChar=[]
for index in range(104):
#26*4+1 ammount of charaters in some data
if (index+1) %4 == 0:
totalAdv = (someData[index]+someData[index-1]+someData[index-2]+someData[index-3])/4
pythonFunkyness= currentLow- totalAdv
if(currentLow <totalAdv):
currentLow=totalAdv
foundIndex=int((index+1)/4)
return foundIndex
#Gets the index of the fastest character
else:
print(len(someData))
return -1
if __name__=="__main__":
# Create a simple menu system to pick the binary we want to force
targets=[]
targets.append(["./targets/advanced1","Password:", "Password Incorrect"])
targets.append(["./targets/advanced2","Password:", "Password Incorrect"])
targets.append(["./targets/advanced3","Password:", "Password Incorrect"])
print("Intermediate Binary Breaker")
print("Which binary do you want to brute force?")
for c in range(len(targets)):
print(f"{c}: {targets[c][0]}")
selection=int(input("Enter the number of the binary to be forced: "))
if 0 <= selection < len(targets):
target=targets[selection]
breakBinary(target[0],target[1],target[2])
else:
print("Invalid selection")
test_basic.py
import pytest
import sys
sys.path.append("./src/")
#You will need to write tests for your own functions, or change tests for ones you modify
import brute_basic
def test_unsuccessful():
test =(brute_basic.breakBinary("./test/testbin","",""))
assert (isinstance(test, str) == False)
def test_guessGenerator():
#This definitely needs changing and expanding
assert len(brute_basic.generateGuesses())==1000