Beispiel #1
0
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()
Beispiel #2
0
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]