When in doubt ...

Brute Force Password Cracking


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



Foolish passwords

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 ...


Low hanging fruit

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


short or medium-sized passwords with identical syllables

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).

Short passwords starting with capital

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

  1. words of 1, 2 or 3 lowercase characters
  2. the same words, starting with a capital
  3. the same words, in all caps
  4. numbers from 1 - 999999
  5. all of the above, with duplication (eg bi becomes bibi, 123 becomes 123123), including caps, all caps, and camel case

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.


Common password formats

a string of characters, with numbers added to the end

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)

longer versions ...

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).

Strong passwords

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.



Timing

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

Koen Noens
August 2008