def start(cls, addr, iface=None, config=None): daemons = [] for proto in ("tcp", "tls", "udp"): ports = config.get("%s_ports" % proto) if ports is None: continue for port in ports: daemon = SipSession(proto=proto, config=config) daemon.bind(addr, port, iface=iface) daemon.listen() daemons.append(daemon) if len(daemons) > 0: global g_timer_cleanup if g_timer_cleanup is None: logger.warning("Starting cleanup loop") g_timer_cleanup = Timer( 60, cleanup, repeat=True, ) g_timer_cleanup.start() else: logger.debug("Cleanup loop already started!") return daemons
def __init__(self, path, config=None): logger.debug("%s ready!" % (self.__class__.__name__)) ihandler.__init__(self, path) self.apikey = config.get("apikey") comment = config.get("comment") if comment is None: comment = "This sample was captured in the wild and uploaded by the dionaea honeypot.\n#honeypot #malware #networkworm" self.comment = comment self.cookies = {} self.backlog_timer = Timer( interval=20, delay=0, function=self.__handle_backlog_timeout, repeat=True, ) self.backlog_timer.start() p = config.get("file") self.dbh = sqlite3.connect(p) self.cursor = self.dbh.cursor() self.cursor.execute(""" CREATE TABLE IF NOT EXISTS backlogfiles ( backlogfile INTEGER PRIMARY KEY, status TEXT NOT NULL, -- new, submit, query, comment md5_hash TEXT NOT NULL, path TEXT NOT NULL, timestamp INTEGER NOT NULL, scan_id TEXT, lastcheck_time INTEGER, submit_time INTEGER );""")
def __init__(self, path, config=None): logger.debug('hpfeedhandler init') reconnect_timeout = config.get("reconnect_timeout") if reconnect_timeout is None: reconnect_timeout = self.default_reconnect_timeout try: reconnect_timeout = float(reconnect_timeout) except (TypeError, ValueError) as e: logger.warn( "Unable to convert value '%s' for reconnect timeout to float" % reconnect_timeout) reconnect_timeout = self.default_reconnect_timeout port = config.get("port") if port is None: port = self.default_port try: port = int(port) except (TypeError, ValueError) as e: logger.warn("Unable to convert value '%s' for port to int" % port) port = self.default_port self.client = hpclient(config['server'], port, config['ident'], config['secret'], reconnect_timeout=reconnect_timeout) ihandler.__init__(self, path) self.dynip_resolve = config.get('dynip_resolve', '') self.dynip_timer = None self.ownip = None if isinstance(self.dynip_resolve, str) and self.dynip_resolve.startswith("http"): logger.debug('hpfeedihandler will use dynamic IP resolving!') self.dynip_timer = Timer( 300, self._dynip_resolve, delay=2, repeat=True, ) self.dynip_timer.start()
def handle_INVITE(self, msg): logger.debug("{!s} handle_INVITE".format(self)) self.__state = SipCall.INVITE self._timers["invite_handler"] = Timer( 0.1, self.__handle_invite, ) # ToDo: the data isn't used in the callback at the moment # self._timers["invite_handler"].data = msg self._timers["invite_handler"].start() return True
def __init__(self, proto, call_id, session, invite_message): logger.debug("{!s} __init__".format(self)) logger.debug("SipCall {} session {} ".format(self, session)) connection.__init__(self, proto) # Store incoming information of the remote host self.__session = session self.__state = SipCall.SESSION_SETUP self.__msg = invite_message # list of messages self._msg_stack = [] self.__call_id = invite_message.headers.get(b"call-id").value self._call_id = call_id self._rtp_streams = {} self.local.host = self.__session.local.host self.local.port = self.__session.local.port self.remote.host = self.__session.remote.host self.remote.port = self.__session.remote.port user = self.__msg.headers.get(b"to").get_raw().uri.user self._user = self.__session.config.get_user_by_username( self.__session.personality, user) # fake a connection entry i = incident("dionaea.connection.udp.connect") i.con = self i.report() # Global timers self._timers = { "idle": Timer(60.0, self.__handle_timeout_idle, repeat=True), "invite_handler": None, } self._timers["idle"].start()
class hpfeedihandler(ihandler): default_reconnect_timeout = 10.0 default_port = 10000 def __init__(self, path, config=None): logger.debug('hpfeedhandler init') reconnect_timeout = config.get("reconnect_timeout") if reconnect_timeout is None: reconnect_timeout = self.default_reconnect_timeout try: reconnect_timeout = float(reconnect_timeout) except (TypeError, ValueError) as e: logger.warn( "Unable to convert value '%s' for reconnect timeout to float" % reconnect_timeout) reconnect_timeout = self.default_reconnect_timeout port = config.get("port") if port is None: port = self.default_port try: port = int(port) except (TypeError, ValueError) as e: logger.warn("Unable to convert value '%s' for port to int" % port) port = self.default_port self.client = hpclient(config['server'], port, config['ident'], config['secret'], reconnect_timeout=reconnect_timeout) ihandler.__init__(self, path) self.dynip_resolve = config.get('dynip_resolve', '') self.dynip_timer = None self.ownip = None if isinstance(self.dynip_resolve, str) and self.dynip_resolve.startswith("http"): logger.debug('hpfeedihandler will use dynamic IP resolving!') self.dynip_timer = Timer( 300, self._dynip_resolve, delay=2, repeat=True, ) self.dynip_timer.start() def stop(self): if self.dynip_timer: self.dynip_timer.stop() self.dynip_timer = None def _ownip(self, icd): if self.dynip_resolve and 'http' in self.dynip_resolve: if self.ownip: return self.ownip else: raise Exception('Own IP not yet resolved!') return icd.con.local.host def __del__(self): # self.client.close() pass def connection_publish(self, icd, con_type): try: con = icd.con self.client.publish(CONNCHAN, connection_type=con_type, connection_transport=con.transport, connection_protocol=con.protocol, remote_host=con.remote.host, remote_port=con.remote.port, remote_hostname=con.remote.hostname, local_host=self._ownip(icd), local_port=con.local.port) except Exception as e: logger.warn('exception when publishing: {0}'.format(e)) def handle_incident(self, i): pass def handle_incident_dionaea_connection_tcp_listen(self, icd): self.connection_publish(icd, 'listen') con = icd.con logger.info("listen connection on %s:%i" % (con.remote.host, con.remote.port)) def handle_incident_dionaea_connection_tls_listen(self, icd): self.connection_publish(icd, 'listen') con = icd.con logger.info("listen connection on %s:%i" % (con.remote.host, con.remote.port)) def handle_incident_dionaea_connection_tcp_connect(self, icd): self.connection_publish(icd, 'connect') con = icd.con logger.info("connect connection to %s/%s:%i from %s:%i" % (con.remote.host, con.remote.hostname, con.remote.port, self._ownip(icd), con.local.port)) def handle_incident_dionaea_connection_tls_connect(self, icd): self.connection_publish(icd, 'connect') con = icd.con logger.info("connect connection to %s/%s:%i from %s:%i" % (con.remote.host, con.remote.hostname, con.remote.port, self._ownip(icd), con.local.port)) def handle_incident_dionaea_connection_udp_connect(self, icd): self.connection_publish(icd, 'connect') con = icd.con logger.info("connect connection to %s/%s:%i from %s:%i" % (con.remote.host, con.remote.hostname, con.remote.port, self._ownip(icd), con.local.port)) def handle_incident_dionaea_connection_tcp_accept(self, icd): self.connection_publish(icd, 'accept') con = icd.con logger.info("accepted connection from %s:%i to %s:%i" % (con.remote.host, con.remote.port, self._ownip(icd), con.local.port)) def handle_incident_dionaea_connection_tls_accept(self, icd): self.connection_publish(icd, 'accept') con = icd.con logger.info("accepted connection from %s:%i to %s:%i" % (con.remote.host, con.remote.port, self._ownip(icd), con.local.port)) def handle_incident_dionaea_connection_tcp_reject(self, icd): self.connection_publish(icd, 'reject') con = icd.con logger.info("reject connection from %s:%i to %s:%i" % (con.remote.host, con.remote.port, self._ownip(icd), con.local.port)) def handle_incident_dionaea_connection_tcp_pending(self, icd): self.connection_publish(icd, 'pending') con = icd.con logger.info("pending connection from %s:%i to %s:%i" % (con.remote.host, con.remote.port, self._ownip(icd), con.local.port)) def handle_incident_dionaea_download_complete_unique(self, i): self.handle_incident_dionaea_download_complete_again(i) if not hasattr(i, 'con') or not self.client.connected: return logger.debug('unique complete, publishing md5 {0}, path {1}'.format( i.md5hash, i.file)) try: self.client.sendfile(i.file) except Exception as e: logger.warn('exception when publishing: {0}'.format(e)) def handle_incident_dionaea_download_complete_again(self, i): if not hasattr(i, 'con') or not self.client.connected: return logger.debug('hash complete, publishing md5 {0}, path {1}'.format( i.md5hash, i.file)) try: tstamp = timestr() sha512 = sha512file(i.file) self.client.publish(CAPTURECHAN, time=tstamp, saddr=i.con.remote.host, sport=str(i.con.remote.port), daddr=self._ownip(i), dport=str(i.con.local.port), md5=i.md5hash, sha512=sha512, url=i.url) except Exception as e: logger.warn('exception when publishing: {0}'.format(e)) def handle_incident_dionaea_modules_python_smb_dcerpc_request(self, i): if not hasattr(i, 'con') or not self.client.connected: return logger.debug('dcerpc request, publishing uuid {0}, opnum {1}'.format( i.uuid, i.opnum)) try: self.client.publish(DCECHAN, uuid=i.uuid, opnum=i.opnum, saddr=i.con.remote.host, sport=str(i.con.remote.port), daddr=self._ownip(i), dport=str(i.con.local.port)) except Exception as e: logger.warn('exception when publishing: {0}'.format(e)) def handle_incident_dionaea_module_emu_profile(self, icd): if not hasattr(icd, 'con') or not self.client.connected: return logger.debug('emu profile, publishing length {0}'.format( len(icd.profile))) try: self.client.publish(SCPROFCHAN, profile=icd.profile) except Exception as e: logger.warn('exception when publishing: {0}'.format(e)) def _dynip_resolve(self, events, data): i = incident("dionaea.upload.request") i._url = self.dynip_resolve i._callback = "dionaea.modules.python.hpfeeds.dynipresult" i.report() def handle_incident_dionaea_modules_python_hpfeeds_dynipresult(self, icd): fh = open(icd.path, mode="rb") self.ownip = fh.read().strip().decode('latin1') logger.debug('resolved own IP to: {0}'.format(self.ownip)) fh.close()
def __handle_invite(self): logger.debug("{!s} __handle_invite".format(self)) logger.info("Handle invite") if self.__state == SipCall.INVITE: logger.debug("Send TRYING") # ToDo: Check authentication #self.__authenticate(headers) if self._user is None: msg = self.__msg.create_response(rfc3261.NOT_FOUND) self.send(msg.dumps()) self.close() return msg = self.__msg.create_response(rfc3261.TRYING) self.send(msg) self.__state = SipCall.INVITE_TRYING # Wait up to two seconds self._timers["invite_handler"] = Timer( random.random() * 2, self.__handle_invite, ) self._timers["invite_handler"].start() return if self.__state == SipCall.INVITE_TRYING: # Send 180 Ringing to make honeypot appear more human-like logger.debug("Send RINGING") msg = self.__msg.create_response(rfc3261.RINGING) self.send(msg) delay = random.randint(self._user.pickup_delay_min, self._user.pickup_delay_max) logger.info( "Choosing ring delay between {} and {} seconds: {}".format( self._user.pickup_delay_min, self._user.pickup_delay_max, delay)) self.__state = SipCall.INVITE_RINGING self._timers["invite_handler"] = Timer( delay, self.__handle_invite, ) self._timers["invite_handler"].start() return if self.__state == SipCall.INVITE_RINGING: logger.debug("Send OK") # Create a stream dump file with date and time and random ID in case of # flooding attacks pcap = self.__session.config.get_pcap() # Create RTP stream instance and pass address and port of listening # remote RTP host sdp = self.__msg.sdp media_port_names = self.__session.config.get_sdp_media_port_names( self._user.sdp) media_ports = {} for name in media_port_names: media_ports[name] = None bistream_enabled = False if "bistream" in self.__session.config._rtp.get("modes", {}): bistream_enabled = True for name in media_port_names: for media in sdp.get(b"m"): if media.media.lower() == bytes(name[:5], "utf-8"): self._rtp_streams[name] = RtpUdpStream( name, self, self.__session, self.__session.local.host, 0, # random port self.__session.remote.host, media.port, pcap=pcap, bistream_enabled=bistream_enabled) media_ports[name] = self._rtp_streams[name].local.port i = incident("dionaea.connection.link") i.parent = self i.child = self._rtp_streams[name] i.report() # Send 200 OK and pick up the phone msg = self.__msg.create_response(rfc3261.OK) # ToDo: add IP6 support msg.sdp = rfc4566.SDP.froms( self.__session.config.get_sdp_by_name( self._user.sdp, unicast_address=self.local.host, addrtype="IP4", media_ports=media_ports)) msg_stack = self._msg_stack msg_stack.append(("out", msg)) pcap.open(msg_stack=msg_stack, personality=self.__session.personality, local_host=self.local.host, local_port=self.local.port, remote_host=self.remote.host, remote_port=self.remote.port) self.send(msg) self.__state = SipCall.CALL # ToDo: Send rtp data? return
class virustotalhandler(ihandler): def __init__(self, path, config=None): logger.debug("%s ready!" % (self.__class__.__name__)) ihandler.__init__(self, path) self.apikey = config.get("apikey") comment = config.get("comment") if comment is None: comment = "This sample was captured in the wild and uploaded by the dionaea honeypot.\n#honeypot #malware #networkworm" self.comment = comment self.cookies = {} self.backlog_timer = Timer( interval=20, delay=0, function=self.__handle_backlog_timeout, repeat=True, ) self.backlog_timer.start() p = config.get("file") self.dbh = sqlite3.connect(p) self.cursor = self.dbh.cursor() self.cursor.execute(""" CREATE TABLE IF NOT EXISTS backlogfiles ( backlogfile INTEGER PRIMARY KEY, status TEXT NOT NULL, -- new, submit, query, comment md5_hash TEXT NOT NULL, path TEXT NOT NULL, timestamp INTEGER NOT NULL, scan_id TEXT, lastcheck_time INTEGER, submit_time INTEGER );""") def __handle_backlog_timeout(self, watcher, event): logger.debug("backlog_timeout") # try to comment on files # comment on files which were submitted at least 60 seconds ago sfs = self.cursor.execute( """SELECT backlogfile, md5_hash, path FROM backlogfiles WHERE status = 'comment' AND submit_time < strftime("%s",'now')-1*60 LIMIT 1""" ) for sf in sfs: self.cursor.execute( """UPDATE backlogfiles SET status = 'comment-' WHERE backlogfile = ?""", (sf[0], )) self.dbh.commit() self.make_comment(sf[0], sf[1], sf[2], 'comment') return # try to receive reports for files we submitted sfs = self.cursor.execute( """SELECT backlogfile, md5_hash, path FROM backlogfiles WHERE status = 'query' AND submit_time < strftime("%s",'now')-15*60 AND lastcheck_time < strftime("%s",'now')-15*60 LIMIT 1""" ) for sf in sfs: self.cursor.execute( """UPDATE backlogfiles SET status = 'query-' WHERE backlogfile = ?""", (sf[0], )) self.dbh.commit() self.get_file_report(sf[0], sf[1], sf[2], 'query') return # submit files not known to virustotal sfs = self.cursor.execute( """SELECT backlogfile, md5_hash, path FROM backlogfiles WHERE status = 'submit' LIMIT 1""" ) for sf in sfs: self.cursor.execute( """UPDATE backlogfiles SET status = 'submit-' WHERE backlogfile = ?""", (sf[0], )) self.dbh.commit() self.scan_file(sf[0], sf[1], sf[2], 'submit') return # query new files sfs = self.cursor.execute( """SELECT backlogfile, md5_hash, path FROM backlogfiles WHERE status = 'new' ORDER BY timestamp DESC LIMIT 1""" ) for sf in sfs: self.cursor.execute( """UPDATE backlogfiles SET status = 'new-' WHERE backlogfile = ?""", (sf[0], )) self.dbh.commit() self.get_file_report(sf[0], sf[1], sf[2], 'new') return def stop(self): self.backlog_timer.stop() self.backlog_timer = None def handle_incident(self, icd): pass def handle_incident_dionaea_download_complete_unique(self, icd): self.cursor.execute( """INSERT INTO backlogfiles (md5_hash, path, status, timestamp) VALUES (?,?,?,strftime("%s",'now')) """, (icd.md5hash, icd.file, 'new')) def get_file_report(self, backlogfile, md5_hash, path, status): cookie = str(uuid.uuid4()) self.cookies[cookie] = vtreport(backlogfile, md5_hash, path, status) i = incident("dionaea.upload.request") i._url = "https://www.virustotal.com/vtapi/v2/file/report" i.resource = md5_hash i.apikey = self.apikey i._callback = "dionaea.modules.python.virustotal.get_file_report" i._userdata = cookie i.report() def handle_incident_dionaea_modules_python_virustotal_get_file_report( self, icd): f = open(icd.path, mode='r') j = json.load(f) cookie = icd._userdata vtr = self.cookies[cookie] if j['response_code'] == -2: logger.warn("api throttle") self.cursor.execute( """UPDATE backlogfiles SET status = ? WHERE backlogfile = ?""", (vtr.status, vtr.backlogfile)) self.dbh.commit() elif j['response_code'] == -1: logger.warn("something is wrong with your virustotal api key") elif j['response_code'] == 0: # file unknown # mark for submit if vtr.status == 'new': self.cursor.execute( """UPDATE backlogfiles SET status = 'submit', lastcheck_time = strftime("%s",'now') WHERE backlogfile = ?""", (vtr.backlogfile, )) elif vtr.status == 'query': self.cursor.execute( """UPDATE backlogfiles SET lastcheck_time = strftime("%s",'now') WHERE backlogfile = ?""", (vtr.backlogfile, )) self.dbh.commit() elif j['response_code'] == 1: # file known # self.cursor.execute("""UPDATE backlogfiles SET status = 'comment', lastcheck_time = strftime("%s",'now') WHERE backlogfile = ?""", (vtr.backlogfile,)) self.cursor.execute( """DELETE FROM backlogfiles WHERE backlogfile = ?""", (vtr.backlogfile, )) self.dbh.commit() logger.debug("report {}".format(j)) i = incident("dionaea.modules.python.virustotal.report") i.md5hash = vtr.md5hash i.path = icd.path i.report() else: logger.warn("virustotal reported {}".format(j)) del self.cookies[cookie] def scan_file(self, backlogfile, md5_hash, path, status): cookie = str(uuid.uuid4()) self.cookies[cookie] = vtreport(backlogfile, md5_hash, path, status) i = incident("dionaea.upload.request") i._url = "https://www.virustotal.com/vtapi/v2/file/scan" i.apikey = self.apikey i.set('file://file', path) i._callback = "dionaea.modules.python.virustotal_scan_file" i._userdata = cookie i.report() def handle_incident_dionaea_modules_python_virustotal_scan_file(self, icd): f = open(icd.path, mode='r') j = json.load(f) logger.debug("scan_file {}".format(j)) cookie = icd._userdata vtr = self.cookies[cookie] if j['response_code'] == -2: logger.warn("api throttle") self.cursor.execute( """UPDATE backlogfiles SET status = ? WHERE backlogfile = ?""", (vtr.status, vtr.backlogfile)) self.dbh.commit() elif j['response_code'] == -1: logger.warn("something is wrong with your virustotal api key") elif j['response_code'] == 1: scan_id = j['scan_id'] # recycle this entry for the query self.cursor.execute( """UPDATE backlogfiles SET scan_id = ?, status = 'comment', submit_time = strftime("%s",'now') WHERE backlogfile = ?""", ( scan_id, vtr.backlogfile, )) self.dbh.commit() del self.cookies[cookie] def make_comment(self, backlogfile, md5_hash, path, status): cookie = str(uuid.uuid4()) self.cookies[cookie] = vtreport(backlogfile, md5_hash, path, status) i = incident("dionaea.upload.request") i._url = "https://www.virustotal.com/vtapi/v2/comments/put" i.apikey = self.apikey i.comment = self.comment i.resource = md5_hash i._callback = "dionaea.modules.python.virustotal_make_comment" i._userdata = cookie i.report() def handle_incident_dionaea_modules_python_virustotal_make_comment( self, icd): cookie = icd._userdata vtr = self.cookies[cookie] f = open(icd.path, mode='r') try: j = json.load(f) if j['response_code'] == -2: logger.warn("api throttle") self.cursor.execute( """UPDATE backlogfiles SET status = ? WHERE backlogfile = ?""", (vtr.status, vtr.backlogfile)) self.dbh.commit() elif j['response_code'] == -1: logger.warn("something is wrong with your virustotal api key") elif j['response_code'] == 1: self.cursor.execute( """UPDATE backlogfiles SET status = 'query' WHERE backlogfile = ? """, (vtr.backlogfile, )) self.dbh.commit() except Exception as e: pass del self.cookies[cookie]