Example #1
0
    def _update_attack(self, db_params):
        # two types of update for DNS:
        #   attack - update attack details (query count, last seen, etc.)
        #   domain - update attack domain-related details (number of returned records, amp rate...)
        addr_int = utils.addr_to_int(db_params['ip'])
        for attack in self.session.query(DBThread.Attack).\
                        filter(DBThread.Attack.src_id == addr_int):
            if attack.domain.domain_name == db_params['dns_name'] and\
               attack.domain.dns_type == db_params['dns_type'] and\
               attack.domain.dns_cls == db_params['dns_class']:

                # attack found, check if this is currently active attack
                if attack.latest + self.new_attack_interval >= db_params['last_seen']:
                    if db_params['type'] == 'attack':
                        # update attack and source last seen time
                        attack.source.last_seen = db_params['last_seen']
                        attack.latest = db_params['last_seen']
                        attack.count = db_params['count']
                    elif db_params['type'] == 'domain':
                        attack.num_entries = db_params['num_entries']
                        attack.amplification = db_params['amp']
                        attack.response = db_params['response']

                    self.session.add(attack)

        self.session.commit()
Example #2
0
    def log_packet(self,
                   msg,
                   addr,
                   sport,
                   dport,
                   timestamp,
                   incoming_pkt,
                   req_size,
                   resp_size,
                   last=False):
        data = {}
        data['time'] = str(timestamp)
        data['src_ip'] = addr
        data['src_port'] = sport
        data['dst_port'] = dport
        data['req_size'] = req_size
        data['resp_size'] = resp_size
        if self.server.log_req_packets:
            data['req_pkt'] = incoming_pkt.decode('ascii')

        raw_json = json.dumps(data)

        self.logger.info('%s - %s' % (msg, raw_json))

        if not last:
            db_params = {
                'ip': utils.addr_to_int(addr),
                'port': sport,
                'dport': dport,
                'time': timestamp,
                'request_pkt': incoming_pkt,
                'input_size': req_size,
                'output_size': resp_size,
            }
            self.server.log_queue.put({
                'type': 'insert',
                'db_params': db_params
            })

        # if last packet, send to hpfeeds and notifier/alerter
        if last:
            if self.server.hpfeeds_client:
                self.server.hpfeeds_client.publish('genericpot.events',
                                                   raw_json)

            # send notification if alerter is enabled
            # THIS OPERATION CAN BE SLOW!
            if self.server.alerter:
                self.server.alerter.alert(addr, int(sport))
Example #3
0
    def _add_attack(self, db_params):
        # ip addresses are stored as integers in order to ensure
        # efficient lookups and additions
        addr_int = utils.addr_to_int(db_params['ip'])

        # check if source and domain already exist in the database and add them if necessary
        source = self.session.query(DBThread.Source).\
                      filter(DBThread.Source.src_ip == addr_int).one_or_none()
        domain = self.session.query(DBThread.Domain).\
                      filter(DBThread.Domain.domain_name == db_params['dns_name']).\
                      filter(DBThread.Domain.dns_type == db_params['dns_type']).\
                      filter(DBThread.Domain.dns_cls == db_params['dns_class']).one_or_none()

        if not source:
            source = DBThread.Source(
                                    src_ip=addr_int,
                                    src_port=db_params['port'],
                                    first_seen=db_params['time'],
                                    last_seen=db_params['time']
                                    )
            self.session.add(source)
            self.session.commit()
        else:
            source.last_seen = db_params['time']
        if not domain:
            domain = DBThread.Domain(
                                    domain_name=db_params['dns_name'],
                                    opcode=db_params['opcode'],
                                    dns_type=db_params['dns_type'],
                                    dns_cls=db_params['dns_class'],
                                    first_seen=db_params['time']
                                    )
            self.session.add(domain)
            self.session.commit()

        # add new attack related to the specified source and domain
        attack = DBThread.Attack(
                                src_id=source.src_ip,
                                domain_id=domain.id,
                                start=db_params['time'],
                                latest=db_params['time']
                                )

        self.session.add(attack)
        self.session.commit()
Example #4
0
def create_server(conf, logger_name, log_queue, output_queue, hpf_client=None, alerter=None):
    global LOGGER_NAME
    LOGGER_NAME = logger_name

    server, ip, port = genpot.create_base_server(
                                                ThreadedNTPServer,
                                                NTPServer,
                                                conf,
                                                logger_name,
                                                log_queue,
                                                output_queue,
                                                hpf_client,
                                                alerter
                                                )

    # parse NTP configuration and apply default settings for mode 3 and mode 7 responses
    # this is done during start-up because these settings are static during server lifetime
    leap = conf.getint('NTP', 'leap')
    precision = conf.getint('NTP', 'precision')
    root_delay = int(conf.get('NTP', 'root_delay'), 16)
    dispersion = int(conf.get('NTP', 'dispersion'), 16)
    ref_id = utils.addr_to_int(conf.get('NTP', 'reference_id'))
    offset = conf.getfloat('NTP', 'timestamp_offset')

    NTPMode3Packet.set_response_defaults(
            leap=leap,
            precision=precision,
            root_delay=root_delay,
            dispersion=dispersion,
            ref_id=ref_id,
            ref_timestamp_offset=offset)

    peer_lists = _get_monlist_peer_lists(conf)
    NTPMode7Packet.set_response_defaults(peer_lists=peer_lists)

    msg = "NTPot started at %s:%d" % (ip, port)
    logging.getLogger(LOGGER_NAME).info(msg)
    print(msg)

    return server
Example #5
0
    def handle(self):
        try:
            addr = self.client_address[0]
            port = self.client_address[1]
            data = self.request[0]
            sock = self.request[1]
            first = False
            last = False

            # parse request and check if valid packet
            # invalid packets are discarded
            # logging only to output log, not to DB
            try:
                request = SSDPServer.SSDPRequest(data)
            except SSDPException as msg:
                self.logger.error('%s:%d - %s' % (addr, port, msg))
                return

            # IP addresses in transaction log and database will be stored as integers/long
            addr_int = utils.addr_to_int(addr)

            now = datetime.datetime.now()
            log_msg = 'New SSDP packet received'

            # check if SSDP request from this IP address was already received - ENTERING CRITICAL SECTION HERE!
            # IP address and ST are the only criteria useful for disrimination of different attacks on the same host
            # other variables (MX, MAN, HOST headers) should be more or less constant and so irrelevant
            with self.server.tx_log_lock:
                req_key = (
                            addr_int,
                            request.st
                          )
                if req_key in self.server.transaction_log:
                    addr_log = self.server.transaction_log[req_key]

                    # check if this is an already existing attack or a new attack
                    # attack is classified as NEW if more than new_attack_duration_interval
                    # minutes have passed since the last seen packet
                    if addr_log['last_seen'] + self.server.new_attack_interval < now:
                        # consider this as a new attack, reset cache data
                        first = True
                        addr_log['count'] = 1
                        addr_log['last_seen'] = now
                        log_msg = 'New attack detected'
                    else:
                        # update transaction log and database last-seen time and packet count and do not respond to the packet
                        addr_log['last_seen'] = now
                        addr_log['count'] += 1

                        # add the pair to the request cache set - this set will be frequently flushed to DB
                        self.server.ip_log.add(req_key)

                        # if count >= threshold, ignore the packet, never respond
                        if addr_log['count'] > self.server.threshold:
                            return
                        # log reaching of threshold and mark packet as last that will be accepted
                        elif addr_log['count'] == self.server.threshold:
                            last = True
                            self.logger.info(
                                    'Threshold reached for host %s and search target %s - will not respond to this host pair' % (addr, request.st))
                            log_msg = 'Last packet - threshold reached'
                else:
                    # add host to transaction log
                    first = True
                    self.server.transaction_log[req_key] = {}
                    self.server.transaction_log[req_key]['last_seen'] = now
                    self.server.transaction_log[req_key]['count'] = 1

            targets = []
            # send info about all supported devices if ssdp:all is specified as ST
            if request.st == 'ssdp:all':
                targets = self.server.device_list
            else:
                for device in self.server.device_list:
                    if request.st == device.device_type or \
                       request.st == device.uuid:
                        targets.append(device)

            b64_resp = b''
            output_size = 0
            responses = []
            for target in targets:
                response = self._create_response(request.st, target)
                responses.append(response)
                b64_resp += base64.b64encode(response)
                output_size += len(response)

            # log packets to file and database
            # log and then send the packets, because sending is interrupted by sleep
            # this is per specification!
            if first or last:
                b64_req = base64.b64encode(data)
                input_size = len(data)
                self.log_packet(
                                log_msg,
                                addr,
                                port,
                                now,
                                request,
                                b64_req,
                                input_size,
                                b64_resp,
                                output_size,
                                last
                                )

            for response in responses:
                sock.sendto(response, self.client_address)
                # sleep random number of seconds between 1 and the value
                # received in the request - per specification
                time.sleep(random.randint(1, int(request.mx)))

        except Exception:
            t = traceback.format_exc()
            self.logger.error('Unknown error during communication with %s:%d - %s' % (addr, port, base64.b64encode(data)))
            self.logger.error('Stacktrace: %s' % t)
Example #6
0
def _get_monlist_peer_lists(conf):
    logger = logging.getLogger(LOGGER_NAME)
    try:
        generate_random = conf.getboolean('monlist', 'generate_random')
        num_peers = conf.getint('monlist', 'peers_num')
        if num_peers < 0:
            logger.error('Invalid peers_num value, must be greater than zero!')
            return

        peers = []

        # get 'daddr' (i.e. local_address) from the config file
        # this value is the same for all the peers
        daddr = utils.addr_to_int(conf.get('monlist', 'local_address'))

        for i in range(1, num_peers + 1):
            peer = None
            if generate_random:
                # while generating data, make it look "realistic"
                # avg_int and count should not differ much between peers!
                # use values generated for the first peer as a baseline
                first_avg_int = 10
                first_count = 50
                if i > 1:
                    first_avg_int = peers[0].avg_int
                    first_count = peers[0].count
                avg_int = random.randint(first_avg_int - 5, first_avg_int + 5)
                last_int = random.randint(0, 100)
                restr = 0
                count = random.randint(first_count - 10, first_count + 10)
                addr = (random.randint(0, 255) << 24) | (random.randint(0, 255) << 16) | \
                        (random.randint(0, 255) << 8) | random.randint(0, 255)

                peer = NTPMode7Packet.MonlistPeer(avg_int, last_int, restr, count, addr, daddr)
                peers.append(peer)
            else:
                try:
                    section = 'peer-' + str(i)
                    avg_int = conf.getint(section, 'avg_int')
                    last_int = conf.getint(section, 'last_int')
                    restr = conf.getint(section, 'restr')
                    count = conf.getint(section, 'count')
                    addr = utils.addr_to_int(conf.get(section, 'addr'))
                    flags = conf.getint(section, 'flags')
                    port = conf.getint(section, 'port')
                    mode = conf.getint(section, 'mode')
                    version = conf.getint(section, 'version')
                    v6_flag = conf.getint(section, 'v6_flag')

                    peer = NTPMode7Packet.MonlistPeer(
                            avg_int,
                            last_int,
                            restr,
                            count,
                            addr,
                            daddr,
                            flags,
                            port,
                            mode,
                            version,
                            v6_flag)
                    peers.append(peer)
                except configparser.NoSectionError:
                    logger.warn('No section %s, ignoring peer...' % section)
                    continue
                except configparser.NoOptionError as msg:
                    logger.warn('Option error: %s. Ignoring peer...' % msg)
                    continue

        peer_lists = [peers[i:i + 6] for i in range(0, len(peers), 6)]
        return peer_lists
    except configparser.Error as msg:
        logger.error('Error occurred while parsing monlist section in configuration file: %s' % msg)
Example #7
0
    def handle(self):
        try:
            data = self.request[0]
            sock = self.request[1]
            addr = self.client_address[0]
            port = self.client_address[1]
            first = False
            last = False

            # ignore empty packets
            if not len(data):
                self.logger.error('%s:%d - %s' % (addr, port, 'Empty packet received'))
                return

            try:
                packet = self._ntp_packet(data)
            except NTPException as msg:
                self.logger.error('%s:%d - %s' % (addr, port, msg))
                return

            # IP addresses in transaction log and database will be stored as integers/long
            addr_int = utils.addr_to_int(addr)

            mode = packet.get_mode()
            now = datetime.datetime.now()
            log_msg = 'New NTP packet received'

            # check if this type of packet was already received - ENTERING CRITICAL SECTION HERE!
            with self.server.tx_log_lock:
                if addr_int in self.server.transaction_log:
                    addr_log = self.server.transaction_log[addr_int]
                    # take mode into account
                    if addr_log['mode'] == mode:
                        # check if this is an already existing attack or a new attack
                        # attack is classified as NEW if more than new_attack_duration_interval
                        # minutes have passed since the last seen packet
                        if addr_log['last_seen'] + self.server.new_attack_interval < now:
                            # consider this as a new attack, reset cache data
                            first = True
                            addr_log['count'] = 1
                            addr_log['last_seen'] = now
                            log_msg = 'New attack detected'
                        else:
                            # update transaction log and database last-seen time and packet count and do not respond to the packet
                            addr_log['last_seen'] = now
                            addr_log['count'] += 1

                            # add the address to ip set - this set will be frequently flushed to DB
                            self.server.ip_log.add(addr_int)

                            # if count >= threshold, ignore the packet, never respond
                            if addr_log['count'] > self.server.threshold:
                                return
                            # log reaching of threshold and mark packet as last that will be accepted
                            elif addr_log['count'] == self.server.threshold:
                                last = True
                                self.logger.info(
                                        'Threshold reached for host %s and mode %d - will not respond to this host/mode pair' % (addr, mode))
                                log_msg = 'Last packet - threshold reached'
                else:
                    # add host to transaction log
                    first = True
                    self.server.transaction_log[addr_int] = {}
                    self.server.transaction_log[addr_int]['mode'] = mode
                    self.server.transaction_log[addr_int]['last_seen'] = now
                    self.server.transaction_log[addr_int]['count'] = 1

            # handle the received packets (list of response packets) and send the appropriate NTP responses (or exit if no responses should be returned)
            try:
                responses = packet.handle()
            except Exception as msg:
                self.logger.error('Error while parsing NTP packet from %s:%d or unable to create proper response: %s' % (addr, port, msg))
                return

            if not len(responses):
                return

            b64_resp = b''
            output_size = 0
            for response in responses:
                sock.sendto(response, self.client_address)
                b64_resp += base64.b64encode(response)
                output_size += len(response)

            # log packet to file and database
            if first or last:
                b64_req = base64.b64encode(data)
                input_size = len(data)
                self.log_packet(
                        log_msg,
                        addr,
                        port,
                        mode,
                        packet.get_mode_name(),
                        now,
                        b64_req,
                        input_size,
                        b64_resp,
                        output_size,
                        last)
        except Exception:
            t = traceback.format_exc()
            self.logger.error('Unknown error during communication with %s:%d - %s' % (addr, port, base64.b64encode(data)))
            self.logger.error('Stacktrace: %s' % t)
Example #8
0
    def handle(self):
        try:
            addr = self.client_address[0]
            port = self.client_address[1]
            dport = self.server.server_address[1]
            data = self.request[0]
            sock = self.request[1]
            first = False
            last = False

            # no need to check for validity, any packet is accepted
            # IP addresses in transaction log and database will be stored as integers/long
            addr_int = utils.addr_to_int(addr)

            now = datetime.datetime.now()
            log_msg = 'New genericpot packet received'

            # check if the request from this IP address was already received - ENTERING CRITICAL SECTION HERE!
            with self.server.tx_log_lock:
                # since generic pot can be ran on any port, use port that the service is running on for
                # attack discrimination
                req_key = (addr_int, dport)
                if req_key in self.server.transaction_log:
                    addr_log = self.server.transaction_log[req_key]

                    # check if this is an already existing attack or a new attack
                    # attack is classified as NEW if more than new_attack_duration_interval
                    # minutes have passed since the last seen packet
                    if addr_log[
                            'last_seen'] + self.server.new_attack_interval < now:
                        # consider this as a new attack, reset cache data
                        first = True
                        addr_log['count'] = 1
                        addr_log['last_seen'] = now
                        log_msg = 'New attack detected'
                    else:
                        # update transaction log and database last-seen time and packet count and do not respond to the packet
                        addr_log['last_seen'] = now
                        addr_log['count'] += 1

                        # add the IP address to the request cache set - this set will be frequently flushed to DB
                        self.server.ip_log.add(req_key)

                        # if count >= threshold, ignore the packet, never respond
                        if addr_log['count'] > self.server.threshold:
                            return
                        # log reaching of threshold and mark packet as last that will be accepted
                        elif addr_log['count'] == self.server.threshold:
                            last = True
                            self.logger.info(
                                'Threshold reached for host %s and port %d - will not respond to this host'
                                % (addr, dport))
                            log_msg = 'Last packet - threshold reached'
                else:
                    # add host to transaction log
                    first = True
                    self.server.transaction_log[req_key] = {}
                    self.server.transaction_log[req_key]['last_seen'] = now
                    self.server.transaction_log[req_key]['count'] = 1

            if self.random:
                # if multiplier is specified, response size is multiplied by request size
                # do not create response that is larger than 1 MB!
                resp_size = self.response_size
                if isinstance(resp_size, float):
                    resp_size = min(int(len(data) * self.response_size),
                                    1048576)
                response = random.randbytes(resp_size)
            else:
                # not random, use predefined response
                response = self.response
                resp_size = self.response_size

            sock.sendto(response, self.client_address)
            if first or last:
                b64_req = base64.b64encode(data)
                input_size = len(data)
                self.log_packet(log_msg, addr, port, dport, now, b64_req,
                                input_size, resp_size, last)
        except Exception:
            t = traceback.format_exc()
            self.logger.error(
                'Unknown error during communication with %s:%d - %s' %
                (addr, port, base64.b64encode(data)))
            self.logger.error('Stacktrace: %s' % t)
Example #9
0
    def handle(self):
        try:
            addr = self.client_address[0]
            port = self.client_address[1]
            data = self.request[0]
            sock = self.request[1]
            first = False
            last = False

            # no need to check for validity. any packet is accepted

            # IP addresses in transaction log and database will be stored as integers/long
            addr_int = utils.addr_to_int(addr)

            now = datetime.datetime.now()
            log_msg = 'New chargen packet received'

            # check if the request from this IP address was already received - ENTERING CRITICAL SECTION HERE!
            with self.server.tx_log_lock:
                if addr_int in self.server.transaction_log:
                    addr_log = self.server.transaction_log[addr_int]

                    # check if this is an already existing attack or a new attack
                    # attack is classified as NEW if more than new_attack_duration_interval
                    # minutes have passed since the last seen packet
                    if addr_log[
                            'last_seen'] + self.server.new_attack_interval < now:
                        # consider this as a new attack, reset cache data
                        first = True
                        addr_log['count'] = 1
                        addr_log['last_seen'] = now
                        log_msg = 'New attack detected'
                    else:
                        # update transaction log and database last-seen time and packet count and do not respond to the packet
                        addr_log['last_seen'] = now
                        addr_log['count'] += 1

                        # add the IP address to the request cache set - this set will be frequently flushed to DB
                        self.server.ip_log.add(addr_int)

                        # if count >= threshold, ignore the packet, never respond
                        if addr_log['count'] > self.server.threshold:
                            return
                        # log reaching of threshold and mark packet as last that will be accepted
                        elif addr_log['count'] == self.server.threshold:
                            last = True
                            self.logger.info(
                                'Threshold reached for host %s - will not respond to this host'
                                % addr)
                            log_msg = 'Last packet - threshold reached'
                else:
                    # add host to transaction log
                    first = True
                    self.server.transaction_log[addr_int] = {}
                    self.server.transaction_log[addr_int]['last_seen'] = now
                    self.server.transaction_log[addr_int]['count'] = 1

            # access needs to be synchronized since multiple threads can transform the alphabet
            # response is shifted alphabet, depending on response size and line length in config
            response = ''
            with self.server.alphabet_lock:
                for i in range(1 + int(self.response_size / self.line_len)):
                    suf_len = min(self.line_len,
                                  abs(self.response_size - len(response) - 2))
                    response += self.server.alphabet[0:suf_len] + '\r\n'
                    self.server.alphabet = self.server.alphabet[
                        1:] + self.server.alphabet[0]

                    # last line
                    if suf_len < self.line_len:
                        break

            sock.sendto(response.encode('ascii'), self.client_address)
            if first or last:
                b64_req = base64.b64encode(data)
                input_size = len(data)
                output_size = len(response)
                self.log_packet(log_msg, addr, port, now, b64_req, input_size,
                                output_size, last)
        except Exception:
            t = traceback.format_exc()
            self.logger.error(
                'Unknown error during communication with %s:%d - %s' %
                (addr, port, base64.b64encode(data)))
            self.logger.error('Stacktrace: %s' % t)