#!/usr/bin/python # DCEPT # James Bettke # Dell SecureWorks 2016 from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from SocketServer import ThreadingMixIn import urlparse import random from datetime import datetime import os import sqlite3 import threading import logging from ConfigReader import config import cgi import cgitb from Cracker import cracker import dcept # https://wiki.python.org/moin/BaseHttpServer gsHandle = None class GenerationServer: def __init__(self, hostname="", http_port=80, sqlite_path='/opt/dcept/var/honeytoken.db'): http_port = int(http_port) server_class = ThreadedHTTPServer #BaseHTTPServer.HTTPServer global gsHandle gsHandle = self self.sqlite_path = sqlite_path self.conn = None self.initDatabase() # Only master node should run the generation server if not config.master_node: logging.info("Database contains %d generated passwords" % self.getRecordCount()) self.httpd = server_class((hostname, http_port), HttpHandler) # Start the webserver on it's own thread. Call to serve_forever() blocks thread = threading.Thread(target = self.httpd.serve_forever) thread.daemon = True logging.info("Starting honeytoken generation server HTTP daemon %s:%d" % (hostname,http_port)) thread.start() # Initialize the sqlite database. Create the db and tables if it doesn't exist. def initDatabase(self): if not os.path.exists(self.sqlite_path): self.conn = sqlite3.connect(self.sqlite_path, check_same_thread=False) c = self.conn.cursor() c.execute('''CREATE TABLE db_version (major integer, minor integer)''') c.execute("INSERT INTO db_version VALUES (?,?)", (1,0)) c.execute('''CREATE TABLE logs (date text, domain text, username text, machine text, password text)''') # Add a test honeytoken to the database c.execute("INSERT INTO logs VALUES (?,?,?,?,?)", (datetime.now(), "ALLSAFE.LAN", "Administrator", "FAKE-PC", "dcepttest")) self.conn.commit() else: self.conn = sqlite3.connect(self.sqlite_path, check_same_thread=False) def genPass(self): alpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" password = "" while True: # Create a random password using the above alphabet for i in xrange(10): password += alpha[random.randrange(len(alpha))] print "Generated -",password # Does this password already exist? if self.findPass(password) == None: break else: print "Password collision, regenerating..." return password def findPass(self, password): c = self.conn.cursor() c.execute("SELECT * FROM logs WHERE password=?" , (password,)) row = c.fetchone() if row == None: return None return row def getPasswords(self): c = self.conn.cursor() c.execute("SELECT password FROM logs ORDER BY date DESC") passwords = [] for i in c.fetchall(): passwords.append(i[0]) return passwords def getRecordCount(self): c = self.conn.cursor() c.execute("SELECT count(password) FROM logs") return c.fetchone() class HttpHandler(BaseHTTPRequestHandler): # Generation requests from endpoints are always GET requests and must contain # the honeytoken_param_name parameter. def do_GET(s): global gsHandle #if not s.path.startswith("/backup"): # s.send_response(404) # return s.send_response(200) s.send_header("Content-type", "text/json") s.end_headers() #print s.path qs = urlparse.urlparse(s.path).query qs = urlparse.parse_qs(qs) #print qs machine = "" try: machine = qs[config.honeytoken_param_name][0] except: return print "Request from IP: %s workstation: %s" % (s.client_address[0], machine) domain = config.domain username = config.honey_username password = gsHandle.genPass() jSONstring = "{'d':'%s','u':'%s',p:'%s'}" % (domain, username, password) s.wfile.write(jSONstring) print "Sent:"+jSONstring # Log transaction c = gsHandle.conn.cursor() c.execute("INSERT INTO logs VALUES (?,?,?,?,?)", (datetime.now(), domain, username, machine, password)) gsHandle.conn.commit() # DCEPT to DCEPT communication happens via POST requests. # Example for dcepttest: POST /notify # u=Administrator&d=ALLSAFE.LAN&t=64118a956797600c6e1239f1cf9c8db4ae780f0a1d0bc8b3a0e12de736a14792f17cb58671e42813fbd522e22e021c5d6924b7b114064889 def do_POST(s): length = int(s.headers['content-length']) postvars = cgi.parse_qs(s.rfile.read(length), keep_blank_values=1) logging.debug(postvars) try: username = postvars['u'][0] domain = postvars['d'][0] encTimestamp = postvars['t'][0] except: s.send_response(500) s.end_headers() return cracker.enqueueJob(username, domain, encTimestamp, dcept.passwordHit) s.send_response(200) s.end_headers() class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): """Handle requests in a separate thread.""" if __name__ == '__main__': gs = GenerationServer()