3
\$\begingroup\$

THIS IS JUST FOR LEARNING PURPOSES. I WON'T BE USING THIS TO HACK ANYONE

Here is a small algorithm I made to crack a passwork. This is just the background of a more complex code I'm going to make.

To be honest, this code is TERRIBLY written, so I need help on making it more efficient and flexible. It is limited to 5-characters password and not easy to expand.

#include<algorithm>
#include<iostream>
#include<string>
#include<vector>

std::string checkCharacters = {
    '%',
    'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
    'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    '1','2','3','4','5','6','7','8','9','0'
};

std::string search(std::string password) {
    for (int a = 0; a < checkCharacters.size(); a++)
        for (int b = 0; b < checkCharacters.size(); b++)
            for (int c = 0; c < checkCharacters.size(); c++)
                for (int d = 0; d < checkCharacters.size(); d++)
                    for (int e = 0; e < checkCharacters.size(); e++)
                    {
                        std::string checkString = "";
                        checkString += checkCharacters[a];
                        checkString += checkCharacters[b];
                        checkString += checkCharacters[c];
                        checkString += checkCharacters[d];
                        checkString += checkCharacters[e];

                        checkString.erase(std::remove(checkString.begin(), checkString.end(), '%'), checkString.end());

                        if (checkString == password)
                            return checkString;
                    }

    return "TARGET_PASSWORD_NOT_FOUND";
}

int main() {
    while (1) {
        std::string password;
        std::cout << "Enter password : ";
        std::cin >> password;
        std::cout << "Your password is : " << password << std::endl;
        std::string attempt = search(password);
        std::cout << attempt << std::endl << std::endl;
    }
}
\$\endgroup\$
  • \$\begingroup\$ Generating all the possible passwords (if this is what you need for your other use-cases) is a special case of a more general problem: make all subsets of length k from a set of size n. It might be a good learning experience to try to write such a general code for example using a simple recursive method and with templates to make your method work for arbitrary sets and arbitrary password-lengths k. \$\endgroup\$ – Winther Apr 17 '17 at 2:00
  • \$\begingroup\$ @Winther, or one can just use std::next_permutation in a loop \$\endgroup\$ – Incomputable Apr 17 '17 at 8:54
  • 1
    \$\begingroup\$ @Incomputable That won't work here. std::next_permutation gives you the next permutation of the current word but it does not add or remove letters from the word. \$\endgroup\$ – Martin York Apr 17 '17 at 16:28
  • 1
    \$\begingroup\$ @Dat I don't think anybody is worried about you cracking passwords with this. It would take longer than the current existence of the universe before it cracked any real password system. \$\endgroup\$ – Martin York Apr 17 '17 at 16:30
5
\$\begingroup\$

This is a critique, and a rewrite of the code you showed, that is, the code for checking if a given password only contains characters from a given character set; for an actual brute force password cracker, see e.g. this question.


I think you should look at the problem a bit differently.

Why don't you go through the characters in the password, and check if every character is in the set of allowed characters?
If you get that every character of the password is in the set of allowed characters, then the password is valid, otherwise it isn't.

I'd implement this in the following way.

#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<set>
#include<numeric>

const static std::set<char> charSet = {
    '%',
    'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
    'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    '1','2','3','4','5','6','7','8','9','0'
};

bool checkPassword(const std::string password) {
  return std::accumulate( password.begin(), password.end(), true,
    [](bool acc, char c) { return acc && charSet.find(c) != charSet.end(); } );
}

int main() {
  for (;;) {
    std::string password;
    std::cout << "Enter password : ";
    std::cin >> password;
    std::cout << "Your password is : " << password << std::endl;
    if(checkPassword(password)) {
      std::cout << "VALID" << std::endl << std::endl;
    } else {
      std::cout << "INVALID" << std::endl << std::endl;
    }
  }
  return 0;
}

This will work with passwords of any length.
Set lookups have logarithmic complexity, so checking a password of length \$n\$ given a character set of size \$m\$ should be \$\mathcal O(n\cdot \log(m))\$.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ @JerryCoffin That's why I wrote 'linear time in its [the password's] length'! Isn't checking a password actually \$O(Nlog(M))\$, since set lookups have logarithmic complexity? Oh my... why did I write that sets have constant lookup time.. thanks for making me notice that. \$\endgroup\$ – Andrew Apr 17 '17 at 1:40
  • \$\begingroup\$ @JerryCoffin Thank you again, it should be fixed now. \$\endgroup\$ – Andrew Apr 17 '17 at 1:46
  • \$\begingroup\$ One other point: instead of using std::accumulate, I'd probably use std::all_of here. return std::all_of(pw.begin(), pw.end(), [](char c) { return charSet.find(c) != charSet.end(); }); \$\endgroup\$ – Jerry Coffin Apr 17 '17 at 2:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.