YCP Logo Assignment 2: Password Cracking, Part II

Due: Tuesday, Feb 8th Wednesday, Feb 9th by 11:59 PM

Updated 1/25/11 - Corrected a typo.

Updated 2/2/11 - Filled in Grading, Deliverables sections.

Updated 2/9/11 - One day extension due to cluster head node crash.

Getting Started

Download CS365_Assign2.zip. Extract the contents of the archive into a directory.

Using a Unix shell, use the cd command to navigate into the directory containing the extracted contents (CS365_Assign2).

Copy the crack_passwd.c source file from your CS365_Assign1 folder. Assuming that you extracted the assignments in the same parent folder, you can run the command

cp ../CS365_Assign1/crack_passwd.c .

Your Task

Your task is to implement a parallel dictionary password cracking program, using MPI to handle the task of running the program on multiple computers/cores in parallel.

You can use the runpar script to execute your program in parallel. The script takes two arguments: the number of processes to create, and a string containing the encrypted password entry you would like to crack.

Example session (user input in bold):

dhovemey@slartibartfast$ ./runpar 20 '$1$KZGJpy5g$ImO7p8gqmNaY27DFC8rqD0'
Algorithm       : 1
Salt            : KZGJpy5g
Encrypted passwd: ImO7p8gqmNaY27DFC8rqD0
Cracked! Password is cathode6

Dictionary Cracking

Dictionary cracking is an approach to password cracking (figuring out plaintext passwords based on their encrypted form). The idea is to try encrypting words from a dictionary, along with variations of those words. If the encrypted form of a dictionary word (or variation) matches an encrypted password, then we learn the plaintext password.

Example: let's say that $1$KZGJpy5g$ImO7p8gqmNaY27DFC8rqD0 is an encrypted password entry. We know from Assignment 1 that the encryption algorithm is 1 (which happens to mean MD5), the salt value is KZGJpy5g, and the encrypted form of the salted plaintext password is ImO7p8gqmNaY27DFC8rqD0.

In this case, the password is not a strong one: it happens to be an English word followed by a single digit. A password cracking program that has access to a list of English words can mount an attack on the encrypted password as follows. For each word in the dictionary:

  • Enumerate a list of variations of the word, including the original word and some variations (such as adding a digit to the end.) For example, if the dictionary word is benign, then some variations we might try are benign0, benign1, 4benign.
  • For each variation, encrypt it, using the same salt value that was used for the encrypted password we are trying to crack.
  • Compare the resulting encrypted password entry to the original one. If it matches exactly, we know that the variation is the plaintext password.

Using this approach, we find that the string cathode6 is the plaintext password which generated the encrypted password entry.

Implementation Details

Each node in the computing cluster has a publically-readable file named

/usr/local/data/wordfile.txt

This is a list of exactly 499,189 English words, one per line.

You should implement your program so that each parallel process is responsible for trying a subset of the words in this file. For example, if your program is run with 20 parallel processes, then each one should try about 25,000 words (including variations).

You can use the crypt_r library function to encrypt your candidate password strings. Let's say that you have a candidate password string in a character array called candidate, and also that entry is a character array containing the original encrypted password entry (e.g., $1$KZGJpy5g$...). You can compute the encrypted form of the candidate password as follows:

struct crypt_data data;
data.initialized = 0;
const char *result = crypt_r(candidate, entry, &data);

[Note that we are taking advantage of the fact that the original encrypted password entry can be used verbatim to specify the algorithm and salt values we want to use.]

You can determine if the candidate is the original password by comparing the the result of the encryption to the original entry. If they are the same, you now know the plaintext!

if (strcmp(result, entry) == 0) {
        // Success!  The candidate is the plaintext password.

To report a successfully discovered password, the process which found it should simply print it using printf. E.g.:

printf("Cracked! Password is %s\n", candidate);

You do not need to use any MPI communication mechanisms to convey the cracked password between processes.

Hints

Each process should determine how many other processes are running, and also its own rank. In your main function, add

MPI_Init(&argc, &argv);

int rank, size;

MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

Important: make sure that you call MPI_Init before the code that retrieves the encrypted password entry from the argv array.

The variable size will contain the number of processes, and the variable rank will contain an integer value in the range 0..size-1. Based on these two values, each process should assign itself a range of words in the dictionary file to try.

Don't forget to call MPI_Finalize() before the program exits.

The fgets function is a good way to read lines of text from a file.

Grading

  • Dividing work (subsets of words) over multiple processors: 40%
  • Reading dictionary to find words: 15%
  • Generating word variants (as described above): 15%
  • Encrypting candidate words and variants, comparing to encrypted entry, printing match: 30%
  • Extra credit of up to 10%: trying other variants

Deliverables

There are two deliverables for the assignment:

  • the source code
  • a text document

The text document should contain the decrypted plaintext of as many of the following encrypted password entries as you can crack:

$1$dLGSZ.e1$GjuidDnG6sTwgqMEcD1Dt/
$1$UaAgbCip$WKTgLOUx96yvdXrq/ch.c1
$1$xpaV4gis$4TDRY/X1I1sjwN4PETmaF1
$1$8mVedDRC$WKbTM.OmlGztJ0avhbIpK/
$1$2vKkWfoI$pyk3woJ46Yl3Izz2sIe/d1
$1$tB8FXWw2$WnZ9TuBPgQFNr2893ZRxu.
$1$5SUSqMIT$EqsgG.7f7tLW./kWw3HaW/
$1$iKegyC0c$XtX5phu2i6TTN3M6twlfZ/

All of these entries are based on dictionary words, but some of them are not the kinds of variations described above.

Please do not share the results of your program with anyone else in the class.

Submitting

From the directory containing your source files and your text document, run the command

make submit

When prompted, enter your Marmoset username and password. You should see a message indicating that the submission was successfully uploaded to the server.

Important: You should log into the server and download your submitted files. Check to make sure that the files you submitted were the ones you intended. The server URL is

https://camel.ycp.edu:8443