far in this course we've been talking about collecting passwords
from compromised systems. And so there's a couple of different ways to do that and the results that we collect can vary from one system to another and based off of where we're grabbing this information from. So in some cases we might get legitimate passwords for a system, in others we might just get password hashes. And so in this video, going to look at cracking Linux password hashes and so we'll talk through the format of what you might grab if you pull a password hash from let's say shadow and then how to crack it using python. So our code here is shown in the note pad plus plus across the top of the screen. And so for this we're going to be using the passlib library. And so the reason for this is that the crypt library that you might use on your Linux system only works on Linux systems. So for running on say Windows, we need to use something designed for Windows like passlib. So just for more universal usage. So we're going to pull in passlib and then from passlib.hash we're going to import the sha512 hash function as 512. And so, the reason why we're using sha512 is that's what was default on some Linux systems. We'll talk through how to determine which hash function to use in just a moment. And so here, down at the bottom if you've looked at C shadow, you should have seen something that looks a little bit like this. So user two sample password hashes pulled from a boon two system for user one and user too. And so reading through this format, let's break this down. So we have our user name here. So user one and user two, separated with the colon from what we really want which is the stuff that's between this next pair of colons. And so this tells us everything we need to know about cracking this particular Linux password hash. So the first thing we have here is number 6, in this case this tells us that our hash algorithm is sha512. And so up here in our crack hash function we see that we do a test for the algorithm and see if it's a value of 6, and that's why we imported sha512 up here. So in different cases you might have different values here and there are look up tables online that will tell you for this particular algorithm value, this is the algorithm that you should be using. So this section is delimited by dollar signs, and so our next value of interest is right here. And so this is called our password salt. So if you're not familiar with cryptography and password hashing, a salt is a random but public value that's mixed in with the password before hashing it. And so the reason why we use salts is that hash functions are deterministic. You put the same input in, you're always going to get the same input out. And so some people found this out, and there decided well we know that there are some common passwords or we know there's only so many potential inputs to this hash function. Let's just make up a look up table called a rainbow table for this particular hash function saying, this is what we put in, and this is what comes out. And so that way instead of going through all the bother of trying to crack a password hash, if we steal a hash we can just compare it to this look up table and immediately get the password. So we're doing more work up front to speed things up later on. Salts break this because a salt is a unique part of the input to each password. Notice that the first salt and the second salt here are different, and so if you want to generate a rainbow table for a modern password database, you're going to have to generate a rainbow table for every combination of salt and password and that quickly becomes infeasible. And so that helps to protect against rainbow tables. And so here we're going to need to provide this salt value when we're hashing our password because it's part of the password hash. Our third value here is the output of the hash function. And so notice that neither the salt nor this look much like a random string of bits, reason why is everything is encoded to make it printable and easier to view. So see here, we'll decode this and the salt when we pass them into the hash function and that will give us the lengths that we're expecting. So I shouldn't have said that this is going into the hash function, this actually is what our output is. So we'll be testing against this, because one of the properties of hash functions that makes them useful for password management is that they're one way. You can just put an input at and get an output out but you can't go from output to input. The best way to crack a hash or get a password from a hash is to try all the possible passwords until you get a correct output. So if you've got a nice strong password, that brute force search isn't going to work and so nobody can get your password. In this case, in this demonstration, we're going to assume that people used stupid passwords. So we're going to try some variations on the word password using some of the password munging and capabilities that we talked about in an earlier video. So things like substituting in a different character for a letter. So something like an art symbol or a number 4 instead of a lowercase or uppercase. And so our crack hash function here is going to do all the heavy lifting of cracking are Linux passwords for us. We're going to pass in h which is our output here, the salt value which we've grabbed from here, our algorithm which is right here, and then a list of passwords to try. And so in this case we're just assuming that the algorithm is 6, because that's the one that we're using here and all that changes is which hash function we're going to be using. And so we're using 512, you could also use MD5, sha256 etcetera. So it's important to look up which one is used on the system or check this. And so once we've chosen our algorithm we're going to loop over our list of passwords and generate a hash for that particular password. So we'll call sha512, tell to hash will give it the password that we're looking at the salt value that we've extracted from here in a number of rounds. So in this case system is using 5000 rounds of the hash and the whole point of those rounds is to make it harder to perform password cracking. So around is just in the first round will take the password, the hash that produced output, the second round will take the output of that previous one, and hash it to produce the output of round two. Since hash functions are unpredictable, the only way to get from the initial password to the final output is to go through that 5000 hash operations. And so if you only need to do it once because you're legitimate user and pass again the right password that 5000 hashes doesn't take that long. But if like us, you need to test a lot of passwords, having to do 5000 hashes rather than a single hash starts adding up time, and so it makes cracking a little bit more difficult. And so as a result of this we're going to get something very similar to what we have here, this dollar sign with the algorithm, the salt and the hash output. So we're going to split that on our dollar signs, and if the hash output that I've got highlighted here, matches the one that we've generated on a particular password, then we're going to return that password because that's the password for that particular user account. And so down here in our main function, we've generated all of our variations of passwords using those character substitution. And we're going to loop over these two hashes that we've extracted from the machine. We'll split them on the colons, so that we can get this section here that we care about, will store the user name which is the first value that we pull out from vowels here. And then we'll reset vowels to be each of the three values we get here, the algorithm, the salt and the hash output. Well then call crash, crack crash with those three values and the password list that we get from genVariations and if we get a result, that means that we found a hash that matches our hash so we'll have that password. And so we'll print out the user name and the associated password. And so now let's give this a run. So we'll use python crack Linux.py, execute it and so this is going to take a couple of seconds to run. And the reason why is first we need to take this password and generate every single variation of it using substitution. It's going to create a few 1000 passwords after that were then running this crack hash function. So for a few 1000 passwords, for each one of them we need to run 5000 rounds of hashing to get the final hash output that will be comparing. And so we see here that were successful in both cases. So both of these user accounts used a variation of password as their password. First one used just the word password and the 2nd 1 used character substitution. So we have a capital P, a art symbol couple of dollar signs, and a 0 but were able to determine the correct passwords for those user accounts using this python code. And so just important to note here that our success comes down to the fact that we had a pretty good guess for what the passwords for those accounts were. And essentially, we were just looking at simple password munging of a single base password. If we had more passwords to test and munge, the list of passwords is going to go up significantly, so does the amount of time. If we're actually looking at account where someone used a strong password, then the password storage system works as intended, we're not going to be able to guess it. Say if you've got like a 12 character, completely random password using upper case, lower case numbers, symbols, etc, then this isn't going to work. But if we can get a good guess on what the password might be, maybe from performing some of that password analysis we talked about in an earlier video, we've determined that this is how a user build their passwords. This is their basic algorithm. Then we could pass in the results of that analysis into this to see which one is the right one. Thank you.
The Advanced Roblox Coding Book: An Unofficial Guide, Updated Edition: Learn How to Script Games, Code Objects and Settings, and Create Your Own World!