Created
June 30, 2016 06:02
-
-
Save HarmJ0y/116fa1b559372804877e604d7d367bbc to your computer and use it in GitHub Desktop.
Python port of John the Ripper's keepass2john - extracts a HashCat/john crackable hash from KeePass 1.x/2.X databases
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# Python port of keepass2john from the John the Ripper suite (http://www.openwall.com/john/) | |
# ./keepass2john.c was written by Dhiru Kholia <dhiru.kholia at gmail.com> in March of 2012 | |
# ./keepass2john.c was released under the GNU General Public License | |
# source keepass2john.c source code from: http://fossies.org/linux/john/src/keepass2john.c | |
# | |
# Python port by @harmj0y, GNU General Public License | |
# | |
# TODO: handle keyfiles, test file inlining for 1.X databases, database version sanity check for 1.X | |
# | |
import sys | |
import os | |
import struct | |
from binascii import hexlify | |
def process_1x_database(data, databaseName, maxInlineSize=1024): | |
index = 8 | |
algorithm = -1 | |
encFlag = struct.unpack("<L", data[index:index+4])[0] | |
index += 4 | |
if (encFlag & 2 == 2): | |
# AES | |
algorithm = 0 | |
elif (enc_flag & 8): | |
# Twofish | |
algorithm = 1 | |
else: | |
print "Unsupported file encryption!" | |
return | |
# TODO: keyfile processing | |
# TODO: database version checking | |
version = hexlify(data[index:index+4]) | |
index += 4 | |
finalRandomseed = hexlify(data[index:index+16]) | |
index += 16 | |
encIV = hexlify(data[index:index+16]) | |
index += 16 | |
numGroups = struct.unpack("<L", data[index:index+4])[0] | |
index += 4 | |
numEntries = struct.unpack("<L", data[index:index+4])[0] | |
index += 4 | |
contentsHash = hexlify(data[index:index+32]) | |
index += 32 | |
transfRandomseed = hexlify(data[index:index+32]) | |
index += 32 | |
keyTransfRounds = struct.unpack("<L", data[index:index+4])[0] | |
filesize = len(data) | |
datasize = filesize - 124 | |
if((filesize + datasize) < maxInlineSize): | |
dataBuffer = hexlify(data[124:]) | |
end = "*1*%ld*%s" %(datasize, hexlify(dataBuffer)) | |
else: | |
end = "0*%s" %(databaseName) | |
return "%s:$keepass$*1*%s*%s*%s*%s*%s*%s*%s" %(databaseName, keyTransfRounds, algorithm, finalRandomseed, transfRandomseed, encIV, contentsHash, end) | |
def process_2x_database(data, databaseName): | |
index = 12 | |
endReached = False | |
masterSeed = '' | |
transformSeed = '' | |
transformRounds = 0 | |
initializationVectors = '' | |
expectedStartBytes = '' | |
while endReached == False: | |
btFieldID = struct.unpack("B", data[index])[0] | |
index += 1 | |
uSize = struct.unpack("H", data[index:index+2])[0] | |
index += 2 | |
# print "btFieldID : %s , uSize : %s" %(btFieldID, uSize) | |
if btFieldID == 0: | |
endReached = True | |
if btFieldID == 4: | |
masterSeed = hexlify(data[index:index+uSize]) | |
if btFieldID == 5: | |
transformSeed = hexlify(data[index:index+uSize]) | |
if btFieldID == 6: | |
transformRounds = struct.unpack("H", data[index:index+2])[0] | |
if btFieldID == 7: | |
initializationVectors = hexlify(data[index:index+uSize]) | |
if btFieldID == 9: | |
expectedStartBytes = hexlify(data[index:index+uSize]) | |
index += uSize | |
dataStartOffset = index | |
firstEncryptedBytes = hexlify(data[index:index+32]) | |
return "%s:$keepass$*2*%s*%s*%s*%s*%s*%s*%s" %(databaseName, transformRounds, dataStartOffset, masterSeed, transformSeed, initializationVectors, expectedStartBytes, firstEncryptedBytes) | |
def process_database(filename): | |
f = open(filename, 'rb') | |
data = f.read() | |
f.close() | |
base = os.path.basename(filename) | |
databaseName = os.path.splitext(base)[0] | |
fileSignature = hexlify(data[0:8]) | |
if(fileSignature == '03d9a29a67fb4bb5'): | |
# "2.X" | |
print process_2x_database(data, databaseName) | |
elif(fileSignature == '03d9a29a66fb4bb5'): | |
# "2.X pre release" | |
print process_2x_database(data, databaseName) | |
elif(fileSignature == '03d9a29a65fb4bb5'): | |
# "1.X" | |
print process_1x_database(data, databaseName) | |
else: | |
print "ERROR: KeePass signaure unrecognized" | |
if __name__ == "__main__": | |
if len(sys.argv) < 2: | |
sys.stderr.write("Usage: %s <kdb[x] file[s]>\n" % sys.argv[0]) | |
sys.exit(-1) | |
for i in range(1, len(sys.argv)): | |
process_database(sys.argv[i]) |
Traceback (most recent call last):
File "keepass2john.py", line 149, in <module>
process_database(sys.argv[i])
File "keepass2john.py", line 130, in process_database
print process_2x_database(data, databaseName)
File "keepass2john.py", line 85, in process_2x_database
btFieldID = struct.unpack("B", data[index])[0]
IndexError: string index out of range
just doesn't work (keepass 2.5.4)
Traceback (most recent call last): File "keepass2john.py", line 150, in <module> process_database(sys.argv[i]) File "keepass2john.py", line 131, in process_database print process_2x_database(data, databaseName) File "keepass2john.py", line 86, in process_2x_database btFieldID = struct.unpack("B", data[index])[0] IndexError: string index out of range
Not working with keepassxc 2.6.2
It has a double hexlify. end = "1%ld*%s" %(datasize, hexlify(dataBuffer)) should be end = "1%ld*%s" %(datasize, dataBuffer) If your cracking keepass 1's change this.
as for the others with exceptions, those are probably keepasses with keyfiles, they're not supported.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Getting error Keepass signature unrecognized on 2.29 file
Is there an update for this?