Skip to content

Instantly share code, notes, and snippets.

@x684867
Created August 4, 2013 16:36
Show Gist options
  • Select an option

  • Save x684867/6150915 to your computer and use it in GitHub Desktop.

Select an option

Save x684867/6150915 to your computer and use it in GitHub Desktop.
tableBuilder.rb
#!/usr/bin/env ruby
#
# Universal Hash Table calculator
# (c) 2013 Sam Caldwell. All Rights Reserved.
#
# ------------------------------------------------------------------------------
# *** LIMITED INTERNAL-USE LICENSE ***
# Script intended and authorized solely for research and education purposes
# to identify the weaknesses of current cryptographic practices in place on
# many systems so that the system operators can appreciate the use of best
# practices. Use of this script for illegal or harmful purposes is prohibited
# and may result in criminal or civil penalties. The user is forewarned that
# the author *will* cooperate with law enforcement in his jurisdiction in the
# non-political prosecution of persons believed to have used this script or
# derived work to harm any person or organization or to otherwise violate the
# laws of the United States or any state thereof.
#
# Use of this script indicates an acknowledgement of and consent to these terms
# and conditions and an agreement to indemnify and hold harmless the author in
# any and all cases which may arise from your use hereof directly or indirectly.
# ------------------------------------------------------------------------------
# Theory of operation:
#
# 1. Parse arguments (params array)
# 2. Create the hashes table directory base_dir
# 3. Calculate Hashes
# a. create the starting keyspace={start_char,start_char...} of width elements.
# b. hash keyspace
# c. increment the keyspace by recursively incrementing each
# element as the 1 is carried.
#
require 'optparse'
require 'fileutils'
require 'digest/md5'
require 'digest/sha1'
require 'digest/sha2'
require 'logger'
GC.enable
GC.start
def throw_missing_param()
puts "Missing expected operand. Use -h or --help for USAGE information."
abort
end
def throw_bad_keyspace()
puts "stop_char must be greater than start_char"
puts "This value must also be a valid decimal ASCII code."
puts "Use -h or --help for more information."
abort
end
def throw_bad_width()
puts "width must be greater than zero (0)"
puts "Use -h or --help for more information."
abort
end
def show_usage()
puts "------------------------------------------------------------------------------"
puts " Universal Hash Table calculator"
puts " (c) 2013 Sam Caldwell. All Rights Reserved."
puts "------------------------------------------------------------------------------"
puts "USAGE:"
puts " tableBuilder.rb base_dir start_char stop_char width max_thread_count"
puts " "
puts " base_dir (string) : base directory path"
puts " start_char (int) : starting ASCII character value"
puts " stop_char (int) : ending ASCII character value"
puts " key_width (int) : width of the keyspace to be analyzed"
puts " max_thread_count(int) : maximum number of threads allowed"
puts "------------------------------------------------------------------------------"
puts "EXAMPLE:"
puts " tableBuilder.rb ./hashes 32 127 16 1000"
puts " "
puts " This call would generate a table of hashes at ./hashes/... with a"
puts " 1-16 character keyspace of characters ranging from ASCII 32 to"
puts " ASCII 127 using MD5, SHA1, SHA256, SHA384 and SHA512 algorithms."
puts " "
puts " Each cleartext would be stored in a directory tree made up of its"
puts " hexadecimal hash digits in a file named for the algorithm used, as"
puts " follows:"
puts " "
puts " ./hashes/d/8/e/8/f/c/a/.../MD5.txt"
puts " "
puts " This means that given a hash, one can rewrite the hash as the"
puts " hash and filename of the file where its cleartext counterpart"
puts " is stored."
puts " "
puts " RELATED WORK:"
puts " "
puts " A subsequent project could be created down the road to create a"
puts " clear-text to cipher-text cross-reference by symlinking to these"
puts " hash tables. This would have little lookup power other than being"
puts " able to identify and inventory the passwords compromised by this"
puts " system."
puts "------------------------------------------------------------------------------"
exit
end
def show_welcome(baseDir,startChar,stopChar,width,max_thread_count)
puts "-- -- -- -- -- -- -- -- -- -- -- -- -- --"
puts "Starting..."
puts " base_dir: " + baseDir
puts " start_char: " + startChar.to_s + " (ASCII)"
puts " stop_char: " + stopChar.to_s + " (ASCII)"
puts " width: " + width.to_s + " chars"
puts " max_thread_count: " + max_thread_count.to_s + " threads"
puts "-- -- -- -- -- -- -- -- -- -- -- -- -- --"
end
def throw_insufficient_threads()
puts "max_thread_count is too low."
puts "use -h or --help for usage information."
abort
end
def min_thread_count(stop_char,start_char)
return (stop_char - start_char + 10)
end
def join_keyspace(keyspace)
s=""
#join they keyspace as characters.
keyspace.each do |e|
s=s+e.chr
end
return s
end
def hash2dir(base_dir,key_hash)
#strip any trailing slash
hash_dir=base_dir.chomp('/')
#parse each char of the hash to create directory structure.
key_hash.each_char do |n|
hash_dir=hash_dir+"/"+n
end
#return hash-directory path without trailing / char.
return hash_dir
end
def pretty_join(i)
s=""
i.each do | e |
s=s+" "+e.to_s(16) unless(e == 0)
end
return s
end
def write_hash_file(base_dir,algorithm,key,hashstring)
#stringify the keyspace array as a string of ASCII characters.
key_string=pretty_join(key).to_s
#Convert the hash into a directory path if it doesn't exist.
#table_path=File.dirname(hash2dir(base_dir,hashstring.to_s))
#FileUtils.mkdir_p(table_path) if not File.directory?(table_path)
# Store the cleartext key into a file named for the current algorithm
# if no such file exists....
#cleartext_file=table_path+"/"+algorithm+".txt"
cleartext_file=base_dir+"/md5.txt"
#output_file=File.new(cleartext_file,'w') if not File.exists?(cleartext_file)
output_file=File.new(cleartext_file,'a')
if(output_file)
output_file.puts(hashstring.to_s+" "+key_string)
puts "wrote "+cleartext_file+" with cleartext string "+key_string
output_file.close
return true
else
puts "skipped existing hash file "+cleartext_file+" with cleartext string "+key_string
return false
end
end
def check_keyspace_end(keyspace,width,stop_char)
check_flag=false
width.times do |i|
check_flag=true if(keyspace[i]==stop_char)
end
return check_flag
end
def increment_keyspace(keyspace,index,start_char,stop_char,width)
if(index > width)
return check_keyspace_end(keyspace,width,stop_char)
else
if(keyspace[index]>=stop_char)
keyspace[index]=0
return increment_keyspace(keyspace,index+1,start_char,stop_char,width)
else
if((keyspace[index]==0) and (start_char>0))
keyspace[index]=start_char
else
keyspace[index]=keyspace[index]+1
end
return false
end
end
end
def initialize_keyspace(slice,width)
#
# We are going to initialize each keyspace with
# the 0-th element being the slice character value.
# This means that we can multithread the application.
# workload.
#
keyspace=Array.new(width)
width.times do |i|
keyspace[i]=0
end
keyspace[0]=slice
return keyspace
end
def calculateHashes(dir_name,keyspace,start_char,stop_char,width,max_thread_count,log)
hash_count=0
write_count=0
is_complete=false
child_threads=Array.new
until is_complete do
log.info "TC:"+Thread.list.count.to_s+" h="+hash_count.to_s+" w="+write_count.to_s+" ks="+keyspace.to_s
throttle_thread_count(max_thread_count)
if(Thread.list.count <= max_thread_count)
child_threads[Thread.list.count]=Thread.new do
if(write_hash_file(
dir_name,
"MD5",
keyspace,
Digest::MD5.hexdigest(join_keyspace(keyspace))
)
)
write_count=write_count+1
end
hash_count=hash_count+1
end
end
#if(Thread.list.count <= max_thread_count)
# child_threads[Thread.list.count]=Thread.new do
# write_hash_file(
# dir_name,
# "SHA1",
# keyspace,
# Digest::SHA1.hexdigest(join_keyspace(keyspace))
# )
# end
#end
#if(Thread.list.count <= max_thread_count)
#threads[Thread.list.count]=Thread.new do
# write_hash_file(
# dir_name,
# "SHA256",
# keyspace,
# Digest::SHA256.hexdigest(join_keyspace(keyspace))
# )
#end
#end
#if(Thread.list.count <= max_thread_count)
#threads[Thread.list.count]=Thread.new do
# write_hash_file(
# dir_name,
# "SHA384",
# keyspace,
# Digest::SHA384.hexdigest(join_keyspace(keyspace))
# )
#end
#end
#if(Thread.list.count <= max_thread_count)
#threads[Thread.list.count]=Thread.new do
# write_hash_file(
# dir_name,
# "SHA512",
# keyspace,
# Digest::SHA512.hexdigest(join_keyspace(keyspace))
# )
#end
#end
is_complete=increment_keyspace(keyspace,0,start_char,stop_char,width)
end
puts "calculateHashes() done."
end
def throttle_thread_count(max_thread_count)
while(Thread.list.count > max_thread_count)
log.info "Cannot start any more threads...NEED MORE COWBELL! Sleeping 5 seconds."
puts "Cannot start any more threads...NEED MORE COWBELL! Sleeping 5 seconds."
sleep(5)
end
end
#-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
# MAIN ROUTINE
#-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
#-- -- -- -- -- -- -- -- -- -- -- -- -- --
# SETUP SYSLOG...
#-- -- -- -- -- -- -- -- -- -- -- -- -- --
log=Logger.new 'tableBuilder'
log.info 'tableBuilder log initialized.'
#-- -- -- -- -- -- -- -- -- -- -- -- -- --
# PARSE ARGUMENTS...
#-- -- -- -- -- -- -- -- -- -- -- -- -- --
show_usage() if((ARGV[0]=="-h") || (ARGV[0]=="--help"))
(base_dir=ARGV[0]) or throw_missing_param()
(start_char=ARGV[1].to_i) or throw_missing_param()
(stop_char=ARGV[2].to_i) or throw_missing_param()
(width=ARGV[3].to_i) or throw_missing_param()
(max_thread_count=ARGV[4].to_i) or throw_missing_param()
show_welcome(base_dir,start_char,stop_char,width,max_thread_count)
#-- -- -- -- -- -- -- -- -- -- -- -- -- --
#-- -- -- -- -- -- -- -- -- -- -- -- -- --
# VALIDATE INPUTS...
#-- -- -- -- -- -- -- -- -- -- -- -- -- --
throw_bad_keyspace() if(stop_char <= start_char)
throw_bad_width() if(width<=0)
throw_insufficient_threads() if(max_thread_count < min_thread_count(stop_char,start_char))
#-- -- -- -- -- -- -- -- -- -- -- -- -- --
puts "Initializing Keyspaces..."
calculateHashes(
base_dir,
initialize_keyspace(0,width),
start_char,
stop_char,
width,
max_thread_count,
log
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment