@@ -0,0 +1,241 @@ | |||
# Here are some examples of DYNAMIC. | |||
# Please refer to ./doc/DYNAMIC for documentation on how to set these up. | |||
# Format names up to dynamic_999 are reserved for builtin functions. | |||
#################################################################### | |||
# Simple DYNAMIC type for md5($p)^^4 (i.e. 4 steps of md5 recursively) | |||
#################################################################### | |||
[List.Generic:dynamic_1001] | |||
# expression shown will be the string: dynamic_1001 md5(md5(md5(md5($p)))) | |||
Expression=md5(md5(md5(md5($p)))) | |||
Flag=MGF_KEYS_INPUT | |||
# here is the optimized 'script' to perform the md5 4 times on itself. | |||
Func=DynamicFunc__crypt_md5 | |||
Func=DynamicFunc__clean_input2 | |||
Func=DynamicFunc__append_from_last_output_to_input2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt_md5_in2_to_out1 | |||
Test=$dynamic_1001$57200e13b490d4ae47d5e19be026b057:test1 | |||
Test=$dynamic_1001$c6cc44f9e7fb7efcde62ba2e627a49c6:thatsworking | |||
Test=$dynamic_1001$0ae9549604e539a249c1fa9f5e5fb73b:test3 | |||
#################################################################### | |||
# Simple DYNAMIC type for md5($p)^^5 (i.e. 5 steps of md5 recursively) | |||
#################################################################### | |||
[List.Generic:dynamic_1002] | |||
# expression shown will be the string: dynamic_1002 md5(md5(md5(md5(md5($p))))) | |||
Expression=md5(md5(md5(md5(md5($p))))) | |||
Flag=MGF_KEYS_INPUT | |||
# here is the optimized 'script' to perform the md5 5 times on itself. | |||
Func=DynamicFunc__crypt_md5 | |||
Func=DynamicFunc__clean_input2 | |||
Func=DynamicFunc__append_from_last_output_to_input2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt_md5_in2_to_out1 | |||
# These are test strings for this format. | |||
Test=$dynamic_1002$25de8cd0b0cf69c5b5bc19c8ee64adab:test1 | |||
Test=$dynamic_1002$a0b535420ea47849f7c2cc09a3ad0ac3:thatsworking | |||
Test=$dynamic_1002$4cb029bd5b4ef79f785ca685caf17bf8:test3 | |||
#################################################################### | |||
# Simple DYNAMIC type for md5(md5($p).md5($p)) | |||
#################################################################### | |||
[List.Generic:dynamic_1003] | |||
# expression shown will be the string: dynamic_1003 md5(md5($p).md5($p)) | |||
Expression=md5(md5($p).md5($p)) | |||
# NOTE, this format does NOT work on SSE2. It requires a md5() of a 64 byte string. | |||
# SSE (or MMX) is limtited to 54 byte max password, due to 'enhancements' | |||
# Thus, we need a non-sse2 safe flag. | |||
Flag=MGF_NOTSSE2Safe | |||
Flag=MGF_KEYS_INPUT | |||
Flag=MGF_FULL_CLEAN_REQUIRED | |||
# here is the optimized 'script' to perform hash 'like' IPB but salt replaced with password. | |||
Func=DynamicFunc__crypt_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output_to_input2_as_base16 | |||
Func=DynamicFunc__append_from_last_output_to_input2_as_base16 | |||
Func=DynamicFunc__crypt_md5_in2_to_out1 | |||
# These are test strings for this format. | |||
Test=$dynamic_1003$478b10974f15e7295883224fd286ccba:test1 | |||
Test=$dynamic_1003$18a59101e6c6fb38260d542a394ecb22:thatsworking | |||
Test=$dynamic_1003$630b01b68b6db6fd43a751f8147d1faf:test3 | |||
#################################################################### | |||
# Simple DYNAMIC type for md5($p)^^6 (i.e. 6 steps of md5 recursively) | |||
#################################################################### | |||
[List.Generic:dynamic_1004] | |||
# expression shown will be the string: dynamic_1004 md5(md5(md5(md5(md5(md5($p)))))) | |||
Expression=md5(md5(md5(md5(md5(md5($p)))))) | |||
Flag=MGF_KEYS_INPUT | |||
# here is the optimized 'script' to perform the md5 6 times on itself. | |||
Func=DynamicFunc__crypt_md5 | |||
Func=DynamicFunc__clean_input2 | |||
Func=DynamicFunc__append_from_last_output_to_input2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt_md5_in2_to_out1 | |||
# These are test strings for this format. | |||
Test=$dynamic_1004$de1b991dd27fb9813e88b957a455dccd:test1 | |||
Test=$dynamic_1004$6a62cd3c4d81139f61fb2553cdef0dc7:thatsworking | |||
Test=$dynamic_1004$a977990e521c5d1d17c6d65fdf2681b4:test3 | |||
#################################################################### | |||
# Simple DYNAMIC type for md5($p)^^7 (i.e. 7 steps of md5 recursively) | |||
#################################################################### | |||
[List.Generic:dynamic_1005] | |||
# expression shown will be the string: dynamic_1005 md5(md5(md5(md5(md5(md5(md5($p))))))) | |||
Expression=md5(md5(md5(md5(md5(md5(md5($p))))))) | |||
Flag=MGF_KEYS_INPUT | |||
# here is the optimized 'script' to perform the md5 7 times on itself. | |||
Func=DynamicFunc__crypt_md5 | |||
Func=DynamicFunc__clean_input2 | |||
Func=DynamicFunc__append_from_last_output_to_input2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt_md5_in2_to_out1 | |||
# These are test strings for this format. | |||
Test=$dynamic_1005$784c527d0d92873ff9c0773e1c35621d:test1 | |||
Test=$dynamic_1005$efcbbe6331caecf0e7f40160e65aadcc:thatsworking | |||
Test=$dynamic_1005$abb8bdd2c6ac2dfea2b2af6f5aed5446:test3 | |||
#################################################################### | |||
# Simple DYNAMIC type for md5($p)^^8 (i.e. 8 steps of md5 recursively) | |||
#################################################################### | |||
[List.Generic:dynamic_1006] | |||
# expression shown will be the string: dynamic_1006 md5(md5(md5(md5(md5(md5(md5(md5($p)))))))) | |||
Expression=md5(md5(md5(md5(md5(md5(md5(md5($p)))))))) | |||
Flag=MGF_KEYS_INPUT | |||
# here is the optimized 'script' to perform the md5 8 times on itself. | |||
Func=DynamicFunc__crypt_md5 | |||
Func=DynamicFunc__clean_input2 | |||
Func=DynamicFunc__append_from_last_output_to_input2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt2_md5 | |||
Func=DynamicFunc__clean_input2_kwik | |||
Func=DynamicFunc__append_from_last_output2_as_base16 | |||
Func=DynamicFunc__crypt_md5_in2_to_out1 | |||
# These are test strings for this format. | |||
Test=$dynamic_1006$1ec1f32398f64cab51183f63630eceea:test1 | |||
Test=$dynamic_1006$f66b339ac21d6fd6af216f2b70aab2c9:thatsworking | |||
Test=$dynamic_1006$e9d38522b5eeec753332e576e2e0fe5d:test3 | |||
#################################################################### | |||
# Simple DYNAMIC type for vBulletin md5(md5($p).$s) Included here to 'exercise' the script parser | |||
#################################################################### | |||
[List.Generic:dynamic_1007] | |||
# expression shown will be the string: dynamic_1007 md5(md5($p).$s) [vBulletin] | |||
Expression=md5(md5($p).$s) (vBulletin) | |||
# Flag needed here, is Salt. There is no 'fixed' saltlen. | |||
Flag=MGF_SALTED | |||
Flag=MGF_KEYS_BASE16_IN1 | |||
# vBulletin has a 'fixed' 3 byte salt, so list the fixed size | |||
SaltLen=3 | |||
# here is the optimized 'script' to perform vBulletin hash | |||
Func=DynamicFunc__set_input_len_32 | |||
Func=DynamicFunc__append_salt | |||
Func=DynamicFunc__crypt_md5 | |||
Test=$dynamic_1007$daa61d77e218e42060c2fa198ac1feaf$SXB:test1 | |||
Test=$dynamic_1007$de56b00bb15d6db79204bd44383469bc$T &:thatsworking | |||
Test=$dynamic_1007$fb685c6f469f6e549c85e4c1fb5a65a6$HEX$5C483A:test3 | |||
#################################################################### | |||
# Dynamic type for algorithm used in RADIUS User-Password attrinute md5($p.$s) | |||
#################################################################### | |||
[List.Generic:dynamic_1008] | |||
# expression shown will be this string: | |||
Expression=md5($p.$s) (RADIUS User-Password) | |||
# Flag needed here, is Salt | |||
Flag=MGF_SALTED | |||
# The salt has a fixed length of 16 bytes | |||
Saltlen=16 | |||
Func=DynamicFunc__clean_input | |||
Func=DynamicFunc__append_keys | |||
Func=DynamicFunc__append_salt | |||
Func=DynamicFunc__crypt_md5 | |||
Test=$dynamic_1008$b962b0d40fc9111ce5f8efab424bad73$NormalSaltNormal:secret | |||
Test=$dynamic_1008$8bfccd9d67ec0bcdc38e9ae3c19a2903$FinishingwitHEX$:secret | |||
Test=$dynamic_1008$bf239357f3aa95508a53fe41b7e5f2e3$inthem$HEXiddle6:secret | |||
# unfortunately, these next 2 have embedded NULLs, so at this time they have been removed. | |||
# later we will get dynamic working with these also. | |||
#Test=$dynamic_1008$7fe3c4d1bf2ac68e94ee9f2bf75b9601$HEX$00000000000000000000000000000000:secret | |||
#Test=$dynamic_1008$658bbf9f04538d6bede09a4a52a77504$HEX$626c6168003637383930313233343536:secret | |||
###################################################################### | |||
# Dynamic Type for algorithm used in RADIUS Responses md5($s.$p) | |||
###################################################################### | |||
[List.Generic:dynamic_1009] | |||
Expression=md5($s.$p) (RADIUS Responses) | |||
Flag=MGF_SALTED | |||
Saltlen=-16 | |||
Func=DynamicFunc__clean_input | |||
Func=DynamicFunc__append_salt | |||
Func=DynamicFunc__append_keys | |||
Func=DynamicFunc__crypt_md5 | |||
Test=$dynamic_1009$0b9b9fdf75fc79d85c5b69aa1de26288$Salt:test1 | |||
###################################################################### | |||
# Dynamic Type for algorithm used in RAdmin v2.x Responses md5($p.NULL-to-100-bytes) | |||
# v2, where keys are in input, and set_input_len_100 'cleans' up if needed. | |||
###################################################################### | |||
[List.Generic:dynamic_1010] | |||
Expression=md5($p null_padded_to_len_100) RAdmin v2.x MD5 | |||
Flag=MGF_NOTSSE2Safe | |||
Flag=MGF_KEYS_INPUT | |||
Func=DynamicFunc__set_input_len_100 | |||
Func=DynamicFunc__crypt_md5 | |||
Test=$dynamic_1010$B137F09CF92F465CABCA06AB1B283C1F:lastwolf | |||
Test=$dynamic_1010$14e897b1a9354f875df51047bb1a0765:podebradka | |||
Test=$dynamic_1010$02ba5e187e2589be6f80da0046aa7e3c:12345678 | |||
Test=$dynamic_1010$b4e13c7149ebde51e510959f30319ac7:firebaLL | |||
Test=$dynamic_1010$3d2c8cae4621edf8abb081408569482b:yamaha12345 | |||
Test=$dynamic_1010$60cb8e411b02c10ecc3c98e29e830de8:xplicit |
@@ -0,0 +1,78 @@ | |||
#!/usr/bin/ruby -w | |||
require 'getoptlong' | |||
def help | |||
puts "Usage: #{$0} [options]" | |||
puts "\t-h --help\t\tthis help." | |||
puts "\t-f --file\t\toutput file." | |||
puts "\t-n --num\t\tcharset: 0123456789" | |||
puts "\t-a --alpha\t\tcharset: abcdefghijklmnopqrstuvwxyz" | |||
puts "\t-A --alphamaj\t\tcharset: ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
puts "\t-l --alphanum\t\tcharset: alpha + num" | |||
puts "\t-l --alphanummaj\tcharset: alpha + alphamaj + num" | |||
puts "\t-s --all\t\tcharset: alpha + alphamaj + num + !@#$+=.*" | |||
puts "\t-c --custom" | |||
puts "\nExample:\n" | |||
puts "#{$0} -f stats -s" | |||
puts "#{$0} -f stats -c \"0123abc+=\"" | |||
exit | |||
end | |||
ch_alpha = 'abcdefghijklmnopqrstuvwxyz' | |||
ch_num = '0123456789' | |||
ch_sp = '!@#$+=.*' | |||
opts = GetoptLong.new( | |||
[ '--help', '-h', GetoptLong::NO_ARGUMENT ], | |||
[ '--file', '-f', GetoptLong::OPTIONAL_ARGUMENT], | |||
[ '--all', '-s', GetoptLong::NO_ARGUMENT], | |||
[ '--num', '-n', GetoptLong::NO_ARGUMENT], | |||
[ '--alpha', '-a', GetoptLong::NO_ARGUMENT ], | |||
[ '--alphamaj', '-A', GetoptLong::NO_ARGUMENT ], | |||
[ '--alphanum', '-l', GetoptLong::NO_ARGUMENT ], | |||
[ '--alphanummaj', '-L', GetoptLong::NO_ARGUMENT ], | |||
[ '--custom', '-c', GetoptLong::OPTIONAL_ARGUMENT ] | |||
) | |||
charset = nil | |||
filename = "stats_out" | |||
opts.each do |opt, arg| | |||
case opt | |||
when '--help' | |||
help | |||
when '--file' | |||
filename = arg | |||
when '--num' | |||
charset = ch_num | |||
when '--alpha' | |||
charset = ch_alpha | |||
when '--alphamaj' | |||
charset = ch_alpha.capitalize | |||
when '--alphanum' | |||
charset = ch_alpha + ch_num | |||
when '--alphanummaj' | |||
charset = ch_alpha.capitalize + ch_num | |||
when '--all' | |||
charset = ch_alpha + ch_alpha.capitalize + ch_num + ch_sp | |||
when '--custom' | |||
charset = arg | |||
end | |||
end | |||
if charset == nil | |||
help | |||
end | |||
fstat = File.open(filename, "w") | |||
charset.each_byte do |c| | |||
fstat.write("1=proba1[#{c.to_s}]\n") | |||
charset.each_byte do |tmp| | |||
fstat.write("1=proba2[#{c.to_s}*256+#{tmp.to_s}]\n") | |||
end | |||
end | |||
fstat.close | |||
@@ -0,0 +1,134 @@ | |||
#!/usr/bin/env python | |||
################################################################## | |||
# Filename: netscreen.py | |||
# | |||
# Please note this script will now run in Python version 3.x | |||
# | |||
# This script will generate a netscreen formatted password | |||
# | |||
# This program requires two command line arguments, and works in two modes: | |||
# Mode 1: | |||
# The first argument is a username | |||
# The second argument is a plaintext password | |||
# Mode 2: | |||
# The first argument is -f to indicate reading usernames and passwords from a file | |||
# The second argument is the filename to read | |||
# | |||
# The input file should have one of the following formats (a "," or ":" separator): | |||
# <username>,<plain-text-password> | |||
# or | |||
# <username>:<plain-text-password> | |||
# | |||
# (Don't put a "space" after the separator, unless it is part of the password) | |||
# | |||
# Example input file: | |||
# admin,netscreen | |||
# cisco:cisco | |||
# robert,harris | |||
# | |||
# Output will be the username and hashed password in John the Ripper format | |||
# If reading usernames and passwords from a file, the output file name will be: netscreen-JtR-output.txt | |||
# If the file netscreen-JtR-output.txt exists, it will be overwritten. | |||
# | |||
# Version 2.04 | |||
# Updated on September 13, 2010 by Robert B. Harris from VA and Brad Tilley | |||
# Updated to now run in Python v3.x (still works in Python 2.x) | |||
# Additional separator for the input file. It can now have the new separator ":" (or use the old one ",") | |||
# Now correctly handles a separator ("," or ":") in the password field when reading from a file. | |||
# Updated help text in script | |||
# | |||
# Version 2.01 | |||
# Updated on August 30, 2010 by Robert B. Harris from VA | |||
# Very minor changes, removed tab, noted it won't run in python 3.x | |||
# | |||
# Version 2.0 | |||
# Updated on August 12, 2010 by Robert B. Harris from VA | |||
# Updated to use the hashlib library | |||
# Updated to print help text if both input arguments are missing | |||
# Updated to optionally read from a file | |||
# | |||
################################################################## | |||
import sys | |||
def net(user, password): | |||
b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | |||
middle = "Administration Tools" | |||
s = "%s:%s:%s" % (user, middle, password) | |||
# For versions of Python 2.5 and older | |||
if sys.version_info[0] == 2 and sys.version_info[1] < 6: | |||
import md5 | |||
m = md5.new(s).digest() | |||
else: | |||
import hashlib | |||
m = hashlib.md5(s.encode('latin_1')).digest() | |||
narray = [] | |||
for i in range(8): | |||
if sys.version_info[0] == 2: | |||
n1 = ord(m[2*i]) | |||
n2 = ord(m[2*i+1]) | |||
narray.append( (n1<<8 & 0xff00) | (n2 & 0xff) ) | |||
if sys.version_info[0] == 3: | |||
n1 = ord(chr(m[2*i])) | |||
n2 = ord(chr(m[2*i+1])) | |||
narray.append( (n1<<8 & 0xff00) | (n2 & 0xff) ) | |||
res = "" | |||
for i in narray: | |||
p1 = i >> 12 & 0xf | |||
p2 = i >> 6 & 0x3f | |||
p3 = i & 0x3f | |||
res = res + b64[p1] + b64[p2] + b64[p3] | |||
for c, n in zip("nrcstn", [0, 6, 12, 17, 23, 29]): | |||
res = res[:n] + c + res[n:] | |||
return res | |||
if __name__ == '__main__': | |||
if len(sys.argv) == 3: | |||
if (sys.argv[1])== "-f": # If true, reading from a file | |||
in_file = (sys.argv[2]) # 2nd commandline arg is the filename to read from | |||
input_file = open( in_file, 'r') | |||
output_file = open ("netscreen-JtR-output.txt" , 'w') | |||
import re | |||
for line in input_file: | |||
data=line.strip('\n') | |||
if re.search(',',line): | |||
data=data.split(',',1) # line contains , | |||
else: | |||
if re.search(':',line): | |||
data=data.split(':',1) # line contains : | |||
else: | |||
print ("\n\n\n") | |||
print ("Error in input file.") | |||
print ("The input file must have either a \",\" or \":\" separator on each line.") | |||
print ("Also it should not contain any blank lines. Please correct the input file.") | |||
break | |||
username = data[0] | |||
password = data[1] | |||
ciphertext = net(username,password) | |||
output_file.write ("%s:%s$%s" % (username,username,ciphertext)) | |||
output_file.write ("\n") | |||
input_file.close() | |||
print("\nThe output file has been created.") | |||
output_file.close() | |||
else: # We are not reading from a file | |||
username = sys.argv[1] | |||
password = sys.argv[2] | |||
ciphertext = net(username,password) | |||
print(("%s:%s$%s" % (username,username,ciphertext))) | |||
else: # User did not input the required two commandline arguments | |||
print("\n\n") | |||
print("This program requires two commandline arguments:") | |||
print("The first argument is a username, or -f to indicate reading from a file.") | |||
print("The second argument is a plaintext password, or the name of the file to read from.") | |||
print("See the additional text at the beginning of this script for more details.\n") | |||
print("Output will be the username and the (Netscreen algorithm based) hashed password, in John the Ripper format. \n\n") | |||
print("Example") | |||
print("Input: netscreen.py admin netscreen") | |||
print("Output: admin:admin$nKv3LvrdAVtOcE5EcsGIpYBtniNbUn") | |||
print("(Netscreen uses the username as the salt)") |
@@ -0,0 +1,281 @@ | |||
#!/usr/bin/perl -w | |||
# | |||
# Original code believed to be "(c) x7d8 sap loverz, public domain" (as noted in | |||
# sapB_fmt_plug.c). Also Copyright (c) 2011, 2012 magnum, and hereby released to | |||
# the general public under the following terms: Redistribution and use in | |||
# source and binary forms, with or without modification, are permitted. | |||
# | |||
# This perl script converts password hashes downloaded from SAP systems | |||
# into a format suitable for John the Ripper (written to stdout). | |||
# | |||
# Usage: ./sap2john.pl <input-file> [A|B|D|E|F|H] | |||
# | |||
# To read from stdin instead, use: ./sap2john.pl - [A|B|D|E|F|H] | |||
# | |||
# If you omit the optional parameter, the script generates output | |||
# for all codvn F and codvn H hashes, as well as for all the | |||
# CODVN B hashes (including hashes where the script assumes CODVN B | |||
# because the CODVN column is missing or empty). | |||
# That means, the default implementation will mix up to 3 different | |||
# hash formats into the output file, but since these formats | |||
# are not ambiguous, that is not a problem. | |||
# CODVN A, D, and E hashes will be skipped, because these hashes | |||
# are currently not supported, and because these hashes would | |||
# be considered as valid CODVN B hashes by the current sapB_fmt_plug.c. | |||
# (And, by the way, CODVN A and CODVN D are obsolete.) | |||
# | |||
# By specifying the optional parameter, you can decide which | |||
# SAP hash format will be written to stdout instead of the default | |||
# hash formats. | |||
# | |||
# To generate a suitable input file for this script, download | |||
# the SAP hashes from one of the database tables USR02, USH02, | |||
# or USRPWDHISTORY. | |||
# Download the data as a spreadsheet using SAP transaction code SE16. | |||
# Make sure to check the user settings: pick field names instead of | |||
# field descriptions as column headings. | |||
# If the SAP user names (which work as salts for CODVN A, B, D, E, F) | |||
# contain non-ascii characters, please download the data using a | |||
# single byte code page if you want to crack CODVN A or CODVN B hashes. | |||
# Download the data using utf-8 (SAP code page 4110), if you want | |||
# to crack CODVN F, CODVN E, or CODVN D hashes (or if you want to crack | |||
# CODVN H hashes. (For CODVN H, the user name ist't used as a salt | |||
# anymore.) | |||
# | |||
# CODVN G just means, the system computes and stores CODVN B and | |||
# CODVN F hashes. | |||
# (In this case, the script will create two lines of output.) | |||
# CODVN I means, the system computes and stores CODVN B, CODVN F, and | |||
# CODVN H hashes. | |||
# (In this case, the script will create three lines of output.) | |||
# | |||
# If CODVN is empty (or the column is missing), but BCODE is filled, | |||
# the script assumes that the corresponding hash is a CODVN B hash, | |||
# but for very old USH02 records (created before around 1996) | |||
# probably CODVN was in use. | |||
# | |||
# Please note that currently John the Ripper (jumbo) only supports | |||
# SAP CODVN B (--format=sapb) and CODVN F (--format=sapg) hashes. | |||
# | |||
# FIXME: should the script generate different lines of output | |||
# for the current password (e.g. uid=0, gid=$mandt[$i]) | |||
# and for older passwords (ocod1-ocod5, or USH02 | |||
# (column MODDA or MODTI exists) or USRPWDHISTORY | |||
# (column TIMESTAMP exists) | |||
# | |||
sub fill_field | |||
{ | |||
if ($_[0] == -1 || $_[0] > $#tmp) { | |||
$_[1] = ""; | |||
} | |||
else { | |||
$_[1] = $tmp[$_[0]]; | |||
$_[1] =~ s/\s*$//; | |||
} | |||
} | |||
sub write_pwdsaltedhash | |||
{ | |||
if ($hashtypes =~ /H/ && $pwdsaltedhash[$i] ne "") { | |||
print "$bname[$i]:$pwdsaltedhash[$i]\n"; | |||
} | |||
} | |||
sub write_passcode | |||
{ | |||
# FIXME: prefix hash with "sapF$", to avoid ambiguity with other hash formats? | |||
if ($hashtypes =~ /F/ && $passcode[$i] ne "0000000000000000000000000000000000000000") { | |||
print "$bname[$i]:$bname[$i]\$$passcode[$i]\n"; | |||
} | |||
} | |||
sub write_bcode | |||
{ | |||
# FIXME: prefix CODVN A/D/E(/B) hashes with "sap<CODVN>$"? | |||
$vn = $_[1]; | |||
$bc = $_[0]; | |||
if ($vn eq "" || $vn eq "G" or $vn eq "I") { $vn = "B" } | |||
if ($hashtypes =~ /$vn/) { | |||
if ($bc ne "" && $bc ne "0000000000000000") { | |||
print "$bname[$i]:$bname[$i]\$$bc\n"; | |||
} | |||
} | |||
} | |||
if ($#ARGV < 0 || $#ARGV > 1) { | |||
die "Usage: $0 <input-file> [A|B|D|E|F|H]\n"; | |||
} | |||
open INPUT_FILE, "$ARGV[0]" or die "Can't open input-file ($ARGV[0])\n"; | |||
if ($#ARGV == 1) { | |||
$hashtypes = $ARGV[1]; | |||
if ($hashtypes =~ /^[^ABDEFH]$/) { | |||
die "invalid optional parameter: \"$hashtypes\"\n"; | |||
} | |||
} | |||
else { | |||
$hashtypes = ""; | |||
} | |||
$line = ""; | |||
$count = 0; | |||
# USR01, USH02, USRPWDHISTORY | |||
#$pos_mandt = -1; | |||
$pos_bname = -1; | |||
$pos_bcode = -1; | |||
$pos_passcode = -1; | |||
$pos_pwdsaltedhash = -1; | |||
# USR02 | |||
$pos_codvn = -1; | |||
$pos_ocod1 = -1; | |||
$pos_codv1 = -1; | |||
$pos_ocod2 = -1; | |||
$pos_codv2 = -1; | |||
$pos_ocod3 = -1; | |||
$pos_codv3 = -1; | |||
$pos_ocod4 = -1; | |||
$pos_codv4 = -1; | |||
$pos_ocod5 = -1; | |||
$pos_codv5 = -1; | |||
# USH02 | |||
#$pos_modda = -1; | |||
#$pos_modti = -1; | |||
# USRPWDHISTORY | |||
#$pos_timestamp = -1; | |||
until ($line =~ /\t/) { | |||
$line=<INPUT_FILE>; | |||
$count++; | |||
} | |||
chomp($line); | |||
$line =~ s/\r//; | |||
# column names can be either left-justified or right-justified, | |||
# so let's remove spaces as well: | |||
@tmp = split(/\s*\t\s*/, $line); | |||
$columns = $#tmp; | |||
for($i = 0; $i <= $columns; $i++) { | |||
if ($tmp[$i] =~ /BNAME/) { $pos_bname = $i } | |||
# elsif ($tmp[$i] =~ /MANDT/) { $pos_mandt = $i } | |||
elsif ($tmp[$i] =~ /BCODE/) { $pos_bcode = $i } | |||
elsif ($tmp[$i] =~ /CODVN/) { $pos_codvn = $i } | |||
elsif ($tmp[$i] =~ /PASSCODE/) { $pos_passcode = $i } | |||
elsif ($tmp[$i] =~ /PWDSALTEDHASH/) { $pos_pwdsaltedhash = $i } | |||
elsif ($tmp[$i] =~ /OCOD1/) { $pos_ocod1 = $i } | |||
elsif ($tmp[$i] =~ /CODV1/) { $pos_codv1 = $i } | |||
elsif ($tmp[$i] =~ /OCOD2/) { $pos_ocod2 = $i } | |||
elsif ($tmp[$i] =~ /CODV2/) { $pos_codv2 = $i } | |||
elsif ($tmp[$i] =~ /OCOD3/) { $pos_ocod3 = $i } | |||
elsif ($tmp[$i] =~ /CODV3/) { $pos_codv3 = $i } | |||
elsif ($tmp[$i] =~ /OCOD4/) { $pos_ocod4 = $i } | |||
elsif ($tmp[$i] =~ /CODV4/) { $pos_codv4 = $i } | |||
elsif ($tmp[$i] =~ /OCOD5/) { $pos_ocod5 = $i } | |||
elsif ($tmp[$i] =~ /CODV5/) { $pos_codv5 = $i } | |||
# elsif ($tmp[$i] =~ /MODDA/) { $pos_modda = $i } | |||
# elsif ($tmp[$i] =~ /MODTI/) { $pos_modti = $i } | |||
# elsif ($tmp[$i] =~ /TIMESTAMP/) { $pos_timestamp = $i } | |||
} | |||
if (-1 == $pos_bcode && | |||
-1 == $pos_ocod1 && -1 == $pos_ocod2 && -1 == $pos_ocod3 && | |||
-1 == $pos_ocod4 && -1 == $pos_ocod5) { | |||
if (-1 == $pos_passcode && -1 == $pos_pwdsaltedhash) { | |||
die "no password hash columns found\n"; | |||
} | |||
elsif ($hashtypes eq "B" || $hashtypes eq "E" || | |||
$hashtypes eq "D" || $hashtypes eq "A") { | |||
die "CODVN B, E, D, or A requested, but column BCODE/OCODV[1-5] not found\n"; | |||
} | |||
} | |||
if (-1 == $pos_bname && -1 == $pos_pwdsaltedhash) { | |||
die "no BNAME column found, but required as salt for BCOCE and PASSCODE\n"; | |||
} | |||
if (-1 == $pos_passcode && $hashtypes =~ /F/) { | |||
die "CODVN F requested, but column PASSCODE not found\n"; | |||
} | |||
if (-1 == $pos_pwdsaltedhash && $hashtypes =~ /H/) { | |||
die "CODVN H requested, but column PWDSALTEDHASH not found\n"; | |||
} | |||
if ($hashtypes eq "") { | |||
# FIXME: Should I use ABDEFH as a default, and prefix | |||
# the hashes with sapA$, sapD$, sapD$ for codvn A/D/E? | |||
# OTOH, if user names contain non-ascii characters, | |||
# the JtR user might want to split formats requiring utf-8 input | |||
# and formats requiring iso-8859* input into different files | |||
# anyway. | |||
$hashtypes = "BFH"; | |||
} | |||
if (-1 == $pos_bname) { $hashtypes =~ s/[ABDEF]//g } | |||
if (-1 == $pos_pwdsaltedhash) { $hashtypes =~ s/H// } | |||
if ($hashtypes eq "") { | |||
die "not all required columns for requested hash types found\n"; | |||
} | |||
$rows = -1; | |||
while ($line = <INPUT_FILE>) { | |||
$count++; | |||
chomp($line); | |||
$line =~ s/\r//; | |||
@tmp = split(/\t/, $line); | |||
if ($#tmp >= 0) { | |||
$rows++; | |||
# fill_field( $pos_mandt, $mandt[$rows]); | |||
fill_field( $pos_bname, $bname[$rows]); | |||
fill_field( $pos_codvn, $codvn[$rows]); | |||
fill_field( $pos_bcode, $bcode[$rows]); | |||
fill_field( $pos_passcode, $passcode[$rows]); | |||
fill_field( $pos_pwdsaltedhash, $pwdsaltedhash[$rows]); | |||
fill_field( $pos_ocod1, $ocod1[$rows]); | |||
fill_field( $pos_codv1, $codv1[$rows]); | |||
fill_field( $pos_ocod2, $ocod2[$rows]); | |||
fill_field( $pos_codv2, $codv2[$rows]); | |||
fill_field( $pos_ocod3, $ocod3[$rows]); | |||
fill_field( $pos_codv3, $codv3[$rows]); | |||
fill_field( $pos_ocod4, $ocod4[$rows]); | |||
fill_field( $pos_codv4, $codv4[$rows]); | |||
fill_field( $pos_ocod5, $ocod5[$rows]); | |||
fill_field( $pos_codv5, $codv5[$rows]); | |||
# fill_field( $pos_modda, $modda[$rows]); | |||
# fill_field( $pos_modti, $modti[$rows]); | |||
# fill_field( $pos_timestamp, $timestamp[$rows]); | |||
} | |||
} | |||
# Should the script count the number of valid hashes found/written to stdout, | |||
# to write summary information to stderr? | |||
# | |||
#$codvn_a = 0; | |||
#$codvn_b = 0; | |||
#$codvn_d = 0; | |||
#$codvn_e = 0; | |||
#$codvn_f = 0; | |||
#$codvn_h = 0; | |||
for ($i=0; $i<=$rows; $i++) { | |||
# write BCODE first, so that hopefully codvn B (the easiest to crack | |||
# hash algorithm) will be detected first... | |||
write_bcode( $bcode[$i], $codvn[$i] ); | |||
write_bcode( $ocod1[$i], $codv1[$i] ); | |||
write_bcode( $ocod2[$i], $codv2[$i] ); | |||
write_bcode( $ocod3[$i], $codv3[$i] ); | |||
write_bcode( $ocod4[$i], $codv4[$i] ); | |||
write_bcode( $ocod5[$i], $codv5[$i] ); | |||
write_passcode( ); | |||
# even if this format is currently not supported by JtR, | |||
# it might be in future: | |||
write_pwdsaltedhash( ); | |||
} |