forked from openwall/john
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bitwarden2john.py
executable file
·134 lines (115 loc) · 4.21 KB
/
bitwarden2john.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python2
"""Utility to extract Bitwarden "hashes" from Google Chrome / Firefox / Android local data"""
# Huge thanks goes to Joshua Stein for documenting the various cryptographic
# constructions used in Bitwarden.
#
# See https://github.com/jcs/bitwarden-ruby for the details.
#
# https://help.bitwarden.com/article/where-is-data-stored-computer/
#
# On Linux with Firefox, "storage.js" can be found at the following location,
# ~/.mozilla/firefox/your_profile/browser-extension-data/\{446900e4-71c2-419f-a6a7-df9c091e268b\}/
#
# Tested with Firefox 57.x running "bitwarden - Free Password Manager 1.23.0" extension.
#
# On Android with bitwarden 1.14.1, the "hash" can be extracted from the following location,
# /data/data/com.x8bit.bitwarden/shared_prefs/com.x8bit.bitwarden_preferences.xml
#
# Use https://help.bitwarden.com/crypto.html to generate sample hashes.
#
# Written by Dhiru Kholia <dhiru at openwall.com> in January 2018 for JtR
# project.
#
# This software is Copyright (c) 2018, Dhiru Kholia <dhiru at openwall.com> and
# it is hereby released to the general public under the following terms:
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted.
#
# pylint: disable=invalid-name,line-too-long
import os
import sys
import base64
import binascii
import traceback
import xml.etree.ElementTree as ET
try:
import json
assert json
except ImportError:
try:
import simplejson as json
except ImportError:
sys.stderr.write("Please install json module which is currently not installed.\n")
sys.exit(1)
try:
import plyvel
except ImportError:
sys.stderr.write("[WARNING] Please install the plyvel module for full functionality!\n")
sys.exit(1)
def process_xml_file(filename):
tree = ET.parse(filename)
root = tree.getroot()
email = None
enc_key = None
for item in root:
if item.tag == 'string':
name = item.attrib['name']
if name == "encKey":
enc_key = item.text
if name == "email":
email = item.text
return email, enc_key
def process_leveldb(path):
db = plyvel.DB(path, create_if_missing=False)
email = db.get(b'userEmail')
email = email.decode("utf-8")
email = email.strip('"').rstrip('"') # always safe?
enc_key = db.get(b'encKey')
enc_key = enc_key.decode("ascii")
return email, enc_key
def process_file(filename):
if "nngceckbap" in filename or os.path.isdir(filename):
try:
email, enc_key = process_leveldb(filename)
if not email or not enc_key:
sys.stderr.write("[ERROR] %s could not be parsed properly!\n" % filename)
return
except:
traceback.print_exc()
return
else:
with open(filename, "rb") as f:
data = f.read()
if filename.endswith(".xml") or data.startswith(b"<?xml"):
try:
email, enc_key = process_xml_file(filename)
if not email or not enc_key:
sys.stderr.write("[ERROR] %s could not be parsed properly!\n" % filename)
return
except:
traceback.print_exc()
return
else:
try:
data = json.loads(data)
email = data["userEmail"]
enc_key = data["encKey"]
except (ValueError, KeyError):
traceback.print_exc()
return
iterations = 5000 # seems to be fixed in the design
email = email.lower()
iv_mix, blob = enc_key.split("|")
iv = iv_mix[2:] # skip over "0."
iv = binascii.hexlify(base64.b64decode(iv)).decode("ascii")
blob = binascii.hexlify(base64.b64decode(blob)).decode("ascii")
sys.stdout.write("%s:$bitwarden$0*%s*%s*%s*%s\n" %
(os.path.basename(filename), iterations, email, iv, blob))
if __name__ == "__main__":
if len(sys.argv) < 2:
sys.stderr.write("Usage: %s <Bitwarden storage.js / com.x8bit.bitwarden_preferences.xml / Google Chrome's 'nngceckbap...' path>\n" %
sys.argv[0])
sys.exit(1)
for j in range(1, len(sys.argv)):
process_file(sys.argv[j])