Improvement #1 - Loops
Your looping structure seems completely unnecessary if you use brace expansions instead, it can be condensed like so:
$ more pass.bash
#!/bin/bash
for str in $(echo {a..z}{a..z}{a..z}); do
pass=$(openssl passwd -salt $1 $str)
if [[ "$pass" == "$2" ]]; then
echo "Password: $str"
exit;
fi
done
# vim: set nolist ts=2 :
I'm showing 4 characters just to make it run faster, simply add additional {a..z}
braces for additional characters for password length.
Example runs
4 characters
$ openssl passwd -salt ab hhhh
abBYJnOuV8dUA
$ time ./pass.bash ab abBYJnOuV8dUA
Password: hhhh
real 18m3.304s
user 6m58.204s
sys 9m34.468s
So it completed in 18 minutes.
5 characters
$ openssl passwd -salt ab ccccc
abZwsITAI6uwM
$ time ./pass.bash ab abZwsITAI6uwM
Password: ccccc
real 426m37.234s
user 16m34.444s
sys 398m20.399s
This took ~426 minutes. I actually Ctrl+C this, so it hadn't finished, but I didn't want to wait any more than this!
NOTE: Both these runs were on this CPU:
brand = "Intel(R) Core(TM) i5 CPU M 560 @ 2.67GHz
Improvement #2 - Using nice?
The next logical step would be to nice
the above runs so that they can consume more resources.
$ nice -n -20 ./pass.bash ab hhhhh
But this will only get you so far. One of the "flaws" in your approach is the calling of openssl
repeatedly. With {a..z}^5
you're calling openssl
26^5 = 11881376 times.
One major improvement would be to generate the patterns of {a..z}....
and save them to a file, and then pass this as a single item to openssl
one time. Thankfully openssl
has 2 key features that we can exploit to get what we want.
Improvement #3 - our call structure to openssl
The command line tool openssl
provides the switches -stdin
and -table
which we can make use of here to have a single invoke of openssl
irregardless of how many passwords we want to pass to it. This is single modification will remove all the overhead of having to invoke openssl
, do work, and then exit it, instead we keep a single instance of it open indefinitely, feeding it as many passwords as we want.
The -table
switch is also crucial since it tells openssl
to include the original password along side the ciphers version, so we can make fairly quick work of looking for our match.
Here's an example using just 3 characters to show what we're changing:
$ openssl passwd -salt ab abc
abFZSxKKdq5s6
$ printf "%s\n" {a..z}{a..z}{a..z} | \
openssl passwd -stdin -table -crypt -salt ab | grep -m1 abFZSxKKdq5s6
abc abFZSxKKdq5s6
So now we can really revamp our original pass.bash
script like so:
$ cat pass2.bash
#!/bin/bash
pass=$(printf "%s\n" {a..z}{a..z}{a..z}{a..z}{a..z} | \
openssl passwd -stdin -table -crypt -salt $1 | grep -m1 $2)
if [[ "$pass" =~ "$2" ]]; then
echo "Password: $pass"
fi
# vim: set nolist ts=2 :
Now when we run it:
$ time ./pass2.bash ab aboznNh9QV/Q2
Password: hhhhh aboznNh9QV/Q2
real 1m11.194s
user 1m13.515s
sys 0m7.786s
This is a massive improvement! This same search that was taking more than 426 minutes is now done in ~1 minute! If we search through to say "nnnnn" that's roughly in the middle of the {a..z}^5
character set space. {a..n}
is 14 characters, and we're taking 5 of them.
$ echo "14^5" | bc
537824
$ openssl passwd -salt ab nnnnn
abRRCp5N3WN32
$ time ./pass2.bash ab abRRCp5N3WN32
Password: nnnnn abRRCp5N3WN32
real 1m10.865s
user 1m12.842s
sys 0m8.530s
This search took ~1.1 minutes. NOTE: We can search the entire space of 5 character passwords in ~1 minute too.
$ time ./pass2.bash ab abBQdT5EcUvYA
Password: zzzzz abBQdT5EcUvYA
real 1m10.783s
user 1m13.556s
sys 0m8.251s
Conclusions
So with a restructuring we're running much faster. This approach scales much better too as we add a 6th, 7th, etc. character to the overall length of the password.
Be warned though that we're using a smallish character set, mainly only the lowercase alphabet characters. If you mix in all the number, both cases, and special characters you can typically get ~96 characters per position. This may not seem like a big deal but this increase your pool tremendously:
$ echo 26^5 | bc
11881376
$ echo 96^5 | bc
8153726976
Adding all those characters just increased by 2 orders of magnitude our search space. If we go up to roughly 10-12 characters of length to the password, it really puts a brute force hacking methodology out of reach.
Using proper a salt as well as additional NONCE's throughout the construction of a hashed password can add still more stumbling blocks.
What else?
You've mentioned using John (John the Ripper) or other cracking tools. Probably the state of the art currently would be HashCat.
Where John is a tighter version of the approach you're attempting to use, HashCat takes it to another level by enlisting the use of GPUs (up to 128) to really make your hacking attempts fly.
You can even make use of CloudCrack, which is a hosted version, and for a mere $17 US you can pay to have a password crack attempted.
References
Best Answer
Alternative #1: Monitor your process with monit
Install M/Monit and create a configuration file based on this template:
Alternative #2: Limit process CPU usage with cgroups
The most native Linux specific solution of them all. Offers a lot of options and complexity.
Example:
I encourage you to read more at:
DigitalOcean - How-to: Limit resources using cgroups on CentOS 6
RedHat - Resource Management Guide
Oracle - List of cgroups subsystems
Alternative #3: Limit process CPU usage with cpulimit
Get latest version of cpulimit from your package manager of choice, or by getting the source available at GitHub.
Limit the CPU usage to 90%:
cpulimit -l 90 /usr/bin/myprogram > /dev/null &
Sidenote:
You can also pin a certain process to use certain CPU core(s) to ensure that you always have some free CPU power.