Skip to content

Instantly share code, notes, and snippets.

@tibbon
Forked from scottlinux/keepass2john.py
Last active August 22, 2024 01:06
Show Gist options
  • Save tibbon/21b8665fd6bb69caa9852f1100b253cf to your computer and use it in GitHub Desktop.
Save tibbon/21b8665fd6bb69caa9852f1100b253cf to your computer and use it in GitHub Desktop.
Python 3 port of John the Ripper's keepass2john - extracts a HashCat/john crackable hash from KeePass 1.x/2.X databases
#!/usr/bin/env python3
# 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
# Updated for Python 3.12+ compatibility
#
# 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 (encFlag & 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 = f"*1*{datasize}*{dataBuffer.decode()}"
else:
end = f"0*{databaseName}"
return f"{databaseName}:$keepass$*1*{keyTransfRounds}*{algorithm}*{finalRandomseed.decode()}*{transfRandomseed.decode()}*{encIV.decode()}*{contentsHash.decode()}*{end}"
def process_2x_database(data, databaseName):
index = 12
endReached = False
masterSeed = ''
transformSeed = ''
transformRounds = 0
initializationVectors = ''
expectedStartBytes = ''
while not endReached:
btFieldID = data[index]
index += 1
uSize = struct.unpack("<H", data[index:index+2])[0]
index += 2
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("<Q", data[index:index+8])[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 f"{databaseName}:$keepass$*2*{transformRounds}*{dataStartOffset}*{masterSeed.decode()}*{transformSeed.decode()}*{initializationVectors.decode()}*{expectedStartBytes.decode()}*{firstEncryptedBytes.decode()}"
def process_database(filename):
with open(filename, 'rb') as f:
data = f.read()
base = os.path.basename(filename)
databaseName = os.path.splitext(base)[0]
fileSignature = hexlify(data[0:8])
if fileSignature == b'03d9a29a67fb4bb5':
# "2.X"
print(process_2x_database(data, databaseName))
elif fileSignature == b'03d9a29a66fb4bb5':
# "2.X pre release"
print(process_2x_database(data, databaseName))
elif fileSignature == b'03d9a29a65fb4bb5':
# "1.X"
print(process_1x_database(data, databaseName))
else:
print("ERROR: KeePass signature unrecognized")
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.stderr.write(f"Usage: {sys.argv[0]} <kdb[x] file[s]>\n")
sys.exit(-1)
for i in range(1, len(sys.argv)):
process_database(sys.argv[i])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment