When in doubt, use brute force
Unix Programmer paradigm
This is both an exploration of Linux shell (bash) programming, and a plea for using strong passwords (or better yet, replace passwords with alternatives such as public key authentication, certificates, etc.)
"Brute force" means that you apply a simple program and rely on the computer's ability to do repetitive tasks at high speed. A brute force attack is simply trial and error, fast.
There are 2 brute force approaches to password cracking. One is to take a list of words (eg. a list of "known common passwords" or a list of "default administrator passwords for popular appliances", or a list of sport heroes if you're looking for the password of someone who's in to sports, or just a complete dictionary), and just try every word in that list (possibly with some common permutations of each word, eg uppercase/lowercase, appended or prepended digits, replace letters by a similar-looking digit, ...). This is usually called a "dictionary attack".
The other approach is to simply try any combination of characters : you start with a, aa, ab, ac, .... aaa ... abcd .... until you reach zzz..z. Usually, when people talk of brute force password cracking, this is what they mean, in contrast to dictionary cracking. A dictionary attack is usually faster, because most people base their passwords on existing words, names, ... , and a simple bash script run on a mediocre computer can read and output over 12,500 words per second. That's 750,000 words per minute. You can test for a lot of commonly used words that way.
I've written a bash shell script that generates random strings of an arbitrary length, using all possible combinations of lowercase, uppercase, numbers and punctuation (generatewords). Obviously, this can be used to crack passwords by brute force : simply try any combination until you find a match. If the output of generatewords is saved to a file, this file can also be used by "dictionary" password crackers.
Simply trying any combination of characters may take days, especially when you're trying 16 character strong passwords against a target that has a blank password, but you forgot to check that. So attackers will go for the low hanging fruit : blank passwords and simple, short passwords, or common combinations such as a word with a number appended to it. Even if you do not use dictionary words (in any language) as part of your password (eg Pussy69), you will see that a seemingly strong password such as Xq_186 (uppercase, lowercase, numbers, special characters, 6 character length) is crackable within an hour.
Here are some tests of weak passwords and common password formats, with a timing of how long it would take to test all such combinations, using shell script. Keep in mind that in real life, the attacking system might be more performant than the one I tested with, shell script is slow compared to compiled programs, good programmers will know better than me how to optimize their code for speed, and real crackers know more about passwords than I do. So these numbers are optimistic : your password will probably be cracked faster.
see also Bash script for text and word processing
blank passwords or 1 or more spaces for a password take less than a second
# fools and pseudo wise guys # 1 second ## blank echo "" ## spaces for n in $(seq 1 20); do res="$res " echo $res done
Obviously, the same goes for passwords that contain only 1 character :
# series of identical characters # 12 seconds for n in $(seq 1 12); do res="" for c in 1 2 3 4 5 6 7 8 9 0 \. \= \$ \- \/ ; do res="$res$c" echo $res done; done; for c in a q w z s x e d c r f v t g b y h n u j i k o l p m M P L O K I J U N H Y B G T V F R C D E X S Z W Q A ; do res="" for n in $(seq 1 12); do res="$res$c" echo $res done; done;
matches: 1 111111111 A XXX xxxxxxxxxxx ...
short passwords (up to 4 characters, all uppercase or all lowercase, or only numbers, are found within seconds or minutes:
# low-hanging fruit : short passwords :
## PIN-style : just numbers (1-5 digits) # 10 seconds
for n in $(seq 0 99999); do
echo $n;
done
## 1-4 chars
generatewords 4 1 a # 4 minutes
generatewords 4 1 A # 4 minutes
## PIN - 6 digits
for n in $(seq 99999 999999); do # 2 min
echo $n;
done
matches: zxyq QLMP 123 985632
making your password longer yet easy to remember by using the same syllable twice doesn"t make it any stronger. It's just as trivial as a short password.
## short passwords with 2 identical syllables # 25 minutes
# first try syllables of 1-2 char, then 3 char
for l in 2 1 3 ; do
generatewords $l $l a | while read part1; do
echo $part1$part1 ;
# while we're at it, include capitalization
cap=$( echo ${part1:0:1} | tr '[:lower:]' '[:upper:]' )
part0=$cap${part1:1}
echo $part0$part1
# and camel case
echo $part0$part0
# and all caps
cap=$( echo $part1 | tr '[:lower:]' '[:upper:]' )
echo $cap$cap
done
done
examples (using all of the above tests in order)
bibi - matched in 21 seconds
Foofoo - matched in 5 minutes
matches: xx Xx bibi Bibi BiBi BIBI foofoo Foofoo FooFoo FOOFOO
time to test all possibilities: 25 minutes
note how duplication of syllables can be applied to all other tests, simply by adding an 'echo $part1$part1' statement anywhere the test already uses $part1
You can just as easy double up passwords that consist of numbers only (123123).
Some people tend to capitalize their passwords, either because they use a name, or because they think including an uppercase makes the password stronger. Usually, they don't consider using uppercase in the middle of a word ...
## short passwords (4 chars), possibly starting with a capital # 10 minutes generatewords 1 1 a A| while read part0; do generatewords 3 1 a |while read part1; do echo $part0$part1; done; done;
matches: Qzap qzap
time to test all possibilities: 10 minutes
If we combine all of the above in 1 run, including
the total test time for "short passwords" becomes 13 minutes.
If we include 4 character passwords (all lowercase, start with uppercase, all uppercase), this adds another 10 minutes, totalling 23 minutes to crack any of the simple and short passwords we described here.
another way of making a short, weak password seemingly stronger is by adding some numbers to it, typically at the end, and sometimes with a punctuation mark (typically _ or - )to join the 2 parts. This makes testing all possibilities slightly more time-consuming, but it remains within minutes, or a few hours for longer combinations
We will test some common cases :
1 or 2 characters, possibly starting with uppercase, and 1-3 digits added (eg zz999)
the same, with the characters repeated (eg Bibi54)
all of the above, in all uppercase (ZZ999, BIBI54)
all of the above, with a separator (ZZ_999, bibi_54)
note that a password such as Bibi54 or ZZ_999 is usually considered a strong password : minimum 6 characters long, minimum 3 different types of characters, and not containing a dictionary word.
generatewords 2 1 a A | while read part0; do generatewords 3 1 n | while read digits; do echo $part0$digits; #B123 b123 bi123 Bi123 bI123 BI 123 echo $part0$part0$digits; #BB123 bb123 bibi123 BiBi123 bIbI123 BIBI123 done; done
time to test all possibilities: 30 minutes
multiply by the number of separators +1 . Eg; if you test also for 2 different separators ( - and _ ), add 2x 30 minutes (total: 1.5 hrs )
One interesting combination is missing from the last test, a capitalized 4 character string (Bibi123), because the 4 character combinations were made up by repeating a 2 character sequence.
To include those, we'd have to convert the first character to uppercase.
generatewords 2 1 a | while read part1; do
part0=$(echo ${part1:0:1} | tr '[:lower:]' '[:upper:]')${part1:1}
generatewords 3 1 n |while read digits; do
echo $part0$part1$digits;
done;
done ;
This takes an additional 5 minutes
We've now established that we can check a wide range of commonly used passwords within an hour, and that even some stronger passwords can be cracked within a matter of a few hours (eg if we include passwords with a punctuation mark between the text and the digits)
with combinations of 4 characters (upper and lower case), and numbers up to 999, resulting in a total password length of up to 8 characters, we're supposedly using a very strong password, but we've seen they can be cracked in a matter of hours.
one cure is to make them even longer ...
## [capitalized] string (4 chars) + 4 digits, 8-9 chars total generatewords 1 1 a A | while read part1 ; do generatewords 3 3 a | while read part2; do for n in $(seq 999 9999); do echo $part1$part2$n; for s in SEPARATOR; do echo $part1$part2$s$n; done; done; done; done
time to run all combinations: several hours.
This is also a popular format because the 4 digits allow the use of a significant year, like the year of birth (Zizi1972). So the testing time can be reduced by testing less digits (e.g. 1000-2500), and/or combining them with shorter strings (3 characters, or 2 characters but with a repeated syllable).
An other cure is to use real strong passwords, containing a mix of different kinds of characters, but not in any particular format, so that the only way to match them is effectively try any combination. The requirement that the password be long, remains.
It's obvious that, when we try any combination, we will repeat some of the words that we've tested before. That seems unavoidable (unless we make the crack script a lot more complex). Given that we're now counting on hours to find a match, some extra minutes wasted on tests that we included before, doesn't seem to matter all that much
Note that, by wrapping 'generatewords' in a loop, we can control the order in which different lengths are tested, to avoid spending days on 14 character combinations while we could find a 6 character combinations in a matter of hours. Here, we try short passwords first, because that only takes a some minutes to cover completely, then we take "6 characters" before "5 characters" because most people who use strong passwords have read that 6 characters is a minimum (and testing for 5 characters would delay us for quite a while), and finally we go for the long haul by looking for everything else up to 12 (or more) characters. This is where you start counting in days, and where 'real' password cracking tools will really make a difference.
# give up on easy passwords, just try everything else for i in 4 3 2 1 6 5 $(seq 7 12); do generatewords $i $i a A n p done
timing :
random strings with 4 characters (lower- and uppercase, numbers, punctuation marks) : 22 hours to generate all combinations.
note that, although short, this is already an order of magnitude stronger than any of the previous passwords.
Compare timing for 4 char complex against 5 char plain. It's obvious that using passwords with different sorts of characters (lowercase, uppercase, numbers, punctuation) exponentially increases the time to crack the password, provided these characters are distributed randomly in the password, in stead of placed at a predictable position.
To do your own testing, you can paste any of the above tests in a time() statement, eg
me@nix:~$ time ( for n in $(seq 99999 999999); do echo $n; done )
To check any password against a given test, you can paste the test in the "testSubject" function of he following script:
#!/bin/bash
function testSubject (){
## paste the function you want to test here ##
for l in 2 1 3; do
generatewords $l $l a | while read part1; do
echo $part1$part1 ;
# while we're at it, include capitalization
cap=$( echo ${part1:0:1} | tr '[:lower:]' '[:upper:]' )
part0=$cap${part1:1}
echo $part0$part1
# and camel case
echo $part0$part0
done
done
## end of function
} ##
START=$(date +%s)
testSubject | while read outcome ; do
echo "$1 - $outcome"
if [ "$outcome" = "$1" ]; then
END=$(date +%s)
DIFF=$(( $END - $START ))
echo "matched in $DIFF seconds"
break
exit
fi
done
exit
Then invoke the script with the password you want tested:
me@nix:~$ testpasswd FooFoo ... ... FooFoo - FodFod FooFoo - focfoc FooFoo - Focfoc FooFoo - FocFoc FooFoo - foofoo FooFoo - Foofoo FooFoo - FooFoo matched in 38 seconds