def _handle_request(self, client_request):
        request_id, server_mtype, record = (client_request.mac, client_request.xID), DHCP.NOT_SET, None
        Log.debug(f'REQ | TYPE={client_request.mtype}, ID={request_id}')

        if (client_request.mtype == DHCP.RELEASE):
            self._release(client_request.ciaddr, client_request.mac)

        elif (client_request.mtype == DHCP.DISCOVER):
            server_mtype, record = self._discover(request_id, client_request)

        elif (client_request.mtype == DHCP.REQUEST):
            server_mtype, record = self._request(request_id, client_request)

        # TODO: logging purposes only. probably isnt needed. the below condition are protected by
        # the initiated value being "DHCP.NOT_SET" so we dont need to cover for them.
        else:
            Log.warning(f'Unknown request type from {client_request.mac}')

        # this is filtering out response types like dhcp nak | modifying lease before
        # sending to ensure a power failure will have persistent record data.
        if (server_mtype not in [DHCP.NOT_SET, DHCP.DROP, DHCP.NAK]):
            self.leases.modify( # pylint: disable=no-member
                client_request.handout_ip, record
            )

        # only types specified in list require a response.
        if (server_mtype in [DHCP.OFFER, DHCP.ACK, DHCP.NAK]):
            client_request.generate_server_response(server_mtype)

            self.send_to_client(client_request, server_mtype)
Exemple #2
0
    def _handle_request(self, client_request):
        request_id, response_mtype = (client_request.mac,
                                      client_request.xID), None
        Log.debug(f'REQ | TYPE={client_request.mtype}, ID={request_id}')
        if (client_request.mtype == DHCP.RELEASE):
            self._release(client_request.ip, client_request.mac)

        elif (client_request.mtype == DHCP.DISCOVER):
            response_mtype, record = self._discover(request_id, client_request)

        elif (client_request.mtype == DHCP.REQUEST):
            response_mtype, record = self._request(request_id, client_request)

        if (response_mtype):
            client_request.generate_server_response(response_mtype)

            # this is filtering out response types like dhcp nak
            if (record):
                self.leases.modify(  # pylint: disable=no-member
                    client_request.handout_ip, record)

            self.send_to_client(client_request)

        else:
            Log.warning(f'Unknown request type from {client_request.mac}')
Exemple #3
0
    def _handle_request(self, client_request):
        request_id, request_mtype = (client_request.mac,
                                     client_request.xID), None
        Log.debug(f'REQ | TYPE={client_request.mtype}, ID={request_id}')

        if (client_request.mtype == DHCP.RELEASE):
            self._release(client_request.ciaddr, client_request.mac)

        elif (client_request.mtype == DHCP.DISCOVER):
            request_mtype, record = self._discover(request_id, client_request)

        elif (client_request.mtype == DHCP.REQUEST):
            request_mtype, record = self._request(request_id, client_request)

        # TODO: i believe this is why the fatal exception was being raised. the log system
        # was not working so i cant prove it yet. adding a return just in case, which should be
        # there anyways as this condition is null and should not be responded to anyways.
        else:
            Log.warning(f'Unknown request type from {client_request.mac}')

            return

        if (request_mtype):
            client_request.generate_server_response(request_mtype)

            # this is filtering out response types like dhcp nak
            if (record):
                self.leases.modify(  # pylint: disable=no-member
                    client_request.handout_ip, record)

            self.send_to_client(client_request, request_mtype)
Exemple #4
0
    def Start(self):
        self.LoadInterfaces()

        self.Log = LogHandler(self)
        self.Syslog = SyslogHandler(self)
        self.Automate = Automate(self)

        self.DNSCache = DNSCache(self)
        self.TLSRelay = TLSRelay(self)
        self.UDPRelay = UDPRelay(self)
        self.DNSRelay = DNSRelay(self)

        ListFile = ListFiles()
        ListFile.CombineDomains()
        ListFile.CombineKeywords()

        self.LoadKeywords()
        self.LoadTLDs()
        self.LoadSignatures()

        Sniffer = DNSSniffer(self)
        # setting from_proxy arg to True to have the sniffer sleep for 5 seconds while settings can initialize
        threading.Thread(target=Sniffer.Start, args=(True,)).start()
        threading.Thread(target=self.DNSRelay.Start).start()

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        asyncio.run(self.RecurringTasks())
def get_current_bytes():
    try:
        return {iface[0][1][:-1]:[iface[3][1], iface[5][1]] for iface
            in [[y.split() for y in x.split('\\')] for x in check_output('ip -s -o link', shell=True).decode().splitlines() \
            if 'wan' in x or 'lan' in x]}
    except Exception as E:
        Log = LogHandler(module=LOG_MOD)
        Log.message(f'Interface bandwidth module error | {E}')
 def update_startup_script(self):
     UpdateScript = US()
     try:
         UpdateScript.run()
     except Exception:
         Log.error('DNX update script failed to run on startup.')
     else:
         Log.notice('DNX update script ran on startup.')
         os.remove(_update_script)
    def _release(self, ip_address, mac_address):
        dhcp = ServerResponse(server=self)

        # if mac/ lease mac match, the lease will be removed from the table
        if dhcp.release(ip_address, mac_address):
            self.leases.modify(ip_address) # pylint: disable=no-member

        else:
            Log.warning(f'Client {mac_address} attempted invalid release.')
Exemple #8
0
    def send_to_client(client_request):
        if (client_request.bcast):
            Log.debug(f'Sent BROADCAST to 255.255.255.255:68')
            client_request.sock.sendto(client_request.send_data,
                                       (f'{BROADCAST}', 68))

        else:
            Log.debug(f'Sent UNICAST to {client_request.handout_ip}:68')
            client_request.sock.sendto(client_request.send_data,
                                       (f'{client_request.handout_ip}', 68))
Exemple #9
0
    def __init__(self):
        self.Log = LogHandler(LOG_MOD)
        # add configuration check for icmp checks prior to handing out ip.
        self.icmp_check = True
        self.Leases = DHCPLeases(self)

        self.ongoing = {}

        self.handout_lock = threading.Lock()
        self.log_queue_lock = threading.Lock()
    def send_to_client(client_request, request_mtype):
        if (request_mtype is DHCP.RENEWING):
            client_request.sock.sendto(client_request.send_data, (f'{client_request.ciaddr}', 68))

            Log.debug(f'Sent unicast to {client_request.ciaddr}:68')

        # NOTE: sending broadcast because f**k.
        else:
            client_request.sock.sendto(client_request.send_data, (f'{BROADCAST}', 68))

            Log.debug(f'Sent broadcast for {client_request.handout_ip} to 255.255.255.255:68')
Exemple #11
0
    def _get_available_ip(self):
        local_net = list(self._intf_net)[self._r_start:self._r_end]
        for _ in range(len(local_net)):
            ip_address = _fast_choice(local_net)

            if not self.is_available(ip_address): continue

            if (self._check_icmp_reach): # TODO: figure out how we will get notified
                if icmp_reachable(ip_address): continue

            return ip_address
        else:
            Log.critical('IP handout error. No available IPs in range.') # TODO: comeback
    def start(self):

        self.get_interface_settings()

        self.Log = LogHandler(process=self)
        self.Automate = Automate(self)
        self.SyslogUDP = UDPMessage(self)
        self.SyslogTCP = TCPMessage(self)

        self.automate_threads()
        self.initialize()

        self._ready_interface_service()
Exemple #13
0
    def Start(self):
        self.LoadInterfaces()

        self.Automate = Automate(self)
        self.Log = LogHandler(self)
        self.Syslog = SyslogHandler(self)

        Sniffer = IPSSniffer(self)
        # True boolean notifies thread that it was the initial start and to minimize sleep time
        threading.Thread(target=Sniffer.Start, args=(True,)).start()

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        asyncio.run(self.Main())
    def _get_available_ip(self):
        handout_range = self._handout_range
        for _ in range(len(handout_range)):
            ip_address = _fast_choice(handout_range)

            if not self._is_available(ip_address): continue

            if (self._check_icmp_reach
                ):  # TODO: figure out how we will get notified
                if icmp_reachable(ip_address): continue

            return ip_address
        else:
            Log.critical('IP handout error. No available IPs in range.'
                         )  # TODO: comeback
Exemple #15
0
    def timeout_status():
        with ConfigurationManager('license') as dnx:
            dnx_license = dnx.load_configuration()['license']

            if not dnx_license['activated']: return

            timestamp = dnx_license['timestamp']
            if (fast_time() - timestamp >= ONE_DAY
                    and dnx_license['validated']):

                dnx_license['validated'] = False

                dnx.write_configuration(dnx_license)

                Log.warning(
                    'DNX license has been invalidated because it has not contacted the license server in 24 hours.'
                )
Exemple #16
0
def handle_system_action(page_settings):
    action = page_settings['action']

    response = request.form.get(f'system_{action}', None)
    if (response == 'YES'):
        page_settings.pop('control', None)
        page_settings.pop('user_role', None)
        page_settings.update({'confirmed': True, 'login_btn': True})

        Log.warning(f'DNX Firewall {action} initiated.')

        system_action_method = getattr(System, action)
        threading.Thread(target=system_action_method).start()

    elif (response == 'NO'):
        return redirect(url_for('dashboard'))

    return render_template('dnx_device.html', **page_settings)
Exemple #17
0
    def user_login(cls, form, login_ip):
        '''function to authenticate user to the dnx web frontend. pass in flask form and source ip. return
        will be a boolean representing whether user is authenticated/authorized or not.'''
        self = cls()

        threading.Thread(target=self._login_timer).start()

        authorized, username, user_role = self._user_login(form, login_ip)
        if (authorized):
            Log.simple_write(
                LOG_NAME, 'notice',
                f'User {username} successfully logged in from {login_ip}.')

        else:
            Log.simple_write(
                LOG_NAME, 'warning',
                f'Failed login attempt for user {username} from {login_ip}.')

        while not self._time_expired:
            time.sleep(.202)

        return authorized, username, user_role
Exemple #18
0
                              file_path='dnx_frontend') as session_tracker:
        stored_tracker = session_tracker.load_configuration()

        if (action is CFG.ADD):
            stored_tracker['active_users'][username] = {
                'user_role': user_role,
                'remote_addr': remote_addr,
                'logged_in': time.time(
                ),  # NOTE: can probably make this human readable format here.
                'last_seen': None
            }

        elif (action is CFG.DEL):
            stored_tracker['active_users'].pop(username, None)

        session_tracker.write_configuration(stored_tracker)


@app.before_request
def user_timeout():
    session.permanent = True
    app.permanent_session_lifetime = timedelta(minutes=30)
    session.modified = True


## SETUP LOGGING CLASS
Log.run(name=LOG_NAME)

if __name__ == '__main__':
    app.run(debug=True)
    def __init__(self):
        self._Log = Log()

        self._time_expired = threading.Event()
class Authentication:
    def __init__(self):
        self._Log = Log()

        self._time_expired = threading.Event()

    @staticmethod
    ## see if this is safe. if use returns something outside of dictionary, error will occur.
    def get_user_role(username):
        local_accounts = load_configuration('logins')['users']
        try:
            return local_accounts[username]['role']
        except KeyError:
            return None

    @classmethod
    def user_login(cls, form, login_ip):
        '''function to authenticate user to the dnx web frontend. pass in flask form and source ip. return
        will be a boolean representing whether user is authenticated/authorized or not.'''
        self = cls()

        threading.Thread(target=self._login_timer).start()

        authorized, username, user_role = self._user_login(form, login_ip)
        if (authorized):
            self._Log.simple_write(
                LOG_NAME, 'notice',
                f'User {username} successfully logged in from {login_ip}.')

        else:
            self._Log.simple_write(
                LOG_NAME, 'warning',
                f'Failed login attempt for user {username} from {login_ip}.')

        while not self._time_expired:
            time.sleep(.202)

        return authorized, username, user_role

    def _user_login(self, form, login_ip):
        password = form.get('password', None)
        username = form.get('username', '').lower()
        if (not username or not password):
            return False, None, None

        hexpass = self.hash_password(username, password)

        if not self._user_authorized(username, hexpass):
            return False, username, None

        # checking for web ui authorization (admins/users only. cli accounts will fail.)
        user_role = self.get_user_role(username)
        if user_role not in ['admin', 'user']:
            return False, username, None

        return True, username, user_role

    def hash_password(self, username, password):
        salt_one = len(username)
        salt_two = len(password)

        if (salt_two > salt_one):
            salt = salt_two / salt_one
        else:
            salt = salt_one / salt_two

        index = int(float(salt_one / 2))
        part_one = username[:index]
        part_two = username[index:]

        salt = f'{part_one}{salt}{part_two}'
        password = f'{password}{salt}'.encode('utf-8')

        hash_object = hashlib.sha256(password).hexdigest()
        hash_part = f'{hash_object}'[:salt_one * 2]

        hash_total = f'{hash_part}{hash_object}'.encode('utf-8')
        hash_total = hashlib.sha256(hash_total)

        return hash_total.hexdigest()

    def _user_authorized(self, username, hexpass):
        local_accounts = load_configuration('logins')['users']
        try:
            password = local_accounts[username]['password']
        except KeyError:
            return False
        else:
            # returning True on password match else False
            return password == hexpass

    def _login_timer(self):
        time.sleep(.6)
        self._time_expired.set()
Exemple #21
0
class DHCPServer:
    def __init__(self):
        self.Log = LogHandler(LOG_MOD)
        # add configuration check for icmp checks prior to handing out ip.
        self.icmp_check = True
        self.Leases = DHCPLeases(self)

        self.ongoing = {}

        self.handout_lock = threading.Lock()
        self.log_queue_lock = threading.Lock()

    def Start(self):
        self.LoadInterfaces()
        # -- Creating Lease Dictionary -- #
        self.Leases.BuildRange()
        self.Leases.LoadLeases()
        # -- Building Server Options Dictionary -- #
        self.SetServerOptions()
        # -- Creating socket && starting main server thread -- #
        self.CreateSocket()
        threading.Thread(target=self.Server).start()

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        asyncio.run(self.Main())

    async def Main(self):
        ## -- Starting Server and Timers in Asyncio Gather -- ##
        await asyncio.gather(self.Leases.LeaseTimer(), self.Leases.ReservationTimer(),
                            self.Leases.WritetoFile(), self.Log.QueueHandler(self.log_queue_lock))

    def CreateSocket(self):
        self.s = socket(AF_INET, SOCK_DGRAM)
        self.s.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)
        self.s.setsockopt(SOL_SOCKET, SO_BROADCAST,1)
        self.s.setsockopt(SOL_SOCKET, SO_BINDTODEVICE, self.bind_int)
        self.s.bind(('0.0.0.0', 67))
        print('[+] Listening on Port 67')

    def Server(self):
        while True:
            try:
                Parse = DHCPParser(self.dhcp_server_options)
                rdata = self.s.recv(1024) ## removed recvfrom, dont need since ip of client is identified in packet
                print(rdata)
                response_info, options = Parse.Data(rdata)
                options = Parse.Options(options)
                threading.Thread(target=self.Response, args=(response_info, options)).start()
            except Exception as E:
                print(E)

    def Response(self, response_info, options):
        print('Response Thread')
        mtype, xID, ciaddr, mac_address = response_info
        if (mac_address in self.ongoing and self.ongoing[mac_address] != xID):
            options[53] = [1, DHCP_NAK]
        elif mtype == DHCP_DISCOVER:
            options[53] = [1, DHCP_OFFER]
        elif mtype == DHCP_REQUEST:
            options[53] = [1, DHCP_ACK]
        elif mtype == DHCP_RELEASE:
            self.Leases.Release(ciaddr, mac_address)

        if (mtype != DHCP_RELEASE):
            self.SendResponse(response_info, options)

    def SendResponse(self, response_info, options):
        print('Send Thread')
        mtype, xID, ciaddr, mac_address = response_info
        mtype = options[53][1]
        ## -- Set ongiong request flag, NAK duplicates -- ##
        if (mtype != DHCP_NAK):
            self.ongoing[mac_address] = xID
            threading.Thread(target=self.OngoingTimer, args=(mac_address,)).start()

        ## locking handout method call to ensure checking/setting leases is thread safe
        with self.handout_lock:
            handout_ip = self.Leases.Handout(mac_address)

        response_info =  xID, mac_address, ciaddr, handout_ip, options
        Response = DHCPResponse(response_info)
        sdata = Response.Assemble()
        if (mtype in {DHCP_OFFER, DHCP_ACK, DHCP_NAK} and handout_ip):
            if (ciaddr == '0.0.0.0'):
                print(f'Sent BROADCAST TYPE: {mtype} to 255.255.255.255:68')
                self.s.sendto(sdata, ('255.255.255.255', 68))
            else:
                print(f'Sent UNICAST TYPE: {mtype} to {ciaddr}:68')
                self.s.sendto(sdata, (ciaddr, 68))

        ## -- Remove ongiong request flag, NAK duplicates -- ##
        if (mtype == DHCP_ACK and handout_ip):
            self.ongoing.pop(mac_address, None)

    def SetServerOptions(self):
        print('[+] DHCP: Setting server options.')
        insideip, netmask, broadcast, mtu = self.InterfaceInfo()
        dhcp_server_options = {}

        dhcp_server_options[1] = [4, inet_aton(netmask)]         # OPT 1  | Subnet Mask
        dhcp_server_options[3] = [4, inet_aton(insideip)]        # OPT 3  | Router
        dhcp_server_options[6] = [4, inet_aton(insideip)]        # OPT 6  | DNS Server
        dhcp_server_options[26] = [2, mtu]                       # OPT 26 | MTU
        dhcp_server_options[28] = [4, inet_aton(broadcast)]      # OPT 28 | Broadcast
        dhcp_server_options[51] = [4, 86400]                     # OPT 51 | Lease Time
        dhcp_server_options[54] = [4, inet_aton(insideip)]       # OPT 54 | Server Identity
        dhcp_server_options[58] = [4, 43200]                     # OPT 58 | Renew Time
        dhcp_server_options[59] = [4, 74025]                     # OPT 59 | Rebind Time

        self.dhcp_server_options = dhcp_server_options

    def OngoingTimer(self, mac):
        time.sleep(6)
        self.ongoing.pop(mac, None)

    def InterfaceInfo(self):
        Interface = Int()
        insideip = Interface.IP(self.lan_int)
        netmask = Interface.Netmask(self.lan_int)
        broadcast = Interface.Broadcast(self.lan_int)
        mtu = Interface.MTU(self.lan_int)

        return(insideip, netmask, broadcast, mtu)

    def LoadInterfaces(self):
        with open(f'{HOME_DIR}/data/config.json', 'r') as settings:
            setting = json.load(settings)

        self.lan_int = setting['settings']['interface']['inside']
        self.bind_int = f'{self.lan_int}\0'.encode('utf-8')
Exemple #22
0
class DNSProxy:
    ''' Main Class for DNS Proxy. This class directly controls the logic regarding the signatures, whether something
        should be blocked or allowed, managing signature updates from user front end configurations. This class also
        serves as a bridge between the DNS Proxy Sniffer and DNS Relay give them a single point to flag traffic and
        identify traffic that should not be relayed/blocked via the class variable "flagged_traffic" dictionary. If
        the Proxy sniffer detects traffic that should be blocked it inputs the connection info into the dictionary
        for the DNS Relay to refer to before relaying the traffic. If the query information matches a dictionary item
        the DNS Relay will not forward the traffic to the configured public resolvers. '''

    def __init__(self):
        self.ip_whitelist = {}
        self.dns_whitelist = {}
        self.dns_blacklist = {}
        self.dns_sigs = {}
        self.dns_records = {}
        self.dns_servers = {}

        self.allowed_request = {}
        self.flagged_request = {}

        self.shared_decision_lock = threading.Lock()
        self.log_queue_lock = threading.Lock()

        self.logging_level = 0
        self.syslog_enabled = False

    ''' Start Method to Initialize All proxy configurations, including cleaning the database tables to the configures
        length. Starting a child thread for DNS Relay and DNS Proxy Sniffer, to handle requests, and doing an AsyncIO
        gather on proxy timer methods in main thread for rule updates '''
    def Start(self):
        self.LoadInterfaces()

        self.Log = LogHandler(self)
        self.Syslog = SyslogHandler(self)
        self.Automate = Automate(self)

        self.DNSCache = DNSCache(self)
        self.TLSRelay = TLSRelay(self)
        self.UDPRelay = UDPRelay(self)
        self.DNSRelay = DNSRelay(self)

        ListFile = ListFiles()
        ListFile.CombineDomains()
        ListFile.CombineKeywords()

        self.LoadKeywords()
        self.LoadTLDs()
        self.LoadSignatures()

        Sniffer = DNSSniffer(self)
        # setting from_proxy arg to True to have the sniffer sleep for 5 seconds while settings can initialize
        threading.Thread(target=Sniffer.Start, args=(True,)).start()
        threading.Thread(target=self.DNSRelay.Start).start()

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        asyncio.run(self.RecurringTasks())

    def SignatureCheck(self, packet):
        timestamp = time.time()

        dns_record = False
        whitelisted_query = False
        redirect = False
        log_connection = False
        category = None

        request_info = {'src_ip': packet.src_ip, 'src_port': packet.src_port, 'request': {1: packet.request, 2: packet.request2}}
        if (packet.request in self.dns_records):
            dns_record = True

        # Whitelist check of FQDN then overall domain ##
        elif (packet.request in self.dns_whitelist or packet.request2 in self.dns_whitelist or packet.src_ip in self.ip_whitelist):
            whitelisted_query = True

        ## will prevent all other checks from being processed if a local dns record is found for domain (for performance)
        if (dns_record):
            self.ApplyDecision(request_info, decision=FLAGGED)

        ## P1. Standard Category blocking of FQDN || if whitelisted, will check to ensure its not a malicious category
        ## before allowing it to continue
        elif (packet.request in self.dns_sigs):
            redirect, reason, category = self.StandardBlock(request_info, whitelisted_query)

        ## P2. Standard Category blocking of overall domain || micro.com if whitelisted, will check to ensure its not
        ## a malicious category before allowing it to continue
        elif (packet.request2 in self.dns_sigs):
            redirect, reason, category = self.StandardBlock(request_info, whitelisted_query, position=2)

        ## P1/P2 Blacklist block of FQDN if not whitelisted ##
        elif (not whitelisted_query) and (packet.request in self.dns_blacklist or packet.request2 in self.dns_blacklist):
            print(f'Blacklist Block: {packet.request}')
            redirect = True
            reason = 'blacklist'
            category = 'time based'

        ## TLD (top level domain) block ##
        elif (packet.request_tld in self.tlds):
            print(f'TLD Block: {packet.request}')
            redirect = True
            category = packet.request_tld
            reason = 'tld filter'

        ## Keyword Search within domain || block if match ##
        else:
            for keyword, cat in self.keywords.items():
                if (keyword in packet.request):
                    redirect = True
                    reason = 'keyword'
                    category = cat
                    break

        ## Redirect to firewall if traffic match/blocked ##
        if (redirect):
            self.ApplyDecision(request_info, decision=FLAGGED)
            DNSResponse(packet, self.lan_int, self.lan_ip).Response()

        ##Redirect to IP Address in local DNS Record (user configurable)
        elif (dns_record):
            record_ip = self.dns_records.get(packet.request)
            DNSResponse(packet, self.lan_int, record_ip).Response()
        else:
            self.ApplyDecision(request_info, decision=ALLOWED)

        ## Log to Infected Clients DB Table if matching malicious type categories
        if (category in {'malicious', 'cryptominer'} and self.logging_level >= ALERT):
            table ='infectedclients'
            if (category in {'malicious'}):
                reason = 'malware'
            elif (category in {'cryptominer'}):
                reason = 'cryptominer'

            logging_options = {'infected_client': packet.src_mac, 'src_ip': packet.src_ip, 'detected_host': packet.request, 'reason': reason}
            self.TrafficLogging(table, timestamp, logging_options)

        # logs redirected/blocked requests
        if (redirect and self.logging_level >= NOTICE):
            action = 'blocked'
            log_connection = True

        # logs all requests, regardless of action of proxy if not already logged
        elif (not redirect and self.logging_level >= INFORMATIONAL):
            category = 'N/A'
            reason = 'logging'
            action = 'allowed'
            log_connection = True

        if (log_connection):
            table = 'dnsproxy'
            logging_options = {'src_ip': packet.src_ip, 'request': packet.request, 'category': category ,
                                'reason': reason, 'action': action}

            self.TrafficLogging(table, timestamp, logging_options)

    def StandardBlock(self, request_info, whitelisted_query, position=1):
        redirect = False
#        print(f'Standard Block: {request}')
        reason = 'category'
        category = self.dns_sigs[request_info['request'][position]]
        if (not whitelisted_query or category in {'malicious', 'cryptominer'}):
            redirect = True

        return redirect, reason, category

    def ApplyDecision(self, request_info, decision):
        info = SName(**request_info)
        request_tracker = getattr(self, f'{decision}_request')
        try:
            request_tracker[info.src_ip].update({info.src_port: info.request[1]})
        except KeyError:
            request_tracker[info.src_ip] = {info.src_port: info.request[1]}
        # else:
            # self.Log.AddtoQueue(f'Client Source port overlap detected: {src_ip}:{src_port}')

    def TrafficLogging(self, table, timestamp, logging_options):
        ProxyDB = DBConnector(table)
        ProxyDB.Connect()
        if (table in {'dnsproxy'}):
            ProxyDB.StandardInput(timestamp, logging_options)

            if (self.syslog_enabled):
                self.AlertSyslog(logging_options)

        elif (table in {'infectedclients'}):
            ProxyDB.InfectedInput(timestamp, logging_options)

        ProxyDB.Disconnect()

    def AlertSyslog(self, logging_options):
        opt = SName(**logging_options)

        if (opt.category in {'malicious', 'cryptominer'}):
            msg_level = ALERT
        else:
            if (opt.action == 'blocked'):
                msg_level = NOTICE
            elif (opt.action == 'allowed'):
                msg_level = INFORMATIONAL

        message = f'src.ip={opt.src_ip}; request={opt.request}; category={opt.category}; '
        message += f'filter={opt.reason}; action={opt.action}'
        self.Syslog.Message(EVENT, msg_level, message)

    def LoadSignatures(self):
        with open(f'{HOME_DIR}/data/whitelist.json', 'r') as whitelists:
            whitelist = json.load(whitelists)
        wl_exceptions = whitelist['whitelists']['exceptions']

        with open(f'{HOME_DIR}/data/blacklist.json', 'r') as blacklists:
            blacklist = json.load(blacklists)
        bl_exceptions = blacklist['blacklists']['exceptions']

        with open(f'{HOME_DIR}/dnx_domainlists/blocked.domains', 'r') as blocked:
            while True:
                line = blocked.readline().strip().split()
                if (not line):
                    break
                if (line != '\n'):
                    domain = line[0]
                    category = line[1]
                    if (domain not in wl_exceptions):
                        self.dns_sigs[domain] = category

            for domain in bl_exceptions:
                self.dns_sigs[domain] = 'blacklist'

    def LoadTLDs(self):
        self.tlds = set()
        with open(f'{HOME_DIR}/data/dns_proxy.json', 'r') as tlds:
            tld = json.load(tlds)
        all_tlds = tld['dns_proxy']['tlds']

        for entry, info in all_tlds.items():
            tld_enabled = info['enabled']
            if (tld_enabled):
                self.tlds.add(entry)

    ## consider making a combine keywords file. this would be in line with ip and domain categories
    def LoadKeywords(self):
        self.keywords = {}

        with open(f'{HOME_DIR}/dnx_domainlists/blocked.keywords', 'r') as blocked:
            while True:
                line = blocked.readline().strip().split()
                if (not line):
                    break
                if (line != '\n'):
                    keyword = line[0]
                    cat = line[1]

                    self.keywords[keyword] = cat

    def LoadInterfaces(self):
        with open(f'{HOME_DIR}/data/config.json', 'r') as settings:
            setting = json.load(settings)

        self.lan_int = setting['settings']['interface']['inside']
        self.wan_int = setting['settings']['interface']['outside']

        Int = Interface()
        self.lan_ip = Int.IP(self.lan_int)
        self.wan_ip = Int.IP(self.wan_int)

    # AsyncIO method called to gather automated/ continuous methods | this is python 3.7 version of async
    async def RecurringTasks(self):
        await asyncio.gather(self.Automate.Settings(), self.Automate.Reachability(),
                            self.Automate.DNSRecords(), self.Automate.UserDefinedLists(),
                            self.Automate.ClearCache(), self.Syslog.Settings(SYSLOG_MOD),
                            self.Log.Settings(LOG_MOD), self.Log.QueueHandler(self.log_queue_lock))
Exemple #23
0
class IPSProxy:
    def __init__(self):
        self.udp_scan_tracker = {}
        self.tcp_scan_tracker = {}
        self.udp_active_scan = {}
        self.tcp_active_scan = {}

        self.fw_rules = {}
        self.ip_whitelist = {}

        self.tcp_ddos_tracker = {}
        self.udp_ddos_tracker = {}
        self.icmp_ddos_tracker = {}
        self.active_ddos = False
        self.block_length = 0

        self.fw_rule_creation_lock = threading.Lock()
        self.scan_tracker_lock = threading.Lock()
        self.ddos_counter_lock = threading.Lock()

        self.protocol_conversion = {TCP: 'tcp', UDP: 'udp', ICMP: 'icmp'}

        self.ddos_prevention = False
        self.portscan_prevention = False
        self.portscan_reject = False
        self.syslog_enabled = False
        self.icmp_allow = False
        self.logging_level = 0

    def Start(self):
        self.LoadInterfaces()

        self.Automate = Automate(self)
        self.Log = LogHandler(self)
        self.Syslog = SyslogHandler(self)

        Sniffer = IPSSniffer(self)
        # True boolean notifies thread that it was the initial start and to minimize sleep time
        threading.Thread(target=Sniffer.Start, args=(True,)).start()

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        asyncio.run(self.Main())

    def SignatureCheck(self, packet, connection_type):
        timestamp = time.time()
        add_to_tracker = False

        str_proto = self.protocol_conversion[packet.protocol]
        if (packet.protocol != ICMP):
            open_ports = getattr(self, f'open_{str_proto}_ports')

        ## if source ip is in the whitelist, it will not be added to the DDOS tracker or be check as a potential port scanner
        if (packet.src_ip in self.ip_whitelist or packet.dst_ip in self.ip_whitelist):
            pass

        elif (packet.protocol == ICMP and self.icmp_allow):
            add_to_tracker = True

        elif (connection_type == INITIAL and packet.dst_port in open_ports):
            add_to_tracker = True

        ## if an above condition is met, a counter will be added to corresponsing tracker to determine PPS in another thread
        if (add_to_tracker):
            ddos_tracker = getattr(self, f'{str_proto}_ddos_tracker')
            # lock to ensure packet counts remain accurate between all threads
            with self.ddos_counter_lock:
                tracked = ddos_tracker.get(packet.src_ip, None)
                if (not tracked):
                    ddos_tracker.update({packet.src_ip: {'count': 1, 'timestamp': time.time()}})
                else:
                    count = tracked['count'] + 1
                    ddos_tracker[packet.src_ip].update({'count': count})

        # will prevent packet from being inspected if it is either icmp (no ports to scan) or if an ddos is currently active to
        # give more system resources to dealing with the ddos
        if (not self.active_ddos):
            self.PortScan(packet, connection_type, timestamp)

    def PortScan(self, packet, connection_type, timestamp):
        attack_type = PORTSCAN
        connection_log = False
        scan_detected = False
        active_block = False
        already_blocked = False
        block_status = None

        if (packet.src_ip != self.wan_ip):
            direction = INBOUND
            tracked_ip = packet.src_ip
            tracked_port = packet.src_port
            local_port = packet.dst_port

        elif (packet.src_ip == self.wan_ip):
            direction = OUTBOUND
            tracked_ip = packet.dst_ip
            tracked_port = packet.dst_port
            local_port = packet.src_port

        ## Main Detection Logic
        proto = self.protocol_conversion[packet.protocol]
        active_scan = getattr(self, f'{proto}_active_scan')
        scan_tracker = getattr(self, f'{proto}_scan_tracker')
        if (direction == INBOUND and tracked_ip in active_scan):
            active_scan[tracked_ip] = timestamp
            scan_detected = True
            with self.scan_tracker_lock:
                if (local_port not in scan_tracker[tracked_ip]['target']):
                    scan_tracker[tracked_ip]['target'].update({local_port: False})

        # will match if packet is a tcp syn and the source ip is not the wan ip (iniated from external ip)
        elif (connection_type == INITIAL):
            # if first time the source ip is seen, it will add ip to dictionary
            try:
                count = scan_tracker[tracked_ip]['source'].get(tracked_port, 0) + 1
            except KeyError:
                scan_tracker[tracked_ip] = {'source': {}, 'target': {}}
                count = 1

            scan_tracker[tracked_ip]['source'].update({tracked_port: count})
            scan_tracker[tracked_ip]['target'].update({local_port: False})

            connections = scan_tracker.get(tracked_ip)['target']
            if (count >= 2 or len(connections) >= 3) or (packet.protocol == UDP and not packet.udp_payload):
                active_scan[tracked_ip] = timestamp
                active_block = True
                scan_detected = True

#            print(f'INCREMENTED | {tracked_ip}: {tracked_port} > {count} | {local_port}')

        # will match if wan ip is responding to a tcp stream being initiated
        elif (connection_type == RESPONSE):
            try:
                open_ports = getattr(self, f'open_{proto}_ports')
                if (direction == OUTBOUND and local_port in open_ports):
                    print(f'{proto.upper()} PORT RESPONSE: {timestamp}')
                    scan_tracker[tracked_ip]['target'].update({local_port: True})

            except KeyError:
                # maybe log? seeing stream without seeing the initial connection being established
                pass
            except Exception:
                traceback.print_exc()

        ## Proxy decision logic
        if (self.portscan_prevention):
            ## applying lock on firewall rule dictionary lookup due to the small chance 2 threads check before either
            # can add the key to the dictionary to prevent duplicate rules and timeout method calls
            initial_block = False
            with self.fw_rule_creation_lock:
                if (active_block and tracked_ip not in self.fw_rules):
                    self.fw_rules.update({tracked_ip: timestamp})
                    initial_block = True
            # will mark the scan as dropped on the initial block condition (this will prevent multiple logs being sent)
            if (initial_block):
                Popen(f'sudo iptables -t mangle -A IPS -s {tracked_ip} -j DROP && \
                        sudo iptables -t mangle -A IPS -d {tracked_ip} -j DROP', shell=True)
                print(f'RULE INSERTED: {tracked_ip} > {tracked_port} | {time.time()}')
                ## will create a timeout thread to ensure firewall is removed if no persistence is configured or to remove
                # the ip from the scan tracker as well as the active scan dictionary
                threading.Thread(target=self.ConnectionTimeout, args=(tracked_ip, packet.protocol)).start()

                block_status = self.ResponseTracker(tracked_ip, packet.protocol)

            elif (active_block):
                already_blocked = True

            # if portscan is detected and user configured to reject, corresponding messages will be sent as a response to the scan
            if (self.portscan_reject and scan_detected):
                if (packet.protocol == TCP):
                    TCPPacket = ScanResponse(self.wan_int, packet, protocol=TCP)
                    TCPPacket.Response()
                elif (packet.protocol == UDP):
                    ICMPPacket = ScanResponse(self.wan_int, packet, protocol=UDP)
                    ICMPPacket.Response()

        # logging logic
        if (active_block and block_status == MISSED and self.logging_level >= WARNING):
            action = 'missed'
            connection_log = True

            print(f'MISSED BLOCK: {tracked_ip}')

        elif (active_block and block_status == BLOCKED and self.logging_level >= NOTICE):
            action = 'blocked'
            connection_log = True

            print(f'ACTIVE BLOCK: {tracked_ip}')

        ## add a not already blocked to ensure this doesnt get logged alot
        elif (scan_detected and not already_blocked and self.logging_level >= INFORMATIONAL):
            action = 'logged'
            connection_log = True

        if (connection_log):
            logging_options = {'ip': tracked_ip, 'protocol': packet.protocol,
                                'attack_type': attack_type, 'action': action}
            self.Logging(timestamp, logging_options)

    ## after not seeing a tracked ip for 3 seconds, they will be removed from all trackers and the iptable rule
    # will be removed if the user has not configured a persistant blocking time
    def ConnectionTimeout(self, tracked_ip, protocol):
        proto = self.protocol_conversion[protocol]
        active_scan = getattr(self, f'{proto}_active_scan')
        scan_tracker = getattr(self, f'{proto}_scan_tracker')
        while True:
            now = time.time()
            last_scan = active_scan.get(tracked_ip, None)
            if (last_scan and now - last_scan >= 3):
                scan_tracker.pop(tracked_ip, None)
                active_scan.pop(tracked_ip, None)
                print(f'TIMED OUT SCANNER {tracked_ip}')

                if (self.block_length == 0):
                    Popen(f'sudo iptables -t mangle -D IPS -s {tracked_ip} -j DROP && \
                            sudo iptables -t mangle -D IPS -d {tracked_ip} -j DROP', shell=True)
                    print(f'REMOVED FW RULE FOR {tracked_ip}')
                    self.fw_rules.pop(tracked_ip, None)

                break
            time.sleep(1.6)

    ## this function will wait for 1 second after seeing a local ip respond to a tcp syn. if the tracked ip
    # does not send a subsequent ack within 1 second, they are inserted into the active scan dictionary
    def ResponseTracker(self, tracked_ip, protocol):
        blocked_status = True
        missed_ports = set()
        time.sleep(2)

        protocol = self.protocol_conversion[protocol]
        open_ports = getattr(self, f'open_{protocol}_ports')
        scan_tracker = getattr(self, f'{protocol}_scan_tracker')
        for port in open_ports:
            response = scan_tracker[tracked_ip]['target'].get(port, None)
            if (response):
                missed_ports.add(port)

        if (missed_ports):
            blocked_status = False

        return blocked_status

    def Logging(self, timestamp, logging_options):
        ProxyDB = DBConnector(table='ips')
        ProxyDB.Connect()
        ProxyDB.IPSInput(timestamp, logging_options)
        ProxyDB.Disconnect()

        if (self.syslog_enabled):
            self.AlertSyslog(logging_options)

    def AlertSyslog(self, logging_options):
        opt = SName(**logging_options)

        if (opt.attack_type == DDOS):
            msg_level = ALERT
        elif (opt.attack_type == PORTSCAN):
            if (opt.action == 'logged'):
                msg_level = INFORMATIONAL
            elif (opt.action == 'blocked'):
                msg_level = NOTICE
            elif (opt.action == 'missed'):
                msg_level = WARNING

        message = f'src.ip={opt.ip}; protocol={opt.protocol}; attack_type={opt.attack_type}; action={opt.action}'
        self.Syslog.Message(EVENT, msg_level, message)

    def LoadInterfaces(self):
        with open(f'{HOME_DIR}/data/config.json', 'r') as settings:
            self.setting = json.load(settings)

        self.lan_int = self.setting['settings']['interface']['inside']
        self.wan_int = self.setting['settings']['interface']['outside']

        Int = Interface()
        self.wan_ip = Int.IP(self.wan_int)
        self.broadcast = Int.Broadcast(self.wan_int)

    # AsyncIO method called to gather automated/ continuous methods | this is python 3.7 version of async
    async def Main(self):
        await asyncio.gather(self.Automate.DDOSCalculation(), self.Automate.ClearIPTables(),
                            self.Automate.IPSSettings(), self.Automate.IPWhitelist(),
                            self.Log.Settings(LOG_MOD), self.Syslog.Settings(SYSLOG_MOD))
Exemple #24
0
        # return False

    @staticmethod
    # will send response to client over socket depending on host details it will decide unicast or broadcast
    def send_to_client(client_request):
        if (client_request.bcast):
            Log.debug(f'Sent BROADCAST to 255.255.255.255:68')
            client_request.sock.sendto(client_request.send_data,
                                       (f'{BROADCAST}', 68))

        else:
            Log.debug(f'Sent UNICAST to {client_request.handout_ip}:68')
            client_request.sock.sendto(client_request.send_data,
                                       (f'{client_request.handout_ip}', 68))

    @property
    def listener_sock(self):
        l_sock = socket(AF_INET, SOCK_DGRAM)
        l_sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        l_sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
        l_sock.setsockopt(SOL_SOCKET, SO_BINDTODEVICE,
                          f'{self._intf}\0'.encode('utf-8'))
        l_sock.bind((str(INADDR_ANY), PROTO.DHCP_SVR))

        return l_sock


if __name__ == '__main__':
    Log.run(name=LOG_NAME, verbose=VERBOSE, root=ROOT)
    DHCPServer.run(Log, threaded=False)
Exemple #25
0
class IPProxy:
    def __init__(self):
        self.fw_rules = {}
        self.ip_signatures = {}
        self.open_tcp_ports = set()
        self.open_udp_ports = set()

        self.inbound_session_tracker = {}
        self.outbound_session_tracker = {}

        self.tcp_session_tracker = {}

        self.fw_rule_creation_lock = threading.Lock()

        self.block_settings = {
            'malware': 'mal_block',
            'compromised': 'mal_block',
            'entry': 'tor_block',
            'exit': 'tor_block'
        }
        self.chain_settings = {
            'malware': 'MALICIOUS',
            'compromised': 'MALICIOUS',
            'entry': 'TOR',
            'exit': 'TOR'
        }

        # var initialization to give proxy time to load correct settings
        self.tor_entry_block = False
        self.tor_exit_block = False
        self.malware_block = False
        self.compromised_block = False
        self.syslog_enabled = False
        self.logging_level = 0

    def Start(self):
        self.LoadInterfaces()

        self.Log = LogHandler(self)
        self.Syslog = SyslogHandler(self)
        self.Automate = Automate(self)

        ListFile = ListFiles()
        ListFile.CombineIPs()

        self.Timer = TM()
        self.LoadSignatures()

        Sniffer = IPSniffer(self)
        # True boolean notifies thread that it was the initial start and to minimize sleep time
        threading.Thread(target=Sniffer.Start, args=(True, )).start()

        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        asyncio.run(self.Main())

    def SignatureCheck(self, packet):
        timestamp = round(time.time())

        signature_match = False
        active_block = False
        already_blocked = False
        log_connection = False

        direction = None
        category = None

        ## OUTBOUND RULE TCP
        if (packet.dst_ip in self.ip_signatures):
            direction = OUTBOUND
            signature_match = True
            tracked_ip = packet.dst_ip
            local_ip = packet.src_ip

            if (packet.procotol == UDP):
                main_st = self.outbound_session_tracker
                other_st = self.inbound_session_tracker

        ## INBOUND RULE TCP
        elif (packet.dst_port in self.open_tcp_ports
              and packet.src_ip in self.ip_signatures):
            direction = INBOUND
            signature_match = True
            tracked_ip = packet.src_ip
            local_ip = packet.dst_ip

            if (packet.protocol == UDP):
                main_st = self.inbound_session_tracker
                other_st = self.outbound_session_tracker

        if (packet.protocol == TCP and signature_match):
            ## if new connection, will add to fw_rules dictionary to prevent duplicate iptable rules, then will add to
            ## outbound session tracker and set active block to true to notify code to put ip table rule in
            tcp_st = self.tcp_session_tracker
            category = self.ip_signatures.get(tracked_ip)
            if (packet.tcp_syn and not packet.tcp_ack):
                ## applying lock on dict lookup/ add to protect state between condition and insert
                with self.fw_rule_creation_lock:
                    if (tracked_ip not in self.fw_rules):
                        self.fw_rules.update(
                            {tracked_ip: [timestamp, category]})
                        tcp_st[tracked_ip] = {local_ip: 0}
                        active_block = True

            ## will increment packet count of already active connections on responses/acks as a metric of what made it through
            elif (packet.tcp_ack and not packet.tcp_syn):
                count = tcp_st[tracked_ip].get(local_ip, 0) + 1
                tcp_st[tracked_ip].update({local_ip: count})
                print(f'INCREMENTED {tracked_ip}: {count}')

            # if a new connection is seen, but already blocked packet counter will reset to ensure numbers do not inflate
            # can probably remove after more recent changes since we do not see new connections so soon anymore
            elif (packet.tcp_syn and not packet.tcp_ack):
                tcp_st[tracked_ip] = {local_ip: 0}

            ## implementing the block via ip table if user configured to do so based on category and direction
            block_enabled = getattr(self, f'{category}_block')
            block_direction = getattr(self, self.block_settings[category])
            if (block_enabled
                    and active_block) and (block_direction == direction
                                           or block_direction == BOTH):
                self.StandardBlock(tracked_ip, local_ip, direction, category)

                # session tracker will check packet counts to add confidence metric, this blocks for 1.5 seconds
                confidence = self.SessionTracker(tracked_ip, local_ip,
                                                 direction)
                print(f'TRACKED IP: {tracked_ip} | CONFIDENCE: {confidence}')
            # this will prevent the informational log option to log unrelated or already blocked connections
            elif (block_enabled and not active_block):
                already_blocked = True

        ## will match if packet is udp protocol and either source or destination ip matches a proxy signature
        elif (packet.protocol == UDP and signature_match):
            # will match if direction is not already tracked
            category = self.ip_signatures.get(tracked_ip)
            if (tracked_ip not in other_st and tracked_ip not in main_st):
                ## applying lock on dict lookup/ add to protect state between condition and insert
                with self.fw_rule_creation_lock:
                    if (tracked_ip not in self.fw_rules):
                        self.fw_rules.update(
                            {tracked_ip: [timestamp, category]})
                        main_st[tracked_ip] = {local_ip: 0}
                        active_block = True

            ## will match if connection is being tracked and increment packet count for confidence metric
            elif (tracked_ip not in other_st and tracked_ip in main_st):
                count = tcp_st[tracked_ip].get(local_ip, 0) + 1
                main_st[tracked_ip].update({local_ip: count})
                print(f'INCREMENTED {packet.dst_ip}: {count}')

            ## implementing the block via ip table if user configured to do so based on category and direction
            block_enabled = getattr(self, f'{category}_block')
            block_direction = getattr(self, self.block_settings[category])
            if (block_enabled
                    and active_block) and (block_direction == direction
                                           or block_direction == BOTH):
                self.StandardBlock(tracked_ip, local_ip, direction, category)

                # session tracker will check packet counts to add confidence metric, this blocks for 1.5 seconds
                confidence = self.SessionTracker(tracked_ip, local_ip,
                                                 direction)
            # this will prevent the informational log option to log unrelated or already blocked connections
            elif (block_enabled and not active_block):
                already_blocked = True

        ## Log to Infected Hosts DB Table if matching malicious type categories ##
        if (category in {'malware'} and direction == OUTBOUND
                and self.logging_level >= ALERT):

            reason = 'malware'
            table = 'infectedclients'

            logging_options = {
                'infected_client': packet.src_mac,
                'src_ip': packet.src_ip,
                'detected_host': packet.dst_ip,
                'reason': reason
            }
            self.TrafficLogging(table, timestamp, logging_options)

        # logs blocked requests that let more than 7 packets through
        if (active_block and confidence == MEDIUM
                and self.logging_level >= WARNING):
            action = 'blocked'
            log_connection = True

        # logs redirected/blocked requests that blocked within 7 packets
        elif (active_block and confidence in {HIGH, VERY_HIGH}
              and self.logging_level >= NOTICE):
            action = 'blocked'
            log_connection = True

        # logs all interesting requests if not configured to block and log level is informational
        elif (signature_match) and (not already_blocked
                                    and self.logging_level >= INFORMATIONAL):
            action = 'logged'
            confidence = 'n/a'
            log_connection = True

        if (log_connection):
            table = 'ipproxy'
            logging_options = {
                'src_ip': packet.src_ip,
                'dst_ip': packet.dst_ip,
                'category': category,
                'direction': direction,
                'action': action,
                'confidence': confidence
            }

            self.TrafficLogging(table, timestamp, logging_options)

    def StandardBlock(self, blocked_ip, local_ip, direction, category):
        chain = self.chain_settings[category]

        Popen(f'sudo iptables -t mangle -A {chain} -s {blocked_ip} -j DROP && \
                sudo iptables -t mangle -A {chain} -d {blocked_ip} -j DROP',
              shell=True)

    # applying a wait to give response enough time to come back, if
    # if response is not seen within time, assumes packet was dropped
    def SessionTracker(self, blocked_ip, local_ip, direction):
        time.sleep(1.5)
        count = self.tcp_session_tracker[blocked_ip].get(local_ip)

        if (count <= 3):
            confidence = VERY_HIGH
        elif (3 < count <= 7):
            confidence = HIGH
        else:
            confidence = MEDIUM

        self.tcp_session_tracker[blocked_ip].update({local_ip: 0})

        return confidence

    def TrafficLogging(self, table, timestamp, logging_options):
        ProxyDB = DBConnector(table)
        ProxyDB.Connect()
        if (table in {'ipproxy'}):
            ProxyDB.IPInput(timestamp, logging_options)

            if (self.syslog_enabled):
                self.AlertSyslog(logging_options)
        elif (table in {'infectedclients'}):
            ProxyDB.InfectedInput(timestamp, logging_options)

        ProxyDB.Disconnect()

    def AlertSyslog(self, logging_options):
        opt = SName(logging_options)

        if (opt.category in {'malware'}):
            msg_level = ALERT
        else:
            if (opt.confidence == MEDIUM):
                msg_level = WARNING
            elif (opt.confidence in {HIGH, VERY_HIGH}):
                msg_level = NOTICE
            elif (opt.action == 'logged'):
                msg_level = INFORMATIONAL

        message = f'src.ip={opt.src_ip}; dst.ip={opt.dst_ip}; category={opt.category}; '
        message += f'direction={opt.direction}; action={opt.action}; confidence={opt.confidence}'

        self.Syslog.Message(EVENT, msg_level, message)

    # Loading lists of interesting traffic into sets
    def LoadSignatures(self):
        with open(f'{HOME_DIR}/dnx_iplists/blocked.ips', 'r') as blocked:
            while True:
                line = blocked.readline().strip().split()
                if (not line):
                    break
                if (line != '\n'):
                    host = line[0]
                    category = line[1]
                    self.ip_signatures[host] = category

    def LoadInterfaces(self):
        with open(f'{HOME_DIR}/data/config.json', 'r') as settings:
            setting = json.load(settings)

        self.wan_int = setting['settings']['interface']['outside']
        self.lan_int = setting['settings']['interface']['inside']

    # AsyncIO method called to gather automated/ continuous methods | this is python 3.7 version of async
    async def Main(self):
        await asyncio.gather(self.Timer.Settings(), self.Timer.Start(),
                             self.Automate.Blocking(),
                             self.Automate.ClearIPTables(),
                             self.Automate.OpenPorts(),
                             self.Log.Settings(LOG_MOD),
                             self.Syslog.Settings(SYSLOG_MOD))
Exemple #26
0
 def __init__(self):
     self.Log = LogHandler(module=LOG_MOD)
class SyslogService:
    def __init__(self):
        self.tcp_fallback = False
        self.udp_fallback = False
        self.tls_enabled = False
        self.syslog_protocol = None

        self.syslog_queue = deque()
        self.syslog_servers = {}

    def start(self):

        self.get_interface_settings()

        self.Log = LogHandler(process=self)
        self.Automate = Automate(self)
        self.SyslogUDP = UDPMessage(self)
        self.SyslogTCP = TCPMessage(self)

        self.automate_threads()
        self.initialize()

        self._ready_interface_service()

    def initialize(self):
        interface.wait_for_interface(self.lan_int)
        self.lan_ip = interface.wait_for_ip(self.lan_int)
        while True:
            if (self.syslog_servers):
                break
            time.sleep(FIVE_SEC)

        threading.Thread(target=self.process_message_queue).start()

    # Checking the syslog message queue for entries. if entries it will connection to the configured server over the
    # configured protocol/ports, then send the sockets to the protocol classes to actually send the messages
    def process_message_queue(self):
        while True:
            tcp_connections = None
            if (not self.syslog_queue):
                # waiting 5 second before checking queue again for idle perf
                time.sleep(FIVE_SEC)
                continue

            if (self.syslog_protocol == PROTO.TCP):
                if (self.tls_enabled):
                    tcp_connections = self.SyslogTCP.tls_connect()
                    # if all tls connections failed and tcp fallback is enabled, will attempt to connect to same servers over standard tcp port
                    if (not tcp_connections and self.tcp_fallback):
                        self.SyslogTCP.tcp_connect()
                else:
                    self.SyslogTCP.tcp_connect()

                if (tcp_connections):
                    self.SyslogTCP.send_queue(tcp_connections)

            if (self.syslog_protocol
                    == PROTO.UDP) or (self.udp_fallback
                                      and not tcp_connections):
                udp_socket = self.SyslogUDP.create_udp_socket()
                if (udp_socket):
                    self.SyslogUDP.send_queue(udp_socket)

    def get_interface_settings(self):
        interface_settings = load_configuration('config.json')
        self.lan_int = interface_settings['settings']['interface']['inside']

    # local socket receiving messages to be sent over syslog from all processes firewall wide. once a message is
    # received it will add it to the queue to be handled by a separate method.
    def _main(self):
        while True:
            try:
                syslog_message = self.service_sock.recv(2048)
                if (syslog_message):
                    self.syslog_queue.append(syslog_message)
            except OSError:
                #NOTE: should report this to front end if service socket error.
                break

        self._ready_interface_service()

    def _ready_interface_service(self):
        while True:
            error = self._create_service_socket()
            if (not error):
                break

            time.sleep(ONE_SEC)

        self._main()

    # using loopback so shouldnt have problems, but just taking precautions.
    def _create_service_socket(self):
        self.service_sock = socket(AF_INET, SOCK_DGRAM)
        self.service_sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        try:
            self.service_sock.bind((LOCALHOST, SYSLOG_SOCKET))
        except OSError:
            # failed to create socket. interface may be down.
            return True

    def automate_threads(self):
        self.Log.start()
        threading.Thread(target=self.Automate.get_settings).start()