Last active
December 15, 2024 21:15
-
-
Save gerry/c4602c23783d894b8d96 to your computer and use it in GitHub Desktop.
A quick hack to extract and decrypt credentials from DbVisualizer config files.
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/env python | |
# decrypt_dbvis.py ~ [email protected] | |
# DbVisualizer uses PBEWithMD5AndDES with a static key to store passwords. | |
# This is a quick hack to extract and decrypt credentials from DbVisualizer config files. | |
# Tested against DbVisualizer Free 9.0.9 and 9.1.6 | |
""" | |
[2014-03-25 02:05:30][not-the-sea workspace]$ security/p/gerry/misc/decrypt_dbvis.py | |
[+] DbVisualizer Password Extractor and Decryptor (@gerryeisenhaur) | |
[+] Additional Usage Options: | |
[+] security/p/gerry/misc/decrypt_dbvis.py <config filename> | |
[+] security/p/gerry/misc/decrypt_dbvis.py <encrypted password> | |
[+] Extracting credentials from /Users/jack/.dbvis/config70/dbvis.xml | |
driver | name | user | password | connection_info | |
------------+---------------------+-------------+--------------------+--------------------------------------- | |
Proxy | Default Proxy | myproxyuser | somesecretpassword | socks://127.0.0.1:1234 | |
Vertica 6.1 | vertica-prod | admin | password1 | jdbc:vertica://127.0.0.1:5434/userdata | |
PostgreSQL | PGTest | pg_user | pg_pass | Server=localhost,Port=5432,Database= | |
SQLite | Doodie | sqlituser | sqpasdf | Database file name=~/mysqlite.db | |
SSH | Doodie | root | s1qpa | localhost:22 | |
[+] Done. Have Fun! | |
""" | |
import base64 | |
from hashlib import md5 | |
from Crypto.Cipher import DES | |
from os.path import expanduser | |
from lxml import etree, objectify | |
from collections import namedtuple | |
_iterations = 10 | |
_salt = '\x8E\x129\x9C\aroZ' # -114, 18, 57, -100, 7, 114, 111, 90 | |
_password = 'qinda' | |
Credential = namedtuple('Credential', ['driver', 'name', 'user', | |
'password', 'connection_info']) | |
class PBEWithMD5AndDES(object): | |
def __init__(self, password, salt, iterations): | |
key = self._generate_key(password, salt, iterations, 16) | |
self.key = key[:8] | |
self.iv = key[8:16] | |
def _cipher(self): | |
return DES.new(self.key, DES.MODE_CBC, self.iv) | |
def _generate_key(self, key, salt, count, length): | |
key = key + salt | |
for i in range(count): | |
key = md5(key).digest() | |
return key[:length] | |
def encrypt(self, plaintext): | |
padding = 8 - len(plaintext) % 8 | |
plaintext += chr(padding) * padding | |
return self._cipher().encrypt(plaintext) | |
def decrypt(self, ciphertext): | |
plaintext = self._cipher().decrypt(ciphertext) | |
return plaintext[:-ord(plaintext[-1])] | |
def decrypt_password(password): | |
pbe = PBEWithMD5AndDES(_password, _salt, _iterations) | |
return pbe.decrypt(base64.b64decode(password)) | |
def extract_credentials(config_file): | |
with open(config_file, 'r') as xml_file: | |
xml_blob = xml_file.read() | |
root_obj = objectify.fromstring(xml_blob) | |
pbe = PBEWithMD5AndDES(_password, _salt, _iterations) | |
creds = [] | |
# Get any global proxy if it exists. | |
proxy_user = getattr(root_obj.General, 'ProxyUser', None) | |
proxy_pass = getattr(root_obj.General, 'ProxyPassword', None) | |
if proxy_pass and proxy_pass.text: | |
proxy_pass = pbe.decrypt(base64.b64decode(proxy_pass.text)) | |
proxy_host = getattr(root_obj.General, 'ProxyHost', None) | |
proxy_port = getattr(root_obj.General, 'ProxyPort', None) | |
proxy_type = getattr(root_obj.General, 'ProxyType', None) | |
conn_info = "%s://%s:%s" % (proxy_type, proxy_host, proxy_port,) | |
conn_info = (proxy_user and proxy_pass) and conn_info or None | |
if conn_info: | |
creds.append(Credential(name="Default Proxy", user=proxy_user, | |
password=proxy_pass, connection_info=conn_info, driver='Proxy')) | |
# Grab and decrypt each DB password along with any SSh servers | |
cred = {} | |
for db in root_obj.Databases.Database: | |
cred['name'] = getattr(db, 'Alias', None) | |
cred['user'] = getattr(db, 'Userid', None) | |
password = getattr(db, 'Password', None) | |
if password is not None and password.text: | |
cred['password'] = pbe.decrypt(base64.b64decode(password.text)) | |
cred['driver'] = getattr(db, 'Driver', None) | |
conn_info = getattr(db, 'Url', None) | |
if not conn_info: | |
params = db.UrlVariables.Driver.getchildren() | |
conn_info = ",".join(["%s=%s" % (p.get('UrlVariableName'), p) for p in params]) | |
cred['connection_info'] = conn_info | |
creds.append(Credential(**cred)) | |
# Note: I haven't tested anything related to ssh info extraction... no test cases | |
ssh_password = getattr(db.SshSettings, 'SshPassword', None) | |
if ssh_password and ssh_password.text: | |
host = getattr(db.SshSettings, 'SshHost', '') | |
port = getattr(db.SshSettings, 'SshPort', '22') | |
ssh_cred = dict( | |
driver="SSH", | |
name="%s" % (cred['name'],), | |
user=getattr(db.SshSettings, 'SshUserid'), | |
password=pbe.decrypt(base64.b64decode(ssh_password.text)), | |
connection_info="%s:%s" % (host, port,)) | |
creds.append(Credential(**ssh_cred)) | |
return creds | |
# Some modified snippet I had laying around. found it on stackoverflow maybe? | |
# TODO(gerry): Replace with prettytables. | |
def print_table(rows): | |
headers = rows[0]._fields | |
lens = [] | |
for i in range(len(rows[0])): | |
lens.append(len(max([str(x[i]) for x in rows] + [headers[i]], | |
key=lambda x:len(str(x))))) | |
formats, hformats = [], [] | |
for i in range(len(rows[0])): | |
if isinstance(rows[0][i], int): | |
formats.append("%%%dd" % lens[i]) | |
else: | |
formats.append("%%-%ds" % lens[i]) | |
hformats.append("%%-%ds" % lens[i]) | |
pattern = " | ".join(formats) | |
hpattern = " | ".join(hformats) | |
separator = "-+-".join(['-' * n for n in lens]) | |
print " ", hpattern % tuple(headers) | |
print " ", separator | |
for line in rows: | |
print " ", pattern % tuple(line) | |
if __name__ == '__main__': | |
import sys | |
import os.path | |
print "[+] DbVisualizer Password Extractor and Decryptor (@gerryeisenhaur)" | |
if len(sys.argv) == 2: | |
if os.path.exists(sys.argv[1]): | |
dbvis_config = sys.argv[1] | |
else: | |
dbvis_config = None | |
else: | |
print "[+] Additional Usage Options: " | |
print "[+] %s <config filename>" % sys.argv[0] | |
print "[+] %s <encrypted password>" % sys.argv[0] | |
dbvis_config = "%s/.dbvis/config70/dbvis.xml" % os.path.expanduser("~") | |
if not dbvis_config: | |
password = sys.argv[1] | |
print "[+] Decrypting: %s" % (password,) | |
try: | |
print "[+] Plain Text: %s" % (decrypt_password(password),) | |
except Exception, e: | |
print "[!] Error decrypting! %s" % (e,) | |
sys.exit() | |
print "[+] Extracting credentials from %s\n" % (dbvis_config,) | |
print_table(extract_credentials(dbvis_config)) | |
print "\n[+] Done. Have Fun!" |
Hi,
I'm getting error
File "C:\decrypt_dbvis.py", line 140
print " ", hpattern % tuple(headers)
How can I make this work?
Thanks in advance!
Hi.
I just created a small Docker Wrapper for this as Python got removed from MacOS.
Python3 Version: https://gist.github.com/Liki4/c5315c3048cff04b9897fad89d5e1a5f
Java Version: https://github.com/anape03/Decrypt-DBVis
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello,
Is it possible to have the same script but to encrypt the passwords?
Regards,