Developers who create sign-in systems know better than storing passwords in plain text. Typically, they store hashes of a password to prevent the credentials from being stored as a hacker might steal. Due to the way the hashes work, not all are the same. Some are more vulnerable than others, and a little bit of python could be used to brutally force weak hashes to get the passwords that created them.
Hackers often steal entire databases of user login and password data, and for this reason, hashes are the preferred method for storing sensitive information such as passwords.
Hashes are different from encryption because they do not store data. Instead, the number that forms a hash is the result of a calculation that runs on whatever you hash, be it a password or an entire file. This is used to make sure that the downloaded file corresponds to the file you want to download, or to confirm the password that the user has entered with the password with which he logged in.
Depending on the size of the file or password re-hashing, hashes such as SHA-1 or MD5 will take fixed blocks of the data you havehed and perform a complex calculation block by block until a final one Value is reached. This value is a very long number that is unique, so it can be checked by comparing the hash values to see if one file matches another. If the hash value is different, something has been changed to the file.
That's great, because if the user enters a different password than the one he chose, the hash value will be completely different. For this reason, the developer only has to save the hash, because at any time the user has to log in, he can simply enter the password to create a new hash that is compared to the stored one.
As an example, I used hashes nullbyte for the following SHA-1 value. You can create your own SHA-1 hash on sha1-online.com to see for yourself what it looks like.
A problem with SHA-1 hashes
Unfortunately, not all hashes for developers created the same for saving passwords. For hashes like SHA-1, there are a few issues that make saving passwords with SHA-1 a less than ideal solution.
To point out, every time you use the same word with SHA-1, it's the exact same hash. Although this is intentional, you can just take a large number of guesses and hash them all in SHA-1 and then quickly compare the hashes to get the password from which the SHA-1 hash was derived. Since SHA-1 is designed to be fast, this process takes a very short time, making it even easier to run brute force.
There are some solutions to this, and one of the most popular is adding a salt. A salt is a string that you can add to the password before you have it. An example would be adding the word salt to the password nullbyte . While we know the SHA-1 value of nullbyte from above, the hash of nullbytesalt or saltnullbyte would be completely different. That helps, but if the salt is not per user, then finding the salt is not difficult and you're back to the same problem.
A better solution is to add a random salt, and there is a hash algorithm created for storing passwords with just that purpose.
Bcrypt is not only slow to thwart brute-forcing, but it also adds a random salt to each hash. Therefore, no two bcrypt hashes are identical, even if they consist of the exact same password. To validate an estimate against a bcrypt hash, you must instead use a bcrypt function that uses the password estimate and hash as an argument and returns the result of whether they match or not.
To show how these different hashes work I've written Python to turn any password into a SHA-1, MD5 and bcrypt hash.
hashlib, import bcrypt # Displays the difference between two hashing types, SHA1 and Bcrypt password = input ("Enter the password in the hash n>") Print (" nSHA1: n") for i within reach (3): setpass = bytes (password, & utmp-8 & # 39;) hash_object = hashlib.sha1 (setpass) rate_pw = hash_object.hexdigest () print (guess_pw) Print (" nMD5: n") for i within reach (3): setpass = bytes (password, & utmp-8 & # 39;) hash_object = hashlib.md5 (setpass) rate_pw = hash_object.hexdigest () print (guess_pw) Print (" nBCRYPT: n") for i within reach (3): hashed = bcrypt.hashpw (setpass, bcrypt.gensalt (10))
As you can see below, the MD5 and SHA-1 hashes are the same, but the bcrypt hashes change with each generation. For developers, bcrypt is clearly the better choice. But if we pass a SHA-1 or MD5 hash password database, how could we actually enforce the hash command?
/ user / skickar / venv / untitled10 / bin / python / user / skickar / desktop / TestSHA1 .py Enter the password as a hash > nullbyte SHA1: 32c0ced56f1fe08583bdb079d85a35a81995018c 32c0ced56f1fe08583bdb079d85a35a81995018c 32c0ced56f1fe08583bdb079d85a35a81995018c MD5: 5f804b61f8dcf70044ad8c1385e946a8 5f804b61f8dcf70044ad8c1385e946a8 5f804b61f8dcf70044ad8c1385e946a8 bcrypt: b $ 2b $ 10 $ Z1WVDUi50fmqyrpw19rIyOLPIKVUFeh7HO0FfQi1MbKjyxyduG2WS & # 39; b $ 2b $ 10 $ F.vehMYSUh / 6zmTR / VY2quTnPfzPDcIdHTfZpb8twqjRIIIEFcbUW & # 39; b $ 2b $ 10 $ pZyptPPDHrnIgpU7wTW2nu4cfGAUS65kcGZb6FMC7KmYwJmuwSoLO
Building a Brute-Force Python3 SHA-1
Part of growing up as a hacker is learning to write your own tools. In the beginning, your tools will be simple and solve small problems, but as you gain experience, you will be able to achieve more and more. When you start, programming languages like C ++ that are heavily typed may be difficult for beginners to understand, but Python3 is a flexible and beginner-friendly language that lets us abstract ideas and make prototypes easy.
The simple program We are writing today to show how a hacker creates a vulnerability exploit tool. In this example, SHA-1 is vulnerable to brute-forcing because you can compare two hashes, so we write a program to do just that.
To write a program, you must write out the steps that your program must follow in order to be successful. This list may seem a bit long, but it can be compressed and you should be as specific as possible for things to work to get the output you want. I prefer whiteboards or online flow chart makers like MindMupp to draw how these programs should flow from start to finish.
Once you've created your steps, you can jump to pseudocode, where you set the steps in readable order but closer to the way the code is actually expressed. With this pseudocode, you can fill in your code line by line, correct mistakes, and watch every step of your program take shape and interact with each other.
What you need Continue
To follow this guide, you need a computer with Python3 to work on. Python3 has some differences from the previous version of Python. So you should be sure to get the right version. You can install Python3 in several ways. On Linux, you can type the following to install Python3:
apt install python3
You need a Python3 IDE (integrated development environment). These are programs that help you to write, test and experiment with your code. In particular, I recommend PyCharm from Jetbrains. In addition, the Professional Edition is available to students free of charge, which proves to be absolutely worthy of your support.
For everything to work properly, some libraries need to be imported. We will use the urllib urlopen and hashlib libraries for this code to open files from a remote URL and hash password estimates in SHA -1. To enclose them, create a new Python3 file in your IDE and put the following in the first line:
from urllib.request import urlopen, hashlib
This will import the needed libraries to the rest of the program Has access to these libraries. If you need to install one of these libraries on your computer to run this script, you can usually do so with pip install and then the name of the library you need.
To help you continue, you can download the Python programs I wrote for this example. To do this, open a terminal window and enter the following three commands to download the scripts, go to their directory, and list the files in them.
git clone https://gitlab.com/skickar/SHA1cracker cd SHA1cracker ls
For the first command we need to get the hash that we want to crack by the user. For this we can use the function input which gives the user a prompt and allows him to enter a response.
In Python we can store this answer in a variable, and Python is not like C ++, because you have to declare everything at the beginning. We can simply generate variables to store data we just want.
We call our variable sha1hash because we will store a SHA-1 hash in it. We can just type this in to create the variable, and then we need to assign the user's answer to fill that variable. In Python, the symbol equals ( = ) does not mean it compares anything to see if it's the same. This actually happens with two equal signs ( == ). The equal sign is more a command in Python, the variable on the left is assigned to the data on the right side of the equals sign.
We will assign whatever the user types in, so we call input function, which also allows us to put the text that appears to the user in two parentheses. To tell Python that we want to print a string or collection of characters, we also include everything we put in quotation marks. The end result should look like this:
sha1hash = input ("Please enter the hash to crack. N>")
When we do this, a prompt will appear with the message "Please enter the hash, to crack. "Then we see a" new line "symbol, which is a backslash ( ) and a n . This means jumping to a new line. Lastly, I set a > icon so that the user can type in his answer to a new line. When we execute the NBspecial.py file, the result looks like this:
Dell: SHA1cracker skickar $ Dell: SHA1cracker skickar $ python3 NBspecial.py Please enter the hash to crack > _
Once the user has entered a hash value, it is stored in the variable sha1hash for later use in the program.
Step 2: Opening a file full of password estimates
We would like to open a list of many common passwords. We will use a list of the 10,000 most common passwords for our example, which is a plain text file hosted on GitHub. You can use other lists, such as online leaked passwords or those created with the Mentalist or Crunch.
For the file we use, we assign it to a variable again, this time ] LIST_OF_COMMON_PASSWORDS . To open the file, we use a function called urlopen that allows us to easily open this text file and tell Python the correct encoding type. Use the following format:
urlopen (& # 39; TheURLYouWantToOpen & # 39;). Read ()
This opens the quoted URL using method read which means we want to read text from the file. To make sure the function str () knows what it's working with, we'll put a command after this function and & utf-8 & # 39; to tell the program that UTF is being used -8 text encoding
We will save the data again as a string, and to avoid problems with that, we can ensure that the data we put into the variable Write, a string, by "casting" first. it to a string. This means trying to change the data to another type, and it can be done to convert integers to strings, strings to bytes, and any other desired data type. To do this, we enter str () and then paste the data we want to convert into parentheses into a string. The end result should look like this:
LIST_OF_COMMON_PASSWORDS = str (open URL (& # 39; https: //raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list -top- 10000.txt & # 39;) .read (), & utmp-8 & # 39;)
In this line, we open the text file selected by a remote URL and encode it as UTF-8 Text file and save this data to a string named LIST_OF_COMMON_PASSWORDS.
Now we have to solve an interesting problem. Although we know there are 10,000 passwords in the text file, the program has no idea how many to expect. Therefore, we need to create a code that runs once for each estimate in the password file.
I'm using a structure called for loop. A for loop is a very basic term in programming and looks like this:
for [the variable that guess is in] in [the variable that guess is in]: [do this]
What that means is that for the number of Guesses In the variable we created to store all the assumptions in the last step (in this case, 10,000), we will do the following. In practice, this means we take an estimate from the list of guesses, do some action, and then jump back to find the next guess until we're out of new guesses.
We can name the variable that holds every guess, whatever we want, but for the sake of clarity I've named it rate . It would be just as good to say for x in LIST_OF_COMMON_PASSWORDS as well.
The last problem we need to solve is to tell the program how to divide the big long list of passwords into individual passwords. The password list we use separates passwords with a new line, so we can use the new row block to split LIST_OF_COMMON_PASSWORDS into individual guesses.
To put this into action, we can attach .split () to the end of the [LIST_OF_COMMON_PASSWORDS variable and paste the code for a new line ( & # 39; n & # 39; ) in parentheses. The end result looks like this:
for rate in LIST_OF_COMMON_PASSWORDS.split (& # 39; n & # 39;):
This code will record a password at the end of the line from the previously created LIST_OF_COMMON_PASSWORDS variable stops. It will run as many times as there are passwords unless we tell it to behave differently in the next steps.
We need to create a new variable to store a hash version of the password estimate we pulled from the list. If we do this, it should generate an identical hash if we use the same password that was used to create the hash provided by the user in the first step. If it matches in the next step, we know that we have found the password.
We rename the variable to hold the hash version of the Council hashedGuess . Next, we need to make some preparations before we can grab the installments we pulled from the password list. To use the string variable we called guess in a byte object. This is necessary because the SHA-1 function only works on byte objects and not on strings.
Fortunately, it's easy to convert a string to bytes. We can do this the same way we converted the user input to a string in the first step. The formula looks like this. In this case, we will convert rate to bytes, and the text encoding is UTF-8.
byte (StringToTurnIntoBytes, & # 39; EncodingOfString & # 39;)
Now we have the byte version of rate we can turn it into a SHA-1 hash using the following code:
hashlib.sha1 (BytesToHash) .hexdigest ()
So, what does it do? We call the SHA-1 hash from the hashblib function and hash the byte variable we put in the parentheses. Because of the way SHA-1 works, we could continue to add material, but to print the current value of the SHA-1 hash, we'll add .hexidigest () .
In The Last Code, we assign the value of the hash council to the variable HashedGuess .
hashedGuess = hashlib.sha1 (byte (rate, ut utf-8))). hexdigest ()  Now that we've saved the password estimate as a hash, we can compare that estimate to the original SHA-1 hash to crack directly.
In this step we need to tell the program what to do if the hash matches. For this we use a simple statement that if statement.
At if statement calls something like a for statement, but a condition to see if it is true before the next part of the code is executed. If the condition is true, you can instruct the program to perform an action, and if it is wrong, perform another action instead.
The general formula for a if statement in Python is as follows.
if [some condition to check is true]: [do whatever this code says to do]
For our use case, we want to determine if the hash rate matches the original hash value given by the user, so we can use the character == to determine if they are the same. The statement we want to evaluate is whether HashesGuess corresponds to sha1hash the variable in which we retain the original hash. That's a simple statement in our code.
if hashedGuess == sha1hash:
Now that we have made this comparison, we need to explain to the program what to do in three cases that we expect: a match, no match, or no more passwords in the list
In this step we explain what to do if the hashes match or do not match. Since the previous instruction asks what to do when these two are the same, our first instruction is what to do if the hash of the password estimate matches the original password.
If this is the case, we have found the password, and the right thing to do is to print out the correct password and exit the program. If we do not quit, the loop continues even though we found the password that matches the SHA-1 hash. To do this, type the following:
print ("The password is", str (rate))
This will print everything in the quotation marks, and then guess the string version of the current password that was successfully run. It is important that we print the variable rate and not the variable hashedGuess since the version hashedGuess will give us only one more SHA-1 hash. In this case, this variable is also converted to a string, so Python can easily output it without error. After this is printed, we simply include quit () to close the program, because we have the password!
If hashedGuess and sha1hash variables do not match, we must explain what to do. We can supplement this part of the statement with a elif statement. Elif or "else if" tells the program what to do if another condition applies. Our next statement in this case is as follows:
hashedGuess! = Sha1hash
This statement asks if the two variables are not equal, what with the symbol ! = is shown. If this is the case, or if the two hash values are unequal and the password estimate is incorrect, we must tell the user that the guess has failed, and then return to the beginning of the loop to capture a new password
we do the same thing as before and just use the function print () to print a message. In this message we will say: "Password guess", [guess] "does not fit, try next ..." . The final result should look like this:
print ("The password is", str (rate)) Leaving() elif hashedGuess! = sha1hash: print ("password guess", str (rate), "does not fit, keep trying ...")
This code explains what to do if the guess is correct and what to do if the guess is not But what if we can not find a game at all? Instead of simply stopping, we can give the user more information when we find that we have exhausted our list of passwords and there are no further guesses.
If we go through this loop and find no matches, the loop is terminated because there is nothing further to get from the list of password estimates , We'll let the user know that we were not successful, rather than abruptly terminating the program by placing a printout just outside the loop. If the password is found, the last function print will never be executed because the quit () function that we had previously added terminates the program if we get the correct password , 19659004] How do we put that statement outside the loop? In Python, spaces are important, so we can put them in a new line and just not indent them, as shown in the following example.
for rate in LIST_OF_COMMON_PASSWORDS.split (& # 39; n & # 39;): hashedGuess = hashlib.sha1 (byte (rate, ut utf-8 #)). hex digest () if hashedGuess == sha1hash: print ("The password is", str (guess)) Leaving() elif hashedGuess! = sha1hash: print ("guess password", str (guess), "does not fit, keep trying ...") print ("Password not in the database, we'll get it next time.")
Python would first run the loop for then evaluate the if and elif statements, and only if the loop ended would execute the final pressure function, since it lies outside the loop for .
This print function is simple and contains no variables, just a string to let the user know that we did not find a matching password in the list.
print ("Password not in the database, we'll get it next time.")  With this last line we have a fully functional SHA-1 brute-forcing program, so let's do it! First, we are asked to crack the SHA-1 hash. I give it the hash cbfdac6008f9cab4083784cbd1874f76618d2a97 to test it.
Dell: SHA1cracker skickar $ python3 NBspecial.py Please enter the hash to crack. > cbfdac6008f9cab4083784cbd1874f76618d2a97
After you hit Return the script begins to work.
Password rate 171717 does not match, keep trying ... Password guess panzer does not match, keep trying ... Password rate lincoln does not match, keep trying ... Password rate Katana does not fit, try it ... Password guess Firebird does not fit, keep trying ... Password rate blizzard does not match, keep trying ... Guess password a1b2c3d4 does not match, keep trying ... Password white does not match, keep trying ... Password guess Sterling does not match, keep trying ... Password guess redhead does not fit, keep trying ... The password is password123 Dell: SHA1cracker skickar $ _
And that's how we found the password that was used to create a hash that allowed us to reverse the "one way" SHA-1 hash.
With a bit of Python3 knowledge, we could write a simple script to find the password from which a hash was derived in just 11 lines. You can see the entire code without comments below. With some clever formatting of our Python, we can make it more compact (but much harder to read or understand) and do it all with just three lines of code.
from urllib.request import urlopen, hashlib sha1hash = input ("Please enter the hash to crack. n>") LIST_OF_COMMON_PASSWORDS = str (Open URL (https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-10000.txt') .read (), & # 39; utf-8 & # 39;) to guess in LIST_OF_COMMON_PASSWORDS.split (& # 39; s): hashedGuess = hashlib.sha1 (byte (rate, ut utf-8 #)). hex digest () if hashedGuess == sha1hash: print ("The password is", str (guess)) Leaving() elif hashedGuess! = sha1hash: print ("guess password", str (guess), "does not fit, keep trying ...") print ("Password not in the database, we'll get it next time.")
This is possible by cracking the hash in the same line we use for importing libraries and the for and if statements in a line with something called a ternary operator. In general, the format for these is the following and can be added as long as necessary.
if else if
The following format is used in our script:
if else if else  After applying these changes we can summarize our code as in the following example:
from urllib.request import urlopen, hashlib; Origin = input ("Enter SHA1 hash to crack n>") for the password in str (urlopen (& # 39; https: //raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-10000.txt' ) .read (), & utmp-8 & # 39;). split (& # 39;): [print("The password is ", str(password)), quit()] if (hashlib.sha1 (bytes (password, utf-8)). Hexdigest ()) == origin else print ("password not in the database, we'll get it next time. ") If password = =" "else print (" password guess ", str (password)," does not fit, keep trying ... ")
While this is new to someone new to Python, without deciphering comments , kann Python aus verdichtet werden eine grobe Idee zu ein paar prägnanten Codezeilen, einfach indem man durch das Programm arbeitet und nach Abkürzungen sucht.
Wenn man das auf eine Zeile bringen wollte, könnte man es einfach in eine exec () Funktion und fügen Sie eine neue Zeile ( n) Zeichen für jeden neuen Zeilenumbruch hinzu. Warum du das tust, bin ich mir nicht sicher, aber es ist nützlich, Programme bei Bedarf komprimieren zu können.
Ich hoffe, dir hat dieser Anfänger-Leitfaden gefallen, deinen eigenen SHA-1 Brute-Forcer in Python zu schreiben! Wenn Sie Fragen zu diesem Tutorial oder grundlegenden Python3-Programmierung haben, zögern Sie nicht, einen Kommentar zu hinterlassen oder erreichen Sie mich auf Twitter @KodyKinzie .
Nicht verpassen: Alle Null Bytes Passwort -Cracking Guides