In last week's tutorial you should have looked at the format of the /etc/shadow file. Let's suppose a Linux or Unix system has been setup which does not have a high level of physical security. Someone can boot this system using their own CDROM, and if the hard disk is not encrypted, using a password to be entered at boot time, an attacker can mount the hard disk using another operating system and obtain a copy of the /etc/shadow file.
How difficult will it be to obtain the plain text passwords by reversing this hash ? We have to assume our attacker has access to a program like the one below, and a spelling dictionary. On Linux the spelling dictionary is typically installed in /usr/share/dict/words .
There are a number of different versions of Linux available. Newer versions use more advanced hashing algorithms e.g. SHA512 with a greater 'salt' added entropy field e.g. 48 bits. An older version might use DES with 12 bits of salt. Depending upon the way in which the hash is presented, (i.e. if the hash field starts with a $ dollar symbol followed by a number) the crypt(3) library function will apply the appropriate hash algorithm.
SYNOPSIS
#define _XOPEN_SOURCE
#include <unistd.h>
char *crypt(const char *key, const char *salt);
In this synopsis, the crypt function is passed 2 parameters, the plaintext password or key, and the salt. If DES is used the salt might look like this string: "a9" (taken from a base64 alphabet (a-zA-Z0-9./), 12 bits of data). If SHA512 is used the salt might look like this string: "$6$R5y75C5J$". The function returns the second colon delimited column which would be present in /etc/shadow for the plaintext password and salt used. (same alphabet, 48 bits of data between the 2nd and 3rd $ delimiters).
Discussion questions: Why does a. using a slower hash function and b. using larger salts reduce the risk that a well chosen password which is easy to remember will be discovered if the hash is obtained by an attacker ?
Here is the source code crack.c for a very simple hash cracking program:
#include <stdio.h>
#include <string.h>
#define _XOPEN_SOURCE
#include <unistd.h>
void chop(char *); /* chops a \n off the end of a word, replaces with \0 */
int main(int argc, char *argv[]){
int i=0;
char word[BUFSIZ], salt[BUFSIZ], pwhash[BUFSIZ];
FILE *words;
words=fopen("/usr/share/dict/words","r"); /* open spelling dictionary */
if(argc != 3){
fprintf(stderr,"crack: usage\ncrack salt hashedpassword\n");
return 2;
}
strcpy(salt,argv[1]);
strcpy(pwhash,argv[2]);
while( fgets(word,BUFSIZ,words) != NULL ){
chop(word);
if(strcmp((char*)crypt(word,salt),pwhash) == 0){ /* guessed the password ? */
printf("the password is: %s\n",word);
return 0;
}
if(strcmp(word,"nothing") == 0 || i%100 == 0)
printf("word: %s\nsalt: %s\npwhash: %s\nhashguess: %s\n",
word, salt, pwhash, (char*)crypt(word,salt));
i++;
}
printf("The password is not in the spelling dictionary.\n");
return 1;
}
void chop(char *word){
int lenword;
lenword=strlen(word);
if( word[lenword-1] == '\n')
word[lenword-1] = '\0';
}
Discussion question: What modifications to the spelling dictionary would be performed for use with a more practical hash cracking program ?
This source code was compiled using the command:
gcc -lcrypt -o crack crack.c
For this compilation to work you may need the Ubuntu gcc package to be installed (recent Ubuntu versions) or the build-essential package (older versions). If your computer has an internet connection, as root:
apt-get install gcc
If you are using Gutsy hosts 1 or 2, instructions showing you how to make installation packages available from a CD image (needed if you havn't got a C compiler) is here.
I executed the program with the following command. note the use of single quotes around parameters 1 and 2 to prevent shell interpretation of the $ dollar symbols:
./crack '$6$R5y75C5J$' '$6$R5y75C5J$nkkqNdbxkqT4tzdsfqNmsV/19ye/GuED.EhTt4BbvqYh43bWt53.gpK7.A5C0QPfexF8VCj6i0Je7tE5Jxtrp.'
Here is the end of the output showing when crack found the password:
word: norm salt: $6$R5y75C5J$ pwhash: $6$R5y75C5J$nkkqNdbxkqT4tzdsfqNmsV/19ye/GuED.EhTt4BbvqYh43bWt53.gpK7.A5C0QPfexF8VCj6i0Je7tE5Jxtrp. hashguess: $6$R5y75C5J$nHxajB06aNa2YchYOgeWsGjk6X0AGn7ZnOOx82vPZ3KHe8HHef7DBAiKX9wQfpqLxAPD9g0CNaZ/Z/24fIOsn/ word: notches salt: $6$R5y75C5J$ pwhash: $6$R5y75C5J$nkkqNdbxkqT4tzdsfqNmsV/19ye/GuED.EhTt4BbvqYh43bWt53.gpK7.A5C0QPfexF8VCj6i0Je7tE5Jxtrp. hashguess: $6$R5y75C5J$ZKaYfqV56E.qT6TnveQGPr/t4uMCaWOU1JrLnB.veaZay8fcY5FfKGcadJQ1jBsNWcuMj73.RGNu.IlrhFIg./ the password is: nothing
Note that the debugging output isn't needed when the correct password is found and that running it took about 75 minutes on a Pentium 4. The command: wc -l /usr/share/dict/words said there were 98569 in my spelling dictionary. Thg command grep -n nothing /usr/share/dict/words showed that the word 'nothing' was the 65514th word searched.
Create a test user, e.g. fred, with a password which occurs early on in the spelling dictionary. Extract fred's hash from /etc/shadow e.g using command as root:
cat /etc/shadow | grep fred | awk -F: '{ print $2 }'
(grep selects the row for fred, awk selects the 2nd colon delimited column).
Obtain a copy of crack.c within your Linux system (check module download area or cut and paste from webpage if your Linux system can browse the web). Compile it and test it to see if it can crack fred's password.
Compare the speed of crack.c when run using DES to how slowly it runs using SHA512 .