Implemented optimal mask generation logic.

Removed obsolete psyco module.
This commit is contained in:
iphelix 2013-07-17 23:01:14 -07:00
parent 4f84adc320
commit b1958e6cdb
2 changed files with 94 additions and 173 deletions

View File

@ -28,120 +28,104 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import csv, string import sys
import csv
import datetime import datetime
from operator import itemgetter from operator import itemgetter
from optparse import OptionParser from optparse import OptionParser
import code
VERSION = "0.0.2" VERSION = "0.0.2"
# PPS (Passwords per Second) Cracking Speed
pps = 1000000000
# Global Variables class MaskGen:
mastermasks = dict() def __init__(self):
allmasks = dict() # Masks collections with meta data
self.masks = dict()
self.lengthmasks = dict()
################################################################## self.minlength = 0
## Calculate complexity of a single mask self.maxlength = sys.maxint
##################################################################
def complexity(mask): self.maxtime = sys.maxint
self.complexity = sys.maxint
self.occurrence = 0
# PPS (Passwords per Second) Cracking Speed
self.pps = 1000000000
self.checkmask = False
self.showmasks = False
# Counter for total masks coverage
self.total_occurrence = 0
def getcomplexity(self, mask):
""" Return mask complexity. """
count = 1 count = 1
for char in mask[1:].split("?"): for char in mask[1:].split("?"):
if char == "l": count *= 26
# Loweralpha elif char == "u": count *= 26
if char == "l": elif char == "d": count *= 10
count *= 26 elif char == "s": count *= 33
else: print "[!] Error, unknown mask ?%s in a mask %s" % (char,mask)
# Upperalpha
elif char == "u":
count *= 26
# Numeric
elif char == "d":
count *= 10
# Special
elif char == "s":
count *= 33
# Unknown mask
else:
print "[!] Error, unknown mask ?%s in a mask %s" % (char,mask)
return count return count
################################################################### def loadmasks(self, filename):
## Calculate complexity of a complex mask """ Load masks and apply filters. """
################################################################### maskReader = csv.reader(open(args[0],'r'), delimiter=',', quotechar='"')
def maskcomplexity(mask):
combined_complexity = 1
for submask in mask.split():
combined_complexity *= complexity(submask)
return combined_complexity for (mask,occurrence) in maskReader:
################################################################### mask_occurrence = int(occurrence)
## Check if complex mask matches a sample mask mask_length = len(mask)/2
################################################################### mask_complexity = self.getcomplexity(mask)
def matchmask(checkmask,mask):
length = len(mask)/2
checklength = len(checkmask.split(" "))
if length == checklength: self.total_occurrence =+ mask_occurrence
masklist = mask[1:].split("?")
for i, submask in enumerate(checkmask.split(" ")):
for char in submask[1:].split("?"):
if char == masklist[i]:
break
else:
return False
else:
return True
else:
return False
# Apply filters based on occurrence, length, complexity and time
if mask_occurrence >= self.occurrence and \
mask_complexity <= self.complexity and \
mask_length <= self.maxlength and \
mask_length >= self.minlength:
################################################################### self.masks[mask] = dict()
## Combine masks self.masks[mask]['length'] = mask_length
################################################################### self.masks[mask]['occurrence'] = mask_occurrence
def genmask(mask): self.masks[mask]['complexity'] = mask_complexity
global mastermasks self.masks[mask]['time'] = mask_complexity/self.pps
length = len(mask)/2 self.masks[mask]['optindex'] = mask_complexity/mask_occurrence
if not length in mastermasks: def print_optimal_index_masks(self):
mastermasks[length] = dict() print "[*] Masks sorted by optimal index."
for i,v in enumerate(mask[1:].split("?")):
if not i in mastermasks[length]:
mastermasks[length][i] = set()
mastermasks[length][i].add("?%s" % v)
###################################################################
## Store all masks in based on length and count
###################################################################
def storemask(mask,occurrence):
global allmasks
length = len(mask)/2
if not length in allmasks:
allmasks[length] = dict()
allmasks[length][mask] = int(occurrence)
def main():
# Constants
total_occurrence = 0
sample_occurrence = 0
sample_time = 0 sample_time = 0
sample_occurrence = 0
# TODO: I want to actually see statistical analysis of masks not just based on size but also frequency and time # TODO Group by time here 1 minutes, 1 hour, 1 day, 1 month, 1 year....
# per length and per count # Group by length 1,2,3,4,5,6,7,8,9,10....
# Group by occurrence 10%, 20%, 30%, 40%, 50%....
for mask in sorted(self.masks.keys(), key=lambda mask: self.masks[mask]['optindex'], reverse=False):
time_human = "EXCEEDED" if self.masks[mask]['time'] > 60*60*24 else str(datetime.timedelta(seconds=self.masks[mask]['time']))
print "[{:<2}] {:<30} [{:>8}] [{:>7}] ".format(self.masks[mask]['length'], mask, time_human, self.masks[mask]['occurrence'])
sample_occurrence =+ self.masks[mask]['occurrence']
sample_time =+ self.masks[mask]['time']
if sample_time > self.maxtime:
print "[!] Estimated runtime exceeded."
break
print "[*] Coverage is %d%% (%d/%d)" % (sample_occurrence*100/self.total_occurrence,sample_occurrence,self.total_occurrence)
#print "[*] Total time [%dd|%dh|%dm|%ds]" % (sample_time/60/60/24,sample_time/60/60,sample_time/60,sample_time)
if __name__ == "__main__":
header = " _ \n" header = " _ \n"
header += " MaskGen 0.0.2 | |\n" header += " MaskGen %s | |\n" % VERSION
header += " _ __ __ _ ___| | _\n" header += " _ __ __ _ ___| | _\n"
header += " | '_ \ / _` |/ __| |/ /\n" header += " | '_ \ / _` |/ __| |/ /\n"
header += " | |_) | (_| | (__| < \n" header += " | |_) | (_| | (__| < \n"
@ -151,17 +135,17 @@ def main():
header += "\n" header += "\n"
parser = OptionParser("%prog [options] masksfile.csv", version="%prog "+VERSION) parser = OptionParser("%prog [options] masksfile.csv", version="%prog "+VERSION)
parser.add_option("--minlength", dest="minlength",help="Minimum password length", type="int", metavar="8") parser.add_option("--minlength", dest="minlength",help="Minimum password length", type="int", metavar="8")
parser.add_option("--maxlength", dest="maxlength",help="Maximum password length", type="int", metavar="8") parser.add_option("--maxlength", dest="maxlength",help="Maximum password length", type="int", metavar="8")
parser.add_option("--mintime", dest="mintime",help="Minimum time to crack", type="int", metavar="") parser.add_option("--maxtime", dest="maxtime",help="Maximum total time (optimized)", type="int", metavar="")
parser.add_option("--maxtime", dest="maxtime",help="Maximum time to crack", type="int", metavar="")
parser.add_option("--complexity", dest="complexity",help="maximum password complexity", type="int", metavar="") parser.add_option("--complexity", dest="complexity",help="maximum password complexity", type="int", metavar="")
parser.add_option("--occurrence", dest="occurrence",help="minimum times mask was used", type="int", metavar="") parser.add_option("--occurrence", dest="occurrence",help="minimum times mask was used", type="int", metavar="")
parser.add_option("--checkmask", dest="checkmask",help="check mask coverage", metavar="?u?l ?l ?l ?l ?l ?d") parser.add_option("--checkmask", dest="checkmask",help="check mask coverage", metavar="?u?l ?l ?l ?l ?l ?d")
parser.add_option("--showmasks", dest="showmasks",help="Show matching masks", action="store_true", default=False) parser.add_option("--showmasks", dest="showmasks",help="Show matching masks", action="store_true", default=False)
parser.add_option("--pps", dest="pps",help="Passwords per Second", type="int", default=pps, metavar="1000000000") parser.add_option("--pps", dest="pps",help="Passwords per Second", type="int", metavar="1000000000")
parser.add_option("-o", "--masksoutput", dest="masks_output",help="Save masks to a file", metavar="masks.hcmask") parser.add_option("-o", "--masksoutput", dest="masks_output",help="Save masks to a file", metavar="masks.hcmask")
parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.") parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="Don't show headers.")
(options, args) = parser.parse_args() (options, args) = parser.parse_args()
@ -173,87 +157,31 @@ def main():
parser.error("no masks file specified") parser.error("no masks file specified")
exit(1) exit(1)
# Constants
total_occurrence = 0
sample_occurrence = 0
sample_time = 0
print "[*] Analysing masks: %s" % args[0] print "[*] Analysing masks: %s" % args[0]
maskReader = csv.reader(open(args[0],'r'), delimiter=',', quotechar='"')
#headerline = maskReader.next()
# Check the coverage of a particular mask for a given set maskgen = MaskGen()
if options.checkmask:
length = len(options.checkmask.split(" "))
# Prepare master mask list for analysis if options.minlength: maskgen.minlength = options.minlength
mastermasks[length] = dict() if options.maxlength: maskgen.maxlength = options.maxlength
for i, submask in enumerate(options.checkmask.split(" ")): if options.maxtime: maskgen.maxtime = options.maxtime
mastermasks[length][i] = set() if options.complexity: maskgen.complexity = options.complexity
for char in submask[1:].split("?"): if options.occurrence: maskgen.occurrence = options.occurrence
mastermasks[length][i].add("?%s" % char)
for (mask,occurrence) in maskReader: if options.pps: maskgen.pps = options.pps
total_occurrence += int(occurrence)
if matchmask(options.checkmask,mask):
sample_occurrence += int(occurrence)
storemask(mask,occurrence)
# Generate masks from a given set if options.checkmask: maskgen.checkmask = options.checkmask
else: if options.showmasks: maskgen.showmasks = options.showmask
for (mask,occurrence) in maskReader:
total_occurrence += int(occurrence)
if (not options.occurrence or int(occurrence) >= options.occurrence) and \ # Load masks
(not options.maxlength or len(mask)/2 <= options.maxlength) and \ maskgen.loadmasks(args[0])
(not options.minlength or len(mask)/2 >= options.minlength) and \ maskgen.print_optimal_index_masks()
(not options.complexity or complexity(mask) <= options.complexity) and \
(not options.maxtime or complexity(mask)/options.pps <= options.maxtime) and \
(not options.mintime or complexity(mask)/options.pps >= options.mintime):
genmask(mask)
storemask(mask,occurrence)
sample_occurrence += int(occurrence)
####################################################################################
## Analysis
####################################################################################
if options.masks_output:
f = open(options.masks_output,'w+')
for length,lengthmask in sorted(mastermasks.iteritems()):
maskstring = ""
for position,maskset in lengthmask.iteritems():
maskstring += "%s " % string.join(maskset,"")
length_occurrence = 0
combined_mask_time = 0
# Calculate occurrence and time complexity of component masks
# for each password length.
for mask, occurrence in allmasks[length].iteritems():
length_occurrence += int(occurrence)
combined_mask_time += complexity(mask)/options.pps
sample_time += combined_mask_time
time_string = "Don't bother."
if not combined_mask_time > 3784320000:
time_string = str(datetime.timedelta(seconds=combined_mask_time))
print "[*] [%d] [%d/%d] [%.02f] [%s]" % (length, length_occurrence, total_occurrence, length_occurrence*100/total_occurrence, time_string)
if options.showmasks:
for mask,mask_occurrence in sorted(allmasks[length].iteritems(),key=itemgetter(1),reverse=True):
mask_time = complexity(mask)/options.pps
print " [%d] [%d/%d] [%.02f] [%.02f] [%dd|%dh|%dm|%ds] %s" % (length, mask_occurrence, length_occurrence, mask_occurrence*100/length_occurrence, mask_occurrence*100/total_occurrence,mask_time/60/60/24, mask_time/60/60, mask_time/60, mask_time,mask)
if options.masks_output:
for mask,mask_occurrence in sorted(allmasks[length].iteritems(),key=itemgetter(1),reverse=True):
f.write("%s\n" % mask)
print "[*] Coverage is %%%d (%d/%d)" % (sample_occurrence*100/total_occurrence,sample_occurrence,total_occurrence)
print "[*] Total time [%dd|%dh|%dm|%ds]" % (sample_time/60/60/24,sample_time/60/60,sample_time/60,sample_time)
if __name__ == "__main__":
main()

View File

@ -34,13 +34,6 @@ from optparse import OptionParser
VERSION = "0.0.2" VERSION = "0.0.2"
try:
import psyco
psyco.full()
print "[*] Using Psyco to accelerate parsing."
except ImportError:
print "[?] Psyco is not available. Install Psyco on 32-bit systems for faster parsing."
password_counter = 0 password_counter = 0
# Constants # Constants