PACK (Password Analysis and Cracking Kit) by iphelix
====================================================
1.0 About
---------
PACK was developed in order to aid in a password cracking competition "Crack Me If You Can" that occurred during Defcon 2010. The goal of this toolkit is to aiding in preparation for the "better than bruteforce" password attacks by analyzing common ways that people create passwords. After the analysis stage, the statistical database can be used to generate attack masks for common tools such as Hashcat, oclHashcat, and others.
NOTE: This tool itself can not crack passwords, but helps other tools crack more passwords faster.
2.0 Usage
---------
The following is a sample analysis and generation session that will illustrate the use of this kit. In all of the examples, a standard hashcat notation to represent different character sets is used:
?l - lowercase characters
?u - uppercase characters
?d - digits
?s - special characters
2.1 DictStat
------------
Before we can begin using the toolkit we must establish selection criteria for the sample input. Since we are looking to analyze the way people create their passwords, we must obtain as large of a list of leaked passwords as possible. One such excellent list is based on RockYou.com compromise. This list both provides large and diverse enough sample that it can be used as a good estimate for common passwords used by similar sites (e.g. social networking). The analysis obtained from this list may not work for organizations with specific password policies (e.g. 8 characters, minimum digit and special character requirements, etc.). As such, selecting sample input should be as close to your target as possible. In addition, try to avoid obtaining lists based on already cracked passwords as it will generate statistics skewed toward the type of passwords that could be cracked and not the overall sample.
In the example below, we will use rockyou.txt containing approximately 14 million passwords. Launch dictstat with the following command line:
$ python dictstat.py rockyou.txt
NOTE: It is highly recommended that you install psyco python module for larger lists.
Below is an output from the above command:
-------------------------------------------------------------------------------
[*] Analyzing dictionary: rockyou.txt
[+] Analyzing 100% (14344391/14344391) passwords
NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords
[*] Line Count Statistics...
[+] 8: 20% (2966004)
[+] 7: 17% (2506264)
[+] 9: 15% (2191000)
[+] 10: 14% (2013690)
[+] 6: 13% (1947858)
[+] 11: 06% (865973)
[+] 12: 03% (555333)
[+] 13: 02% (364169)
[+] 5: 01% (259174)
[+] 14: 01% (248514)
[+] 15: 01% (161181)
[*] Mask statistics...
[+] stringdigit: 37% (5339715)
[+] allstring: 28% (4115881)
[+] alldigit: 16% (2346842)
[+] othermask: 05% (731240)
[+] digitstring: 04% (663975)
[+] stringdigitstring: 03% (450753)
[+] stringspecialstring: 01% (204494)
[+] stringspecialdigit: 01% (167826)
[+] stringspecial: 01% (147874)
[+] digitstringdigit: 00% (130518)
[+] specialstringspecial: 00% (25100)
[+] specialstring: 00% (14410)
[+] allspecial: 00% (5763)
[*] Charset statistics...
[+] loweralphanum: 42% (6075055)
[+] loweralpha: 25% (3726656)
[+] numeric: 16% (2346842)
[+] loweralphaspecialnum: 03% (472673)
[+] upperalphanum: 02% (407436)
[+] mixedalphanum: 02% (382246)
[+] loweralphaspecial: 02% (381095)
[+] upperalpha: 01% (229893)
[+] mixedalpha: 01% (159332)
[+] mixedalphaspecialnum: 00% (53240)
[+] mixedalphaspecial: 00% (49633)
[+] upperalphaspecialnum: 00% (27732)
[+] upperalphaspecial: 00% (26795)
[+] special: 00% (5763)
[*] Advanced Mask statistics...
[+] ?l?l?l?l?l?l?l?l: 04% (688053)
[+] ?l?l?l?l?l?l: 04% (601257)
[+] ?l?l?l?l?l?l?l: 04% (585093)
[+] ?l?l?l?l?l?l?l?l?l: 03% (516862)
[+] ?d?d?d?d?d?d?d: 03% (487437)
[+] ?d?d?d?d?d?d?d?d?d?d: 03% (478224)
[+] ?d?d?d?d?d?d?d?d: 02% (428306)
[+] ?l?l?l?l?l?l?d?d: 02% (420326)
[+] ?l?l?l?l?l?l?l?l?l?l: 02% (416961)
[+] ?d?d?d?d?d?d: 02% (390546)
[+] ?d?d?d?d?d?d?d?d?d: 02% (307540)
[+] ?l?l?l?l?l?d?d: 02% (292318)
[+] ?l?l?l?l?l?l?l?d?d: 01% (273640)
[+] ?l?l?l?l?l?l?l?l?l?l?l: 01% (267742)
[+] ?l?l?l?l?d?d?d?d: 01% (235364)
[+] ?l?l?l?l?d?d: 01% (215079)
[+] ?l?l?l?l?l?l?l?l?d?d: 01% (213117)
[+] ?l?l?l?l?l?l?d: 01% (193110)
[+] ?l?l?l?l?l?l?l?d: 01% (189855)
[+] ?l?l?l?l?l?l?l?l?l?l?l?l: 01% (189360)
[+] ?l?l?l?d?d?d?d: 01% (178308)
[+] ?l?l?l?l?l?d?d?d?d: 01% (173560)
[+] ?l?l?l?l?l?l?d?d?d?d: 01% (160596)
[+] ?l?l?l?l?l?l?l?l?d: 01% (160061)
[+] ?l?l?l?l?l?d?d?d: 01% (152406)
-------------------------------------------------------------------------------
Here is what we can immediately learn from the list above:
1) The majority of passwords are 6-10 characters
2) The majority of passwords follow masks of the form "string followed by digits", "all string", and "all digits".
3) The majority of passwords use lower alphanumeric and lower alpha character sets.
In the last section "Advanced Mask Statistics", you can look at actual masks matching the most frequent passwords. Masks are generated by attempting to find the minimum matching set of regular expressions that would match that string.
You can interpret individual symbols as follows:
?l - a single lowercase character
?u - a single uppercase character
?d - a single digit
?s - a single special character
So, if we will take the first entry as an example, "?l?l?l?l?l?l?l?l" mask will match all of the lowercase alpha passwords. Given the sample size you will be able to crack approximately 4% of passwords.
While the standard output yields a lot of data, you may be interested in using filters to narrow down on password data and may be gain additional insight into the way people generate passwords.
Let's see how people tend to select their passwords which follow the mask string followed by digits:
$ python dictstat.py -m stringdigit rockyou.txt
[*] Analyzing dictionary: rockyou.txt
[+] Analyzing 37% (5339715/14344391) passwords
NOTE: Statistics below is relative to the number of analyzed passwords, not total number of passwords
[*] Line Count Statistics...
[+] 8: 23% (1267292)
[+] 7: 18% (981472)
[+] 9: 17% (940000)
[+] 10: 14% (750966)
[+] 6: 11% (619001)
[+] 11: 05% (294874)
[+] 12: 03% (175879)
[+] 13: 01% (103048)
[+] 14: 01% (65959)
[*] Mask statistics...
[+] stringdigit: 100% (5339715)
[*] Charset statistics...
[+] loweralphanum: 88% (4720336)
[+] upperalphanum: 06% (325943)
[+] mixedalphanum: 05% (293436)
[*] Advanced Mask statistics...
[+] ?l?l?l?l?l?l?d?d: 07% (420326)
[+] ?l?l?l?l?l?d?d: 05% (292318)
[+] ?l?l?l?l?l?l?l?d?d: 05% (273640)
[+] ?l?l?l?l?d?d?d?d: 04% (235364)
[+] ?l?l?l?l?d?d: 04% (215079)
[+] ?l?l?l?l?l?l?l?l?d?d: 03% (213117)
[+] ?l?l?l?l?l?l?d: 03% (193110)
[+] ?l?l?l?l?l?l?l?d: 03% (189855)
[+] ?l?l?l?d?d?d?d: 03% (178308)
[+] ?l?l?l?l?l?d?d?d?d: 03% (173560)
[+] ?l?l?l?l?l?l?d?d?d?d: 03% (160596)
[+] ?l?l?l?l?l?l?l?l?d: 02% (160061)
[+] ?l?l?l?l?l?d?d?d: 02% (152406)
[+] ?l?l?l?l?l?l?d?d?d: 02% (132220)
[+] ?l?l?l?l?l?l?l?l?l?d: 02% (129833)
[+] ?l?l?l?l?l?d: 02% (114739)
[+] ?l?l?l?l?d?d?d: 02% (111221)
[+] ?l?l?d?d?d?d: 01% (98305)
[+] ?l?l?l?d?d?d: 01% (98189)
[+] ?l?l?l?l?l?l?l?d?d?d: 01% (87613)
[+] ?l?l?l?l?l?l?l?l?l?d?d: 01% (82655)
[+] ?l?l?l?l?l?l?l?d?d?d?d: 01% (70915)
[+] ?l?d?d?d?d?d?d: 01% (54888)
As we look at the above output, you will immediately notice that "Mask statistics" only has one entry: "stringdigit". This is because of our specified filter. Let's analyze the data. On the very top of the output, you can see that only 37% passwords were analyzed. You can estimate that by cracking only passwords match this mask you can at most crack about 37% of the total set.
Next, it appears that only 11% of this password type use anything other than lowercase. So it would be smart to concentrate on only lowercase strings matching this mask. At last, in the "Advanced Mask Statistics" section you can see that the majority of "stringdigit" passwords consist of a string with two or four digits following it.
With the information gained from the above, you can begin creating a mental image of you target's password generation patterns.
There are other filters available for password length, mask, and character sets:
Length: -l [integer]
Mask: -m [numeric, loweralpha, upperalpha, mixedalpha, loweralphanum, upperalphanum, mixedaphanum, special, loweralphaspecial, upperalphaspecial, mixedalphaspecial, loweraphaspecialnum, upperalphaspecialnum, mixedalphaspecialnum]
Character sets: -c [alldigit, allstring, stringdigit, digitstring, digitstringdigit, stringdigitstring, allspecial, stringspecial, specialstring, stringspecialstring, stringspecialdigit, specialstringspecial]
DEVELOPERS: You can edit respective lists on the very top of the sourcefiles and add regular expressions for whatever mask or character set you can imagine.
While the "Advanced Mask Section" only displays patterns matching greater than 1% of all passwords, you can obtain and save a full list of passwords masks matching a given dictionary by using the following command:
$ python dictstat.py -o rockyou.csv rockyou.txt
The above command will save all of the password masks and their frequencies into a CSV file 'rockyou.csv'. Naturally, you can provide filters to only generate masks file matching your parameters. The output file can be used as an input to MaskGen tool covered in the next section.
2.2 MaskGen
-----------
While analyzing passwords using DictGen can be both revealing and exciting, it is simply not feasible for larger data sets. MaskGen will analyze the masks output file produced by DictGen and help you generate custom password mask input files for you applications.
Let's run MaskGen with minimum parameters and observe it's output:
$ python maskgen.py rockyou.csv
[*] [0] [11/14344391] [0.00] [0d|0h|0m|0s] ?
[*] [1] [49/14344391] [0.00] [0d|0h|0m|0s] ?s?u?l?d
[*] [2] [340/14344391] [0.00] [0d|0h|0m|0s] ?s?u?l?d ?s?u?l?d
[*] [3] [2479/14344391] [0.00] [0d|0h|0m|0s] ?s?u?l?d ?s?u?l?d ?s?u?l?d
[*] [4] [18015/14344391] [0.00] [0d|0h|0m|0s] ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d
[*] [5] [259174/14344391] [1.00] [0d|0h|0m|7s] ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d
[*] [6] [1947858/14344391] [13.00] [0d|0h|12m|735s] ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d
[*] [7] [2506264/14344391] [17.00] [0d|19h|1163m|69833s] ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d
[*] [8] [2966004/14344391] [20.00] [76d|1842h|110570m|6634204s] ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d
[*] [9] [2191000/14344391] [15.00] [7294d|175069h|10504156m|630249409s] ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d
...
[*] Coverage is %100 (14344391/14344391)
[*] Total time 1754409989919144353064355175042468812368733249495616893327070104
8211232752070650397369740269713581215815201614225211442387866496572012147724920
4649812938136306665865356654151858255534645195728557246448055491199506753532407
0837796021192337530275757511267739149674126051965467434111830202528596251368154
7242960405598417380173912831468583249522597950717975278703858956758951666222598
6032208513767643382460723235228659294580495141287225869341138204996227078055906
67374828225147228141265693827036d|421058397580594644735445242010192514968495979
8789480543984968251570695860496956095368737664731259491795648387414050746173087
9591772829154539809115955105152713599807685596996445981328314846974853739147533
3178878816208477777001071045086161007266181802704257395921790252471712184186839
2486068631003283571338310497343620171241739079552459979885423508172314066888926
1496221483998934236647730043304234411790573576454878230699318833908934208641873
1691990944987334176016995877403533475390376651848886h|2526350385483567868412671
4520611550898109758792736883263909809509424175162981736572212425988387556950773
8903244843044770385277550636974927238854695730630916281598846113581978675887969
8890818491224348851999073272897250866662006426270516966043597090816225544375530
7415148302731051210354916411786019701428029862984061721027450434477314759879312
5410490338844013335568977328903993605419886380259825406470743441458729269384195
91300345360525185123901519456699240050561019752644212008523422599110933198m|151
5810231290140721047602871236693053886585527564212995834588570565450509778904194
3327455593032534170464334194690582686223116653038218495634331281743837854976895
Ignore the crazy long last line for a minute and let's take a look at line with prefix of [5]:
[*] [5] [259174/14344391] [1.00] [0d:0h:0m:7s] ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d
\ \ \ \ \
\ \ \ \ \ matching mask
\ \ \ \
\ \ \ \ time to crack
\ \ \
\ \ \ percent coverage from sample
\ \
\ \ total number of matching passwords
\
\ password length
NOTE: day, hour, minute, and seconds parameters are independent of each other. [0d|0h|1m|60s] means the total runtime is 60 seconds and not 1 minute 60 seconds. You will find it useful when doing calculations and converting back and forth.
The information contained in the above file will present you with an ordered list of masks together with how long it will take to crack all passwords matching this mask (default speed is 1000000000 keys/sec), the percentage coverage of the total sample, total count of matching passwords.
In our example "?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d ?s?u?l?d" mask shows that every single character in every position has the mask of "?s?u?l?d" or every single character. This line is suggesting that in order to crack every single password of length 5, you must attempt to crack every single character set for each position (or complete bruteforce) and it will take you about 7 seconds to do that.
NOTE: There is a bit of black magic going on in the background to generate masks for a specific password length. As you may have observed passwords become more complex by adding extra characters (exponential growth) as opposed to just increasing the character set in any given slot. As such, I have chosen to generate masks based on the length of passwords that they match. Combined masks are generated by looking at all masks of a given length and combining them together. For example masks ?l?l?l and ?l?l?d matching three character passwords will be combined to "?l ?l ?l?d" to match all passwords represented by both masks.
The last (really long) line tells you the total number of days/hours/minutes/seconds to crack every single password using every single mask in our database. The time is so huge, because we are performing a dumb bruteforcing attack. What we are going to try to do is to attempt to crack the maximum number of password in minimum time.
You should almost never run MaskGen with no parameters (except may be cases to remind yourself why dumb bruteforcing is bad). Let's use some of the data gained from DictStat to generate a set of masks that will give us 50% of passwords withing some reasonable time.
We have already collected statistical data about how many time each of the masks occurs, so let's leave out all of the infrequently occuring masks:
$ python maskgen.py --occurence=10000 rockyou.csv
[*] [5] [220730/14344391] [1.00] [0d|0h|0m|0s] ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d
[*] [6] [1741132/14344391] [12.00] [0d|0h|1m|87s] ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?s?u?l?d
[*] [7] [2228900/14344391] [15.00] [0d|1h|89m|5396s] ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?s?u?l?d
[*] [8] [2591942/14344391] [18.00] [5d|142h|8543m|512622s] ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?s?u?l?d ?u?l?d ?s?u?l?d
[*] [9] [1857159/14344391] [12.00] [563d|13527h|811651m|48699101s] ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?s?u?l?d ?u?l?d ?s?u?l?d ?u?l?d ?s?u?l?d
[*] [10] [1623494/14344391] [11.00] [14884d|357228h|21433720m|1286023221s] ?u?d?l ?u?d?l ?u?d?l ?u?d?l ?u?d?l ?u?d?l ?u?d?l ?u?d?l ?u?d?l ?s?u?d?l
[*] [11] [634442/14344391] [4.00] [602275d|14454600h|867276011m|52036560683s] ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d
[*] [12] [362705/14344391] [2.00] [54842d|1316217h|78973022m|4738381338s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[*] [13] [205833/14344391] [1.00] [1974325d|47383813h|2843028802m|170581728179s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[*] [14] [133214/14344391] [0.00] [71075720d|1705817281h|102349036907m|6140942214464s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[*] [15] [55398/14344391] [0.00] [19412723d|465905372h|27954322371m|1677259342285s] ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l
[*] [16] [33484/14344391] [0.00] [504730820d|12113539694h|726812381657m|43608742899428s] ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l
[*] [17] [13147/14344391] [0.00] [13123001335d|314952032051h|18897121923085m|1133827315385150s] ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l ?l
[*] Coverage is %81 (11701580/14344391)
[*] Total time 13720867497d|329300819931h|19758049195865m|1185482951751954s
Using above masks you will be able to achieve significantly better cracking times while still preserving more than 80% total password coverage. At the same time, you will avoid pure bruteforcing of every possible charter in every position. For example, for passwords greater than 15 characters you can use only lowercase alpha characters.
Let's take the above output to a much more reasonable time to satisfy our goal of cracking passwords in about a day with %50 coverage. For that, we will increase the frequency count as well as add maximum mask runtime paramter of one day or 8640 seconds:
$ python maskgen.py --occurence=100000 --maxtime=8640 rockyou.csv
[*] [5] [125816/14344391] [0.00] [0d|0h|0m|0s] ?l ?l ?l ?l ?l
[*] [6] [1321621/14344391] [9.00] [0d|0h|0m|2s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[*] [7] [1847487/14344391] [12.00] [0d|0h|1m|78s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[*] [8] [2114310/14344391] [14.00] [0d|0h|47m|2821s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[*] [9] [1563883/14344391] [10.00] [1d|28h|1692m|101559s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[*] [10] [638820/14344391] [4.00] [0d|6h|362m|21767s] ?d?l ?d?l ?d?l ?d?l ?d?l ?d?l ?d ?d ?d ?d
[*] [11] [107864/14344391] [0.00] [0d|0h|1m|100s] ?d ?d ?d ?d ?d ?d ?d ?d ?d ?d ?d
[*] Coverage is %53 (7719801/14344391)
[*] Total time 1d|35h|2105m|126327s
We have almost reached our, but we can fine tune it by adding maximum password complexity. Password complexity is determined based on the number of all possible passwords matching a mask. For example, mask "?l?d ?l?d" can have up to (26+10)^2 or 1296 passwords. In our example, we are going to relax the occurence flag, but include maximum password complexity of 2821109907456 which corresponds to an eight character loweralphanumeric password (26+10)^8. We are also going to include --showmasks flag to see the exact component masks and their respective counts and relative percentages.
$ maskgen.py --occurence=50000 --maxtime=8640 --complexity=2821109907456 --showmasks rockyou.csv
[*] [5] [125816/14344391] [0.00] [0d|0h|0m|0s] ?l ?l ?l ?l ?l
[5] [125816/125816] [100.00] [0.00] [0d|0h|0m|0s] ?l?l?l?l?l
[*] [6] [1569957/14344391] [10.00] [0d|0h|0m|56s] ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d ?u?l?d
[6] [601257/1569957] [38.00] [4.00] [0d|0h|0m|0s] ?l?l?l?l?l?l
[6] [390546/1569957] [24.00] [2.00] [0d|0h|0m|0s] ?d?d?d?d?d?d
[6] [215079/1569957] [13.00] [1.00] [0d|0h|0m|0s] ?l?l?l?l?d?d
[6] [114739/1569957] [7.00] [0.00] [0d|0h|0m|0s] ?l?l?l?l?l?d
[6] [98305/1569957] [6.00] [0.00] [0d|0h|0m|0s] ?l?l?d?d?d?d
[6] [98189/1569957] [6.00] [0.00] [0d|0h|0m|0s] ?l?l?l?d?d?d
[6] [51842/1569957] [3.00] [0.00] [0d|0h|0m|0s] ?u?u?u?u?u?u
[*] [7] [1902375/14344391] [13.00] [0d|0h|1m|78s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[7] [585093/1902375] [30.00] [4.00] [0d|0h|0m|8s] ?l?l?l?l?l?l?l
[7] [487437/1902375] [25.00] [3.00] [0d|0h|0m|0s] ?d?d?d?d?d?d?d
[7] [292318/1902375] [15.00] [2.00] [0d|0h|0m|1s] ?l?l?l?l?l?d?d
[7] [193110/1902375] [10.00] [1.00] [0d|0h|0m|3s] ?l?l?l?l?l?l?d
[7] [178308/1902375] [9.00] [1.00] [0d|0h|0m|0s] ?l?l?l?d?d?d?d
[7] [111221/1902375] [5.00] [0.00] [0d|0h|0m|0s] ?l?l?l?l?d?d?d
[7] [54888/1902375] [2.00] [0.00] [0d|0h|0m|0s] ?l?d?d?d?d?d?d
[*] [8] [2114310/14344391] [14.00] [0d|0h|47m|2821s] ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d ?l?d
[8] [688053/2114310] [32.00] [4.00] [0d|0h|3m|208s] ?l?l?l?l?l?l?l?l
[8] [428306/2114310] [20.00] [2.00] [0d|0h|0m|0s] ?d?d?d?d?d?d?d?d
[8] [420326/2114310] [19.00] [2.00] [0d|0h|0m|30s] ?l?l?l?l?l?l?d?d
[8] [235364/2114310] [11.00] [1.00] [0d|0h|0m|4s] ?l?l?l?l?d?d?d?d
[8] [189855/2114310] [8.00] [1.00] [0d|0h|1m|80s] ?l?l?l?l?l?l?l?d
[8] [152406/2114310] [7.00] [1.00] [0d|0h|0m|11s] ?l?l?l?l?l?d?d?d
[*] [9] [1047021/14344391] [7.00] [0d|7h|470m|28211s] ?d?l ?d?l ?d?l ?d?l ?d?l ?d?l ?d?l ?d?l ?d
[9] [307540/1047021] [29.00] [2.00] [0d|0h|0m|1s] ?d?d?d?d?d?d?d?d?d
[9] [273640/1047021] [26.00] [1.00] [0d|0h|13m|803s] ?l?l?l?l?l?l?l?d?d
[9] [173560/1047021] [16.00] [1.00] [0d|0h|1m|118s] ?l?l?l?l?l?d?d?d?d
[9] [160061/1047021] [15.00] [1.00] [0d|0h|34m|2088s] ?l?l?l?l?l?l?l?l?d
[9] [132220/1047021] [12.00] [0.00] [0d|0h|5m|308s] ?l?l?l?l?l?l?d?d?d
[*] [10] [478224/14344391] [3.00] [0d|0h|0m|10s] ?d ?d ?d ?d ?d ?d ?d ?d ?d ?d
[10] [478224/478224] [100.00] [3.00] [0d|0h|0m|10s] ?d?d?d?d?d?d?d?d?d?d
[*] [11] [107864/14344391] [0.00] [0d|0h|1m|100s] ?d ?d ?d ?d ?d ?d ?d ?d ?d ?d ?d
[11] [107864/107864] [100.00] [0.00] [0d|0h|1m|100s] ?d?d?d?d?d?d?d?d?d?d?d
[*] Coverage is %51 (7345567/14344391)
[*] Total time 0d|8h|521m|31276s
Aha! By only losing a few cracked passwords, we have significantly reduced our cracking time. There is a wealth of information that you can analyze, but with enough practice you should be able to achieve that perfect mask combination representative of your target that can be used in your attacks.
There are a few additional parameters which I did not cover before, but that can be useful for generating masks:
--pps - You can specify exact passwords per second speed of your setup
--minlength and --maxlength - Defined minimum and maximum password lengths
--checkmask - Checks how many times a particular mask appears in the sample
For example, if you are interested in finding out how well "?l ?l ?l ?l ?l ?l ?l?d ?l?d" mask will perform in the sample, you can execute the following command:
$ python maskgen.py --checkmask="?l ?l ?l ?l ?l ?l ?l?d ?l?d" --showmasks rockyou.csv
[*] [8] [1305708/14344391] [9.00] [0d|0h|6m|400s] ?l ?l ?l ?l ?l ?l ?l?d ?l?d
[8] [688053/1305708] [52.00] [4.00] [0d|0h|3m|208s] ?l?l?l?l?l?l?l?l
[8] [420326/1305708] [32.00] [2.00] [0d|0h|0m|30s] ?l?l?l?l?l?l?d?d
[8] [189855/1305708] [14.00] [1.00] [0d|0h|1m|80s] ?l?l?l?l?l?l?l?d
[8] [7474/1305708] [0.00] [0.00] [0d|0h|1m|80s] ?l?l?l?l?l?l?d?l
[*] Coverage is %9 (1305708/14344391)
[*] Total time 0d|0h|6m|400s
The above output, will tell you that this mask matches only 9 percent of passwords and will take only 6 minutes of cracking time.
3.0 PolicyGen
--------------
A lot of the dictionary attacks will fail in the corporate environment with minimum password complexity rules. Instead of performing a pure bruteforcing attack, we can leverage known password complexity rules to avoid trying password candidates that are not compliant with the policy (e.g. ?l?l?l?l?l?l?l?l when at least one digit is required). Using PolicyGen, you will be able to generate a list of valid policy compliant (or intentionally non-compliant) rules that can be used to significantly decrease cracking time. Below is a sample session where we generate all valid password masks for the environment requiring at least one digit, one upper, and one special characters.
$ python policygen.py --output=masks.txt --mindigit=1 --minupper=1 --minspecial=1 --length 8 --pps=40000000000
[*] Password policy:
[+] Password length: 8
[+] Minimum strength: lower: 0, upper: 1, digits: 1, special: 1
[+] Maximum strength: lower: 8, upper: 8, digits: 8, special: 8
[*] Total Masks: 65536 Runtime: [1d|38h|2324m|139463s]
[*] Policy Masks: 46620 Runtime: [0d|18h|1097m|65828s]
As you can see from the output above, the total bruteforce runtime for an eight character password is 38 hours. However, when cracking passwords only matching a specific policy, we can reduce this time to only 18 hours!
Here is a snippet of the 'masks.txt' that we have generated above:
?l?l?l?l?l?u?d?s
?l?l?l?l?l?u?s?d
?l?l?l?l?l?d?u?s
?l?l?l?l?l?d?s?u
?l?l?l?l?l?s?u?d
?l?l?l?l?l?s?d?u
?l?l?l?l?u?l?d?s
?l?l?l?l?u?l?s?d
?l?l?l?l?u?u?d?s
?l?l?l?l?u?u?s?d
...
All of the above masks, will contain at least one digit, upper, and special characters.
You can also run PolicyGen in verbose mode to see the exact character and complexity statistics for each mask:
$ python policygen.py --output=masks.txt --mindigit=1 --minupper=1 --minspecial=1 --length 8 --pps=40000000000 --verbose | head
[*] Password policy:
[+] Password length: 8
[+] Minimum strength: lower: 0, upper: 1, digits: 1, special: 1
[+] Maximum strength: lower: 8, upper: 8, digits: 8, special: 8
[*] [0d|0h|0m|2s] ?l?l?l?l?l?u?d?s [l:5 u:1 d:1 s:1]
[*] [0d|0h|0m|2s] ?l?l?l?l?l?u?s?d [l:5 u:1 d:1 s:1]
[*] [0d|0h|0m|2s] ?l?l?l?l?l?d?u?s [l:5 u:1 d:1 s:1]
[*] [0d|0h|0m|2s] ?l?l?l?l?l?d?s?u [l:5 u:1 d:1 s:1]
[*] [0d|0h|0m|2s] ?l?l?l?l?l?s?u?d [l:5 u:1 d:1 s:1]
[*] [0d|0h|0m|2s] ?l?l?l?l?l?s?d?u [l:5 u:1 d:1 s:1]
...
NOTE: You can also use this program to test for compliance of existing passwords to the defined minimum password complexity policy. Simply reverse --mindigit to --maxdigit parameters in order to generate masks matching non-compliant passwords.
4.0 Conclusion
--------------
While this guide introduces a large number of methods to analyze passwords and generate masks, there is a number of tricks that you can discover to truly master the art of password cracking.
Feel free to contact me with bug reports, suggestions, as well as creative use of the tool here: iphelix@gmail.com.
Happy cracking!
-iphelix