├── Implementation Report.pdf ├── LICENSE ├── PDC_Project_Self_Evaluation_Sheet.xlsx ├── Password Cracker using Brute Force Algorithm.pdf ├── Readme.md ├── crack.cpp ├── machinefile ├── pdc ├── pdc.cpp ├── shadow.txt └── utility.cpp /Implementation Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HxnDev/Password-Cracker-using-Brute-Force-Algorithm/6cd3f24b1cf8246091c49635b354cda6f155a41c/Implementation Report.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hassan Shahzad 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PDC_Project_Self_Evaluation_Sheet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HxnDev/Password-Cracker-using-Brute-Force-Algorithm/6cd3f24b1cf8246091c49635b354cda6f155a41c/PDC_Project_Self_Evaluation_Sheet.xlsx -------------------------------------------------------------------------------- /Password Cracker using Brute Force Algorithm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HxnDev/Password-Cracker-using-Brute-Force-Algorithm/6cd3f24b1cf8246091c49635b354cda6f155a41c/Password Cracker using Brute Force Algorithm.pdf -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | All the files have been commented for your ease. Furthermore you may also add further comments if you may. 3 | 4 | This project has equal contributions by [Sana Ali](https://github.com/sanaa-khan), [Abeera Fatima](https://github.com/ninjacarrot) and [Azka Khurram](https://github.com/AzkaKhurram) as this was our group project of Distributed Computing. We used 4 different systems and connected using the same internet connection (as can be seen in the machinefile). 5 | This task was performed on Ubuntu x64. 6 | 7 | 8 | For further queries contact me at : chhxnshah@gmail.com 9 | -------------------------------------------------------------------------------- /crack.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utility.cpp" 8 | 9 | using namespace std; 10 | 11 | bool are_all_Z(string str) { 12 | 13 | // this function is used to check if a string has only zzzzzz (first character excepted) 14 | // done by checking string from 2nd character to the end 15 | 16 | // for the edge case of string having only one character 17 | if (str.length() == 1) { 18 | if (str[0] == 'z') 19 | return true; 20 | return false; 21 | } 22 | 23 | // check that all characters save the starting one are z 24 | for (int i = 1; i < str.length(); i++) { 25 | if (str[i] != 'z') 26 | return false; 27 | } 28 | 29 | return true; 30 | } 31 | 32 | void password_cracker(const char initial, string salt_hash) { 33 | 34 | // initial: the character whose possibilities have to be traversed e.g. a -> a, ab, ac...abbbd, abbbe... till azzzzzzzz 35 | // this function traverses through the character's strings, crypts each using the salt and compares the resulting hash to the salt_hash parameter 36 | // if password is found, function prints result, sends password to master (rank 0 proc) then terminates 37 | 38 | /********************************************************************************************************/ 39 | 40 | bool found = false; // used to keep track of password being found or not 41 | 42 | int current_length = 0; // keep track of which length possibilities are being explored -> 2 characters, 3 characters and so on 43 | const int MAX_LENGTH = 8; // maximum string-length to check possibilities upto 44 | 45 | string initial_char = ""; // string representation of the initial char parameter 46 | initial_char += initial; 47 | string current = initial_char; // initialising the current string with the initial character 48 | 49 | /*********************************************************************************************************/ 50 | 51 | vector tokens = split_string(salt_hash, '$'); // split by dollar sign to split up salt and hash 52 | string salt = "$" + tokens[1] + "$" + tokens[2] + "$"; // get salt and hash 53 | 54 | // run until password is found or max length is exceeded 55 | while (!found and current_length < MAX_LENGTH) { 56 | 57 | // incrementing characters till we reach end e.g. azz or azzzz 58 | while (!are_all_Z(current) and !found) { 59 | 60 | cout << "current: " << current << endl; 61 | 62 | // crypt the current string with constant salt 63 | string crypted = crypt(current.c_str(), salt.c_str()); 64 | 65 | // compare the hash generated to the salt_hash 66 | if (crypted == salt_hash) { 67 | found = true; 68 | cout << "password is " << current << endl; 69 | 70 | // signal master that password has been found (and send the password along) 71 | MPI_Send(current.c_str(), 10, MPI_CHAR, 0, 200, MPI_COMM_WORLD); 72 | break; 73 | } 74 | 75 | else { 76 | bool changed = false; 77 | 78 | // go backwards in the string till a non-z character is found 79 | for (int i = current.length()-1; i >= 0 and !changed; i--) { 80 | // convert any z encountered to a (resetting it) 81 | if (current[i] == 'z') { 82 | current[i] = 'a'; 83 | continue; 84 | } 85 | 86 | // incrementing the first non-z character found from the right 87 | if (current[i] != 'z') { 88 | current[i]++; 89 | 90 | // as we only change one character at a time, so we mark that it has been changed 91 | changed = true; 92 | } 93 | } 94 | } 95 | } 96 | 97 | cout << "current: " << current << endl; 98 | 99 | // this is so we can check for an all-z string e.g. azz, bzzz 100 | // if this was not applied, z-strings would be skipped 101 | string crypted = crypt(current.c_str(), salt.c_str()); 102 | 103 | if (crypted == salt_hash and !found) { 104 | found = true; 105 | cout << "password is " << current << endl; 106 | 107 | // signal master that password has been found (and send the password along) 108 | MPI_Send(current.c_str(), 10, MPI_CHAR, 0, 200, MPI_COMM_WORLD); 109 | 110 | break; 111 | } 112 | 113 | // string length has to added to now 114 | current_length++; 115 | 116 | // resetting the string and incrementing length 117 | current = initial_char; 118 | for (int i = 0; i < current_length; i++) 119 | current += 'a'; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /machinefile: -------------------------------------------------------------------------------- 1 | 192.168.6.11:2 2 | 192.168.6.48:2 3 | 192.168.6.192:2 4 | 192.168.6.13:2 5 | -------------------------------------------------------------------------------- /pdc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HxnDev/Password-Cracker-using-Brute-Force-Algorithm/6cd3f24b1cf8246091c49635b354cda6f155a41c/pdc -------------------------------------------------------------------------------- /pdc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "crack.cpp" 13 | 14 | using namespace std; 15 | 16 | int main(int argc, char** argv) 17 | { 18 | // driver code for the program -> responsible for starting the processes, distributing the alphabet among them, calling on them to search for the password, 19 | // print the password once it is cracked, and then terminate all processes and end program 20 | 21 | int rank, nprocs; 22 | 23 | // MPI initialisation 24 | MPI_Init(&argc, &argv); 25 | MPI_Comm_size(MPI_COMM_WORLD, &nprocs); 26 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 27 | 28 | // master specific code 29 | if (rank == 0) { 30 | cout << "master: there are " << nprocs - 1 << " slave processes" << endl; 31 | 32 | string username = "project"; 33 | 34 | string salt_hash = get_salt_hash("/mirror/shadow.txt", username); // parse file and extract the salt + hash for the specified username 35 | 36 | if (salt_hash.empty()) { 37 | cout << "File was not opened, terminating program." << endl; 38 | MPI_Abort(MPI_COMM_WORLD, 0); // terminates all MPI processes associated with the mentioned communicator (return value 0) 39 | exit(0); // just in case :) 40 | } 41 | 42 | map distrib = divide_alphabet(nprocs-1); // getting the alphabet division per process (including master, if required) 43 | 44 | // send salt_hash and the letters assigned to that process to every slave 45 | for (int i = 1; i < nprocs; i++) { 46 | MPI_Send(salt_hash.c_str(), 200, MPI_CHAR, i, 100, MPI_COMM_WORLD); // send the salt + hash string to every slave 47 | MPI_Send(distrib[i].c_str(), 30, MPI_CHAR, i, 101, MPI_COMM_WORLD); // send every slave their allocated characters 48 | } 49 | 50 | // some characters have been allotted to master 51 | if (distrib[0].length() > 0) { 52 | string letters = distrib[0]; 53 | cout << "master: " << letters << endl; 54 | 55 | // master needs two threads running in parallel here 56 | // one to search through its own assigned characters 57 | // one to wait in case any slave cracks the password and reports to master 58 | #pragma omp parallel num_threads(2) 59 | { 60 | // 1st thread: search through the permuations of it's letters 61 | if (omp_get_thread_num() == 0) { 62 | // go through its assigned characters -> for every character, call the cracking function on it 63 | for (int i = 0; i < letters.length(); i++) { 64 | password_cracker(letters[i], salt_hash); 65 | } 66 | } 67 | 68 | // 2nd thread: wait for slave to find the answer instead 69 | // note: even if master finds the password, it also sends a signal back to itself (and recieves it here) 70 | else { 71 | char password[10]; 72 | 73 | MPI_Status status; 74 | MPI_Recv(password, 10, MPI_CHAR, MPI_ANY_SOURCE, 200, MPI_COMM_WORLD, &status); // blocking recieve waiting for any process to report 75 | 76 | cout << "Process " << status.MPI_SOURCE << " has cracked the password: " << password << endl; 77 | cout << "Terminating all processes" << endl; 78 | 79 | MPI_Abort(MPI_COMM_WORLD, 0); // terminates all MPI processes associated with the mentioned communicator (return value 0) 80 | // this is not a graceful way of exiting but will suffice for the functionality we require 81 | } 82 | } 83 | } 84 | 85 | // in this case, the characters have been divided equally among the slaves 86 | // master has nothing to do except wait for a slave to report password found 87 | else { 88 | char password[10]; 89 | 90 | MPI_Status status; 91 | MPI_Recv(password, 10, MPI_CHAR, MPI_ANY_SOURCE, 200, MPI_COMM_WORLD, &status); // blocking recieve waiting for any process to report 92 | 93 | cout << "Process " << status.MPI_SOURCE << " has cracked the password: " << password << endl; 94 | cout << "Terminating all processes" << endl; 95 | 96 | MPI_Abort(MPI_COMM_WORLD, 0); // terminates all MPI processes associated with the mentioned communicator (return value 0) 97 | // this is not a graceful way of exiting but will suffice for the functionality we require 98 | } 99 | } 100 | 101 | // slave specific code 102 | else { 103 | char salt_hash[200], letters[30]; 104 | 105 | MPI_Recv(salt_hash, 200, MPI_CHAR, 0, 100, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // recieve salt_hash (same for every slave) 106 | MPI_Recv(letters, 200, MPI_CHAR, 0, 101, MPI_COMM_WORLD, MPI_STATUS_IGNORE); // recieve allotted characters (different for every slave) 107 | 108 | cout << "slave " << rank << ": " << letters << endl; 109 | 110 | sleep(2); // this is just to ensure all slaves print their allotted characters then start execution (for neatness :) ) 111 | 112 | // go through its assigned characters -> for every character, call the cracking function on it 113 | for (int i = 0; i < charToString(letters).length(); i++) { 114 | password_cracker(letters[i], charToString(salt_hash)); 115 | } 116 | } 117 | 118 | MPI_Finalize(); 119 | 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /shadow.txt: -------------------------------------------------------------------------------- 1 | root:!:18792:0:99999:7::: 2 | daemon:*:18667:0:99999:7::: 3 | bin:*:18667:0:99999:7::: 4 | sys:*:18667:0:99999:7::: 5 | sync:*:18667:0:99999:7::: 6 | games:*:18667:0:99999:7::: 7 | man:*:18667:0:99999:7::: 8 | lp:*:18667:0:99999:7::: 9 | mail:*:18667:0:99999:7::: 10 | news:*:18667:0:99999:7::: 11 | uucp:*:18667:0:99999:7::: 12 | proxy:*:18667:0:99999:7::: 13 | www-data:*:18667:0:99999:7::: 14 | backup:*:18667:0:99999:7::: 15 | list:*:18667:0:99999:7::: 16 | irc:*:18667:0:99999:7::: 17 | gnats:*:18667:0:99999:7::: 18 | nobody:*:18667:0:99999:7::: 19 | systemd-network:*:18667:0:99999:7::: 20 | systemd-resolve:*:18667:0:99999:7::: 21 | systemd-timesync:*:18667:0:99999:7::: 22 | messagebus:*:18667:0:99999:7::: 23 | syslog:*:18667:0:99999:7::: 24 | _apt:*:18667:0:99999:7::: 25 | tss:*:18667:0:99999:7::: 26 | uuidd:*:18667:0:99999:7::: 27 | tcpdump:*:18667:0:99999:7::: 28 | avahi-autoipd:*:18667:0:99999:7::: 29 | usbmux:*:18667:0:99999:7::: 30 | rtkit:*:18667:0:99999:7::: 31 | dnsmasq:*:18667:0:99999:7::: 32 | cups-pk-helper:*:18667:0:99999:7::: 33 | speech-dispatcher:!:18667:0:99999:7::: 34 | avahi:*:18667:0:99999:7::: 35 | kernoops:*:18667:0:99999:7::: 36 | saned:*:18667:0:99999:7::: 37 | nm-openvpn:*:18667:0:99999:7::: 38 | hplip:*:18667:0:99999:7::: 39 | whoopsie:*:18667:0:99999:7::: 40 | colord:*:18667:0:99999:7::: 41 | geoclue:*:18667:0:99999:7::: 42 | pulse:*:18667:0:99999:7::: 43 | gnome-initial-setup:*:18667:0:99999:7::: 44 | gdm:*:18667:0:99999:7::: 45 | sana:$6$ip3hT96.mMPfUtfe$TpLiJCxk49Pc/YFHWoDIg.V8IHzPgtsvAvQZPUrvLCUMERzkXv0Tt9dS9ZKEilJ.RR5BsuqJguORiIhSSWfSI1:18792:0:99999:7::: 46 | systemd-coredump:!!:18793:::::: 47 | _rpc:*:18793:0:99999:7::: 48 | statd:*:18793:0:99999:7::: 49 | sshd:*:18793:0:99999:7::: 50 | mpiuser:$6$Y18JkyIFFj9QHYBr$DeXEzMMCwdK9Gx1gokJak7m4j5kMaNuETMbA4JOm/2UdJvIdG7l56Y.WJ05LO5Y/1GJHzO7HmQ.IzAwCubeTQ1:18793:0:99999:7::: 51 | project:$6$upnvGygoSdNlnDEh$6PIw9UPJ5dHAGt/OXZEVxiqvsVRs0YQTStSbtcuUVR6ihluTjn21z6c4vKqQ6c7iPRAs.0h1BtHsM7Xk1eT9m1:18794:0:99999:7::: 52 | -------------------------------------------------------------------------------- /utility.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using std::map; 10 | using std::ios; 11 | using std::cout; 12 | using std::fstream; 13 | using std::string; 14 | using std::vector; 15 | using std::stringstream; 16 | 17 | template 18 | void print_map(map to_print) { 19 | // a templatised function to output a map's key value pairs 20 | 21 | for (auto itr = to_print.begin(); itr != to_print.end(); ++itr) { 22 | cout << itr->first << ": " << itr->second << "\n"; 23 | } 24 | } 25 | 26 | string charToString(char arr[]) { 27 | // function that returns a string representation of a character array 28 | 29 | string str = ""; 30 | 31 | for (int i = 0; arr[i] != '\0'; i++) 32 | str += arr[i]; 33 | 34 | return str; 35 | } 36 | 37 | vector split_string(const string str, const char delim) { 38 | 39 | // this is a custom function to split a string 40 | // returns a vector of "words", according to the delimiter char 41 | 42 | string token; 43 | vector out; 44 | stringstream ss(str); 45 | 46 | while (getline(ss, token, delim)) // splitting or tokenizing the string according to the specified delimiter 47 | out.push_back(token); // adding each token to the vector 48 | 49 | return out; 50 | } 51 | 52 | string get_salt_hash(const string file, const string username) { 53 | 54 | // this function reads the file (typically shadow.txt) and finds the value corresponding to the specified username 55 | // the value is then parsed to get the salt and hash (as one string) for this username 56 | // salt and hash are returned as one string 57 | 58 | string line, salt_hash; 59 | 60 | fstream infile; 61 | infile.open(file, ios::in); 62 | 63 | if (infile.is_open()) { 64 | while (getline(infile, line)) { 65 | if (strstr(line.c_str(), username.c_str())) { // if line has specified username 66 | vector tokens = split_string(line, ':'); // split by " : " character 67 | salt_hash = tokens[1]; // salt and hash are after first colon and before second 68 | break; 69 | } 70 | } 71 | 72 | return salt_hash; 73 | } 74 | 75 | else { 76 | cout << "Shadow file not opened :(\n"; 77 | return ""; 78 | } 79 | } 80 | 81 | map divide_alphabet(const int slave_procs) { 82 | 83 | // this function divides the alphabet characters and assigns certain number of characters to each process 84 | // also takes into account the case when alphabet is not perfectly divisible by number of slave processes 85 | // so may also assign master some characters 86 | // the distribution is stored in a map with (key, value) pair being (process rank, characters assigned) 87 | // rank 0 is master, rank 1 is slave 1, rank 2 is slave 2 and so on... 88 | 89 | map distrib; 90 | string alphabet = "abcdefghijklmnopqrstuvwxyz"; 91 | random_shuffle(alphabet.begin(), alphabet.end()); 92 | 93 | // case 1: alphabet perfectly divisible by number of slaves 94 | if (alphabet.length() % slave_procs == 0) { 95 | int chunk = alphabet.length() / slave_procs; 96 | 97 | distrib[0] = ""; // no letters assigned to master (characters can be evenly distributed among the slaves), still we keep a record for master in case required 98 | 99 | int index = 0; 100 | for (int i = 1; i <= slave_procs; i++) { 101 | int start = index; 102 | 103 | string letters = alphabet.substr(start, chunk); 104 | 105 | distrib[i] = letters; // assigning letters to that slave rank 106 | 107 | index += chunk; 108 | } 109 | 110 | } 111 | 112 | // case 2: alphabet not perfectly divisible, some characters will have to be allotted to master too 113 | else { 114 | int mod = alphabet.length() % slave_procs; 115 | 116 | distrib[0] = alphabet.substr(0,mod); 117 | 118 | int new_len = alphabet.length()-mod; 119 | int mod1 = new_len/slave_procs; 120 | 121 | int index = mod; 122 | 123 | for (int i = 1 ; i <= slave_procs; i++) { 124 | int start = index; 125 | 126 | string letters = alphabet.substr(start, mod1); 127 | 128 | distrib[i] = letters; 129 | 130 | index+=mod1; 131 | } 132 | } 133 | 134 | return distrib; 135 | } 136 | --------------------------------------------------------------------------------