#!/usr/bin/env python2 import requests from requests import ConnectionError #The following disables the InsecureRequests warning and the 'Starting new HTTPS connection' log message from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) import cmd import sqlite3 import sys import os import argparse from time import sleep from ConfigParser import ConfigParser from cme.msfrpc import Msfrpc from cme.database import CMEDatabase from cme.helpers import validate_ntlm class CMEDatabaseNavigator(cmd.Cmd): def __init__(self, db_path, config_path): cmd.Cmd.__init__(self) self.prompt = 'cmedb > ' try: # set the database connection to autocommit w/ isolation level conn = sqlite3.connect(db_path, check_same_thread=False) conn.text_factory = str conn.isolation_level = None self.db = CMEDatabase(conn) except Exception as e: print "[-] Could not connect to database: {}".format(e) sys.exit(1) try: self.config = ConfigParser() self.config.read(config_path) except Exception as e: print "[-] Error reading cme.conf: {}".format(e) sys.exit(1) def display_creds(self, creds): print "\nCredentials:\n" print " CredID Admin On CredType Domain UserName Password" print " ------ -------- -------- ------ -------- --------" for cred in creds: # (id, credtype, domain, username, password, host, notes, sid) credID = cred[0] credType = cred[1] domain = cred[2] username = cred[3] password = cred[4] links = self.db.get_links(credID=credID) print u" {}{}{}{}{}{}".format('{:<8}'.format(credID), '{:<13}'.format(str(len(links)) + ' Host(s)'), '{:<12}'.format(credType), u'{:<17}'.format(domain.decode('utf-8')), u'{:<21}'.format(username.decode('utf-8')), u'{:<17}'.format(password.decode('utf-8'))) print "" def display_hosts(self, hosts): print "\nHosts:\n" print " HostID Admins IP Hostname Domain OS" print " ------ ------ -- -------- ------ --" for host in hosts: # (id, ip, hostname, domain, os) hostID = host[0] ip = host[1] hostname = host[2] domain = host[3] os = host[4] links = self.db.get_links(hostID=hostID) print u" {}{}{}{}{}{}".format('{:<8}'.format(hostID), '{:<15}'.format(str(len(links)) + ' Cred(s)'), '{:<17}'.format(ip), u'{:<25}'.format(hostname.decode('utf-8')), u'{:<17}'.format(domain.decode('utf-8')), '{:<17}'.format(os)) print "" def do_exit(self, line): sys.exit(0) def do_import(self, line): if not line: return if line == 'empire': headers = {'Content-Type': 'application/json'} #Pull the username and password from the config file payload = {'username': self.config.get('Empire', 'username'), 'password': self.config.get('Empire', 'password')} #Pull the host and port from the config file base_url = 'https://{}:{}'.format(self.config.get('Empire', 'api_host'), self.config.get('Empire', 'api_port')) try: r = requests.post(base_url + '/api/admin/login', json=payload, headers=headers, verify=False) if r.status_code == 200: token = r.json()['token'] url_params = {'token': token} r = requests.get(base_url + '/api/creds', headers=headers, params=url_params, verify=False) creds = r.json() for cred in creds['creds']: if cred['credtype'] == 'token' or cred['credtype'] == 'krbtgt' or cred['username'].endswith('$'): continue self.db.add_credential(cred['credtype'], cred['domain'], cred['username'], cred['password']) print "[+] Empire credential import successful" else: print "[-] Error authenticating to Empire's RESTful API server!" except ConnectionError as e: print "[-] Unable to connect to Empire's RESTful API server: {}".format(e) elif line == 'metasploit': msf = Msfrpc({'host': self.config.get('Metasploit', 'rpc_host'), 'port': self.config.get('Metasploit', 'rpc_port')}) try: msf.login('msf', self.config.get('Metasploit', 'password')) except MsfAuthError: print "[-] Error authenticating to Metasploit's MSGRPC server!" return console_id = str(msf.call('console.create')['id']) msf.call('console.write', [console_id, 'creds\n']) sleep(2) creds = msf.call('console.read', [console_id]) for entry in creds['data'].split('\n'): cred = entry.split() try: host = cred[0] port = cred[2] proto = cred[3] username = cred[4] password = cred[5] cred_type = cred[6] if proto == '(smb)' and cred_type == 'Password': self.db.add_credential('plaintext', '', username, password) except IndexError: continue msf.call('console.destroy', [console_id]) print "[+] Metasploit credential import successful" def complete_import(self, text, line, begidx, endidx): "Tab-complete 'import' commands." commands = ["empire", "metasploit"] mline = line.partition(' ')[2] offs = len(mline) - len(text) return [s[offs:] for s in commands if s.startswith(mline)] def do_hosts(self, line): filterTerm = line.strip() if filterTerm == "": hosts = self.db.get_hosts() self.display_hosts(hosts) else: hosts = self.db.get_hosts(filterTerm=filterTerm) print "\nHost(s):\n" print " HostID IP Hostname Domain OS" print " ------ -- -------- ------ --" hostIDList = [] for host in hosts: hostID = host[0] hostIDList.append(hostID) ip = host[1] hostname = host[2] domain = host[3] os = host[4] print u" {}{}{}{}{}".format('{:<8}'.format(hostID), '{:<17}'.format(ip), u'{:<25}'.format(hostname.decode('utf-8')), u'{:<17}'.format(domain.decode('utf-8')), '{:<17}'.format(os)) print "" print "\nCredential(s) with Admin Access:\n" print " CredID CredType Domain UserName Password" print " ------ -------- ------ -------- --------" for hostID in hostIDList: links = self.db.get_links(hostID=hostID) for link in links: linkID, credID, hostID = link creds = self.db.get_credentials(filterTerm=credID) for cred in creds: credID = cred[0] credType = cred[1] domain = cred[2] username = cred[3] password = cred[4] print u" {}{}{}{}{}".format('{:<8}'.format(credID), '{:<12}'.format(credType), u'{:<17}'.format(domain.decode('utf-8')), u'{:<21}'.format(username.decode('utf-8')), u'{:<17}'.format(password.decode('utf-8'))) print "" def do_creds(self, line): filterTerm = line.strip() if filterTerm == "": creds = self.db.get_credentials() self.display_creds(creds) elif filterTerm.split()[0].lower() == "add": # add format: "domain username password args = filterTerm.split()[1:] if len(args) == 3: domain, username, password = args if validate_ntlm(password): self.db.add_credential("hash", domain, username, password) else: self.db.add_credential("plaintext", domain, username, password) else: print "[!] Format is 'add domain username password" return elif filterTerm.split()[0].lower() == "remove": args = filterTerm.split()[1:] if len(args) != 1 : print "[!] Format is 'remove '" return else: self.db.remove_credentials(args) self.db.remove_links(credIDs=args) elif filterTerm.split()[0].lower() == "plaintext": creds = self.db.get_credentials(credtype="plaintext") self.display_creds(creds) elif filterTerm.split()[0].lower() == "hash": creds = self.db.get_credentials(credtype="hash") self.display_creds(creds) else: creds = self.db.get_credentials(filterTerm=filterTerm) print "\nCredential(s):\n" print " CredID CredType Domain UserName Password" print " ------ -------- ------ -------- --------" credIDList = [] for cred in creds: credID = cred[0] credIDList.append(credID) credType = cred[1] domain = cred[2] username = cred[3] password = cred[4] print u" {}{}{}{}{}".format('{:<8}'.format(credID), '{:<12}'.format(credType), u'{:<17}'.format(domain.decode('utf-8')), u'{:<21}'.format(username.decode('utf-8')), u'{:<17}'.format(password.decode('utf-8'))) print "" print "\nAdmin Access to Host(s):\n" print " HostID IP Hostname Domain OS" print " ------ -- -------- ------ --" for credID in credIDList: links = self.db.get_links(credID=credID) for link in links: linkID, credID, hostID = link hosts = self.db.get_hosts(hostID) for host in hosts: hostID = host[0] ip = host[1] hostname = host[2] domain = host[3] os = host[4] print u" {}{}{}{}{}".format('{:<8}'.format(hostID), '{:<17}'.format(ip), u'{:<25}'.format(hostname.decode('utf-8')), u'{:<17}'.format(domain.decode('utf-8')), '{:<17}'.format(os)) print "" def complete_creds(self, text, line, begidx, endidx): "Tab-complete 'creds' commands." commands = [ "add", "remove", "hash", "plaintext"] mline = line.partition(' ')[2] offs = len(mline) - len(text) return [s[offs:] for s in commands if s.startswith(mline)] def main(): parser = argparse.ArgumentParser() parser.add_argument("--db-path", type=str, default='~/.cme/cme.db', help="Path to CME database (default: ~/.cme/cme.db)") parser.add_argument("--config-path", type=str, default='~/.cme/cme.conf', help='Path to the CME configuration file (default: ~/.cme/cme.conf)') args = parser.parse_args() db_path = os.path.expanduser(args.db_path) config_path = os.path.expanduser(args.config_path) if not os.path.exists(db_path): print '[-] Path to database invalid!' sys.exit(1) if not os.path.exists(config_path): print "[-] Path to config file invalid!" sys.exit(1) try: cmedbnav = CMEDatabaseNavigator(db_path, config_path) cmedbnav.cmdloop() except KeyboardInterrupt: pass