0

I'm trying to crack a password, given its hash and a dictionary of words. For this, I'm basically doing an exhaustive search, of all the words in the dictionary.

#include <iostream>
#include <list>
#include <cstring>
#include <crypt.h>
//this is an example line from the shadow file:

//tom:$6$9kfonWC7$gzqmM9xD7V3zzZDo.3Fb5mAdM0GbIR2DYTtjYpcGkXVWatTC0pa/XVvKTXLb1ZP0NG9cinGRZF7gPLdhJsHDM/:16471:0:99999:7:::

// the salt and password values are extracted as

std::string target_salt = "$6$9kfonWC7$";
std::string target_pw_hash =  "$6$9kfonWC7$gzqmM9xD7V3zzZDo.3Fb5mAdM0GbIR2DYTtjYpcGkXVWatTC0pa/XVvKTXLb1ZP0NG9cinGRZF7gPLdhJsHDM/";

// define a null string which is returned in case of failure to find the password
char null[] = {'\0'};

// define the maximum length for the password to be searched
#define MAX_LEN 5

std::list<char**> pwlist;
// check if the pw and salt are matching the hash

int check_password(char* pw, char* salt, char* hash)
{
    char* res = crypt(pw, salt);
    
    std::cout << "password " << pw << "\n";
    std::cout << "hashes to " << res << "\n";

    std::cout << "hash: " << hash << "\n";
    std::cout << "hash: " << res << "\n";

    std::cout << strlen(hash) << std::endl;
    if(strlen(hash) == strlen(res))
        std::cout << "equal" << std::endl;

    for (int i = 0; i < strlen(hash); i++) {
        if (res[i] != hash[i]) // the problem is here
            return 0;
        //std::cout << hash[i] << " ";
    }
    std::cout << "match !!!" << "\n";
    return 1;
}
// builds passwords from the given character set
// and verifies if they match the target

char* exhaustive_search(char** wordset, char* salt, char* target)
{
    char** current_password = (char**)malloc(MAX_LEN*sizeof(char *));
    char** new_password;
    int i, current_len;

    // begin by adding each character as a potential 1 character password

    for (i = 0; i < sizeof(wordset)/sizeof(wordset[0]); i++){
        new_password = (char **)malloc(2*sizeof(char*));
        new_password[0] = wordset[i];
        new_password[1] = 0;
        pwlist.push_back(new_password);
    }

    while(true){
    // test if queue is not empty and return null if so
    
        if (pwlist.empty()) 
            return null;
    
    // get the current current_password from queue
    
        current_password = pwlist.front();
        current_len = sizeof(current_password)/sizeof(current_password[0]);

        //std::cout << *current_password << " " << current_len << std::endl;
        // break;
    // check if current password is the target password, if yes return the current_password
        if (check_password(*current_password, salt, target))
        {
            return *current_password;
        }

    // else generates new passwords from the current one by appending each character from the charlist
    // only if the current length is less than the maxlength

        if(current_len < MAX_LEN){
            for (i = 0; i < sizeof(wordset)/sizeof(wordset[0]); i++){
                new_password = (char **)realloc(new_password ,(current_len + 2)*sizeof(char **));
                for(int j = 0; j < current_len; j++) {
                    strncpy(new_password[j], current_password[j], current_len);
                }
                new_password[current_len] = wordset[i];
                new_password[current_len+1] = 0;
                pwlist.push_back(new_password);
            }
        }
        // now remove the front element as it didn't match the password
        pwlist.pop_front();
    }
}

int main(int argc, char const *argv[])
{
    char* salt;
    char* target;
    char* password;
    
    // define the character set from which the password will be built
    char *words[] = {"red", "green", "blue", "orange", "pink"};
    
    //convert the salt from string to char*
    salt = new char[target_salt.length()+1];
    copy(target_salt.begin(), target_salt.end(), salt);
    
    //convert the hash from string to char*
    target = new char[target_pw_hash.length()+1];
    copy(target_pw_hash.begin(), target_pw_hash.end(), target);
    // std::cout << "hash: " << target << std::endl;
    
    //start the search
    password = exhaustive_search(words, salt, target);
    
    if (strlen(password)!= 0) 
        std::cout << "Password successfuly recovered: " << password << " \n";
    else 
        std::cout << "Failure to find password, try distinct character set of size \n";
    return 0;
}

I know the problem is at line 38, however, I can't seem to understand why, but I am guessing it is related to dynamic memory allocation. Any thoughts?

Andrei0408
  • 197
  • 7
  • Don't write *"i know the problem is at line 38"*. Instead write the statement where you know the problem is. So don't we don't have to either copy/paste the code and see the line number or count the line by ourselves. Make sure you're not going out of bounds of the array. – Jason Oct 16 '22 at 11:48
  • @JasonLiam ```if (res[i] != hash[i])```, there's also a comment at this line, in the code. – Andrei0408 Oct 16 '22 at 11:49
  • Why not simply use `std::string`, or even `std::vector` instead of all of the usage of `new char []`? – PaulMcKenzie Oct 16 '22 at 11:50
  • @PaulMcKenzie If I cannot figure out the issue, I'll probably be doing this. But I wanted to see if I can solve it like this first – Andrei0408 Oct 16 '22 at 11:51
  • 1
    `for (i = 0; i < sizeof(wordset)/sizeof(wordset[0]); i++)` -- That `sizeof` does not do what you think it does. Have you printed out what the result of using that `sizeof` will give you? – PaulMcKenzie Oct 16 '22 at 11:51
  • *If I cannot figure out the issue, I'll probably be doing this* -- Maybe it would be wise to just start over using `std::string`, instead of trying to apply duct-tape all over the place with bad code. – PaulMcKenzie Oct 16 '22 at 11:53
  • Also this: `char *words[] = {"red", "green", "blue", "orange", "pink"};` is not valid C++. That should be `const char *words[] = {"red", "green", "blue", "orange", "pink"};`. – PaulMcKenzie Oct 16 '22 at 11:58
  • 1
    Turn on compiler warnings. Fix warnings. Do not use `new` yourself (unless you are writing a smart container such as a kind of `vector` or `string`), instead use `std::vector` or `std::string` or `std::unique_ptr`. Use raw pointers as non-owning pointers. – Eljay Oct 16 '22 at 12:03
  • 2
    This looks like `C` code that was plucked out of some larger program, and without any modifications to make it look like C++, just pasted into a C++ shell. This is your chance to actually make the code C++ by utilizing what has been mentioned. As a matter of fact, just with a quick look, the only `C` interface to anything could be the single line `char* res = crypt(pw, salt);` -- that's it. You simply pass `pw.c_str()`, `salt.c_str()`, etc. Everything else should be `std::string`, `std::vector`, etc – PaulMcKenzie Oct 16 '22 at 12:11

0 Answers0