Exemplo n.º 1
0
    def runTest(self):
        import pytrap
        import os

        urtempl = "ipaddr IP,uint16 PORT"
        c = pytrap.TrapCtx()
        c.init(["-i", "f:/tmp/pytrap_test"], 0, 1)
        c.setDataFmt(0, pytrap.FMT_UNIREC, urtempl)

        t = pytrap.UnirecTemplate("ipaddr IP,uint16 PORT")
        t.createMessage()
        t.IP = pytrap.UnirecIPAddr("192.168.0.1")
        t.PORT = 123
        c.send(t.getData())
        c.sendFlush()
        c.finalize()

        c = pytrap.TrapCtx()
        c.init(["-i", "f:/tmp/pytrap_test"], 1)
        c.setRequiredFmt(0, pytrap.FMT_UNIREC, urtempl)
        data = c.recv()
        t = pytrap.UnirecTemplate(urtempl)
        t.setData(data)
        self.assertEqual(t.IP, pytrap.UnirecIPAddr("192.168.0.1"))
        self.assertEqual(t.PORT, 123)
        c.finalize()

        os.unlink("/tmp/pytrap_test")
Exemplo n.º 2
0
def main():
    # Select input spec for basic and smtp interface
    basic_spec = "ipaddr DST_IP,ipaddr SRC_IP,uint64 BYTES,uint64 LINK_BIT_FIELD,time TIME_FIRST,time TIME_LAST,uint32 PACKETS,uint16 DST_PORT,uint16 SRC_PORT,uint8 DIR_BIT_FIELD,uint8 PROTOCOL,uint8 TCP_FLAGS,uint8 TOS,uint8 TTL"
    smtp_spec = "ipaddr DST_IP,ipaddr SRC_IP,uint64 BYTES,uint64 LINK_BIT_FIELD,time TIME_FIRST,time TIME_LAST,uint32 PACKETS,uint32 SMTP_2XX_STAT_CODE_COUNT,uint32 SMTP_3XX_STAT_CODE_COUNT,uint32 SMTP_4XX_STAT_CODE_COUNT,uint32 SMTP_5XX_STAT_CODE_COUNT,uint32 SMTP_COMMAND_FLAGS,uint32 SMTP_MAIL_CMD_COUNT,uint32 SMTP_RCPT_CMD_COUNT,uint32 SMTP_STAT_CODE_FLAGS,uint16 DST_PORT,uint16 SRC_PORT,uint8 DIR_BIT_FIELD,uint8 PROTOCOL,uint8 TCP_FLAGS,uint8 TOS,uint8 TTL,string SMTP_DOMAIN,string SMTP_FIRST_RECIPIENT,string SMTP_FIRST_SENDER"
    # Create a new trap context
    trap = pytrap.TrapCtx()
    """
    Trap initialization for two input interfaces, and no output interface
    """
    trap.init(sys.argv, 2, 1)
    # Set up required format to accept any unirec format.
    trap.setRequiredFmt(BASIC_IF, pytrap.FMT_UNIREC,
                        basic_spec)  # Refers to flows without SMTP headers
    trap.setRequiredFmt(SMTP_IF, pytrap.FMT_UNIREC,
                        smtp_spec)  # Refers to flows with SMTP headers
    basic_rec = pytrap.UnirecTemplate(basic_spec)
    smtp_rec = pytrap.UnirecTemplate(smtp_spec)
    trap.setDataFmt(0, pytrap.FMT_JSON, "smtp_alert")
    log.info("Daemon: Trap initialized.")
    detector = SpamDetection(trap)
    # Data synchronized queues
    flow_queue = Queue()  # Synchronize input flows
    # Create workers for each receiver
    basic_rcv = Thread(name="basic_receiver",
                       target=fetch_data,
                       args=(trap, basic_rec, BASIC_IF, flow_queue))
    smtp_rcv = Thread(name="smtp_receiver",
                      target=fetch_data,
                      args=(trap, smtp_rec, SMTP_IF, flow_queue))
    # Handle the received data from receivers
    data_handler = Thread(name="data_handler",
                          target=data_handling,
                          args=(detector, flow_queue))
    # Run multi-receiver
    basic_rcv.start()
    smtp_rcv.start()
    data_handler.start()
    log.info("Daemon: Multi-receiver started.")
    # Start detector
    detector.start()
    # Wait until the daemon is requested to stop by releasing the lock (by signal handler)
    g.stop_lock.acquire()
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    signal.signal(signal.SIGTERM, signal.SIG_DFL)
    signal.signal(signal.SIGABRT, signal.SIG_DFL)
    log.info("Stopping running components ...")
    g.is_running = False
    detector.stop()
    # Join the threads
    basic_rcv.join()
    smtp_rcv.join()
    # Stop data_handler
    flow_queue.put(None)
    data_handler.join()
    detector.join()
    # Free allocated memory
    trap.finalize()
    log.info("***** Finished, main thread exiting. *****")
    logging.shutdown()
    sys.exit(0)
Exemplo n.º 3
0
 def runTest(self):
     import pytrap
     c = pytrap.TrapCtx()
     try:
         c.getInIFCState(0)
         self.fail("Calling method of uninitialized context.")
     except:
         pass
Exemplo n.º 4
0
 def runTest(self):
     import pytrap
     c = pytrap.TrapCtx()
     try:
         c.setRequiredFmt(0)
         self.fail("Calling method of uninitialized context.")
     except:
         pass
Exemplo n.º 5
0
 def runTest(self):
     import pytrap
     c = pytrap.TrapCtx()
     try:
         c.ifcctl(0, True, CTL_IMEOUT, 0)
         self.fail("Calling method of uninitialized context.")
     except:
         pass
Exemplo n.º 6
0
 def runTest(self):
     import pytrap
     c = pytrap.TrapCtx()
     try:
         c.init(["-h", "1"], 0, 1)
         self.fail("Calling method of uninitialized context.")
     except pytrap.TrapHelp:
         pass
     c.finalize()
Exemplo n.º 7
0
    def __init__(self, interfaces, callback):
        unirec_init = ["-i", ",".join(x["interface"] for x in interfaces)]

        self.interfaces = interfaces
        self.callback = callback
        self.threads = []
        self.lock = threading.Lock()

        self.trap = pytrap.TrapCtx()
        self.trap.init(unirec_init, len(interfaces), 0)

        for i in range(len(interfaces)):
            self.trap.setRequiredFmt(i, pytrap.FMT_UNIREC,
                                     "ipaddr SRC_IP,ipaddr DST_IP")
Exemplo n.º 8
0
    def runTest(self):
        #"""json.dump returns str object, which was formerly not supported by pytrep send()"""
        import pytrap
        import json
        import os

        urtempl = "test"
        jdata = {"SRC_IP": "192.168.0.1"}
        c = pytrap.TrapCtx()
        c.init(["-i", "f:/tmp/pytrap_test2"], 0, 1)
        c.setDataFmt(0, pytrap.FMT_JSON, urtempl)

        c.send(json.dumps(jdata))
        c.sendFlush()
        c.finalize()

        c = pytrap.TrapCtx()
        c.init(["-i", "f:/tmp/pytrap_test2"], 1)
        c.setRequiredFmt(0, pytrap.FMT_JSON, urtempl)
        data = c.recv()
        c.finalize()
        self.assertEqual(jdata, json.loads(data))

        os.unlink("/tmp/pytrap_test2")
Exemplo n.º 9
0
def load_pytrap():
    """Init nemea libraries and set format of IP flows.
        
    Returns
    --------
    rec : pytrap
        Templete of IP flows.
    trap : pytrap
        Init pytrap NEMEA library, for capture IP flows from IFC interface setted in paramater -i.
    """
    trap = pytrap.TrapCtx()
    trap.init(sys.argv)
    # Set the list of required fields in received messages.
    # This list is an output of e.g. flow_meter - basic flow.
    input_spec = "ipaddr DST_IP,ipaddr SRC_IP,uint64 BYTES,uint64 LINK_BIT_FIELD,time TIME_FIRST,time TIME_LAST,uint32 PACKETS,uint16 DST_PORT,uint16 SRC_PORT,uint8 DIR_BIT_FIELD,uint8 PROTOCOL,uint8 TCP_FLAGS"
    trap.setRequiredFmt(0, pytrap.FMT_UNIREC, input_spec)
    rec = pytrap.UnirecTemplate(input_spec)
    return rec, trap
Exemplo n.º 10
0
    def runTest(self):
        import pytrap
        c = pytrap.TrapCtx()
        try:
            c.init()
            self.fail("argv is mandatory")
        except:
            pass
        try:
            c.init([])
            self.fail("argv must not be empty")
        except:
            pass
        try:
            c.init([[]])
            self.fail("argv[i] must be string")
        except:
            pass

        c.init(["-i", "u:test_init"], 0, 1)
        c.finalize()
Exemplo n.º 11
0
def amendflows(args, resolver):
    """Read flow data, add resolved fields and send amended flow data."""
    trap = pytrap.TrapCtx()
    trap.init(sys.argv, 1, 1)

    urinput = pytrap.UnirecTemplate(args.urformat)

    # this module accepts all Unirec fieds -> set required format:
    trap.setRequiredFmt(0, pytrap.FMT_UNIREC, args.urformat)

    trap.ifcctl(0, False, pytrap.CTL_BUFFERSWITCH, 0)

    uroutformat = urspecaddresolvedfields(args.urformat, args.outfields)
    uroutput = pytrap.UnirecTemplate(uroutformat)
    trap.setDataFmt(0, pytrap.FMT_UNIREC, uroutformat)
    if args.verbose:
        print("Set output format to '{}'."
              "\nWaiting for events.".format(uroutformat))
    while True:
        # Read data from input interface
        try:
            indata = trap.recv()
            if args.verbose:
                sys.stdout.write('.')
        except pytrap.FormatMismatch:
            print("Error: input and output interfaces data format"
                  " or data specifier mismatch")
            break
        except pytrap.FormatChanged as err:
            # Get data format from negotiation, amend it, and set it
            # for output iface
            fmttype, fmtspec = trap.getDataFmt(0)  # pylint:disable=unused-variable
            # Update Unirec templates
            urinput = pytrap.UnirecTemplate(fmtspec)
            uroutformat = urspecaddresolvedfields(fmtspec, args.outfields)
            uroutput = pytrap.UnirecTemplate(uroutformat)
            trap.setDataFmt(0, pytrap.FMT_UNIREC, uroutformat)
            if args.verbose:
                print("Reset output format to '{}'.".format(uroutformat))

            # Store data from the exception
            indata = err.data  # pylint:disable=no-member
        except pytrap.Terminated:
            print("Terminated trap.")
            break
        except pytrap.TrapError:
            break

        # Check for "end-of-stream" record
        if len(indata) <= 1:
            break

        # Set indata for access using attributes
        urinput.setData(indata)
        inputvarlensize = urinput.recVarlenSize()

        # Copy flow info from indata to outdata
        resolvedvarlenmaxsize = sum(ftype[1] for fname, ftype in args.outfields
                                    if len(ftype) > 1)
        outdata = uroutput.createMessage(inputvarlensize +
                                         resolvedvarlenmaxsize)
        uroutput.setData(outdata)

        for attr, value in urinput:
            setattr(uroutput, attr, value)

        for fieldstoresolve, resolution, outfield in args.validatedresolvspecs:
            setattr(uroutput, outfield,
                    getattr(resolver, resolution)(urinput, *fieldstoresolve))

        try:
            trap.send(outdata)
        except pytrap.Terminated:
            print("Terminated trap.")
            break
        if args.verbose:
            sys.stdout.write(',')

    trap.sendFlush()
Exemplo n.º 12
0
                    "--threshold",
                    default=5000,
                    help="set number of JSON records per file")
parser.add_argument('-d',
                    "--destination",
                    default='./',
                    help="target output directory.")
parser.add_argument(
    "--tmp-dir",
    default='./',
    help=
    "directory for storing temporary file before moving them to output destination."
)
args = parser.parse_args()

context = pytrap.TrapCtx()
context.init(['-i', args.ifcspec])

context.setRequiredFmt(0, pytrap.FMT_UNIREC, "")
unirec = pytrap.UnirecTemplate("ipaddr DST_IP,ipaddr SRC_IP,uint64 BYTES,uint64 LINK_BIT_FIELD,time TIME_FIRST, "\
                               "time TIME_LAST,uint32 DNS_RR_TTL,uint32 PACKETS,uint16 DNS_ANSWERS,uint16 DNS_CLASS,"\
                               "uint16 DNS_ID,uint16 DNS_PSIZE,uint16 DNS_QTYPE,uint16 DNS_RLENGTH,uint16 DST_PORT,"\
                               "uint16 SRC_PORT,uint8 DIR_BIT_FIELD,uint8 DNS_DO,uint8 DNS_RCODE,"\
                               "uint8 PROTOCOL,uint8 TCP_FLAGS,uint8 TOS,uint8 TTL,string DNS_NAME,bytes DNS_RDATA")

tmp_dir = args.tmp_dir
if not os.path.exists(tmp_dir):
    sys.stderr.write("Path given by --tmp-dir not found.\n")
    exit(1)

output_path = args.destination
Exemplo n.º 13
0
def Run(module_name,
        module_desc,
        req_type,
        req_format,
        conv_func,
        arg_parser=None):
    """ TODO doc
    """

    # *** Parse command-line arguments ***
    if arg_parser is None:
        arg_parser = argparse.ArgumentParser()
    arg_parser.formatter_class = argparse.RawDescriptionHelpFormatter

    # Set description
    arg_parser.description = str.format(
        desc_template,
        name=module_name,
        type={
            pytrap.FMT_RAW: 'raw',
            pytrap.FMT_UNIREC: 'UniRec',
            pytrap.FMT_JSON: 'JSON'
        }.get(req_type, '???'),
        fmt=req_format,
        original_desc=module_desc + "\n\n  " if module_desc else "",
    )

    # Add arguments defining outputs
    # TRAP output
    arg_parser.add_argument(
        '--trap',
        action='store_true',
        help=
        'Enable output via TRAP interface (JSON type with format id "IDEA"). Parameters are set using "-i" option as usual.'
    )
    # File output
    arg_parser.add_argument(
        '--file',
        metavar="FILE",
        type=str,
        help=
        'Enable output to file (each IDEA message printed to new line in JSON format). Set to "-" to use standard output.'
    )
    arg_parser.add_argument(
        '--file-indent',
        metavar="N",
        type=int,
        help='Pretty-format JSON in output file using N spaces for indentation.'
    )
    arg_parser.add_argument('--file-append',
                            action='store_true',
                            help='Append to file instead of overwrite.')
    # MongoDB output
    arg_parser.add_argument(
        '--mongodb',
        metavar="DBNAME",
        help='Enable output to MongoDB. Connect to database named DBNAME.')
    arg_parser.add_argument(
        '--mongodb-coll',
        metavar="COLL",
        default='alerts',
        help='Put IDEA messages into collection named COLL (default: "alerts").'
    )
    arg_parser.add_argument(
        '--mongodb-host',
        metavar="HOSTNAME",
        default='localhost',
        help='Connect to MongoDB running on HOSTNAME (default: "localhost").')
    arg_parser.add_argument(
        '--mongodb-port',
        metavar="PORT",
        type=int,
        default=27017,
        help='Connect to MongoDB running on port number PORT (default: 27017).'
    )
    # Warden3 output
    arg_parser.add_argument(
        '--warden',
        metavar="CONFIG_FILE",
        help=
        'Send IDEA messages to Warden server. Load configuration of Warden client from CONFIG_FILE.'
    )

    # Other options
    arg_parser.add_argument(
        '-n',
        '--name',
        metavar='NODE_NAME',
        help=
        'Name of the node, filled into "Node.Name" element of the IDEA message. Required if Warden output is used, recommended otherwise.'
    )
    arg_parser.add_argument(
        '--test',
        action='store_true',
        help='Add "Test" to "Category" before sending a message to output(s).')
    arg_parser.add_argument(
        '-v',
        '--verbose',
        action='store_true',
        help=
        "Enable verbose mode (may be used by some modules, common part donesn't print anything"
    )
    arg_parser.add_argument(
        '--srcwhitelist-file',
        metavar="FILE",
        type=str,
        help=
        "File with addresses/subnets in format: <ip address>/<mask>,<data>\\n \n where /<mask>,<data> is optional, <data> is a user-specific optional content. Whitelist is applied to SRC_IP field. If SRC_IP from the alert is on whitelist, the alert IS NOT reported."
    )
    arg_parser.add_argument(
        '--dstwhitelist-file',
        metavar="FILE",
        type=str,
        help=
        "File with addresses/subnets, whitelist is applied on DST_IP, see --srcwhitelist-file help."
    )
    # TRAP parameters
    trap_args = arg_parser.add_argument_group('Common TRAP parameters')
    trap_args.add_argument(
        '-i',
        metavar="IFC_SPEC",
        required=True,
        help='TODO (ideally this section should be added by TRAP')
    # Parse arguments
    args = arg_parser.parse_args()

    # Check if at least one output is enabled
    if not (args.file or args.trap or args.mongodb or args.warden):
        sys.stderr.write(module_name +
                         ": Error: At least one output must be selected\n")
        exit(1)

    # Check if node name is set if Warden output is enabled
    if args.name is None:
        if args.warden:
            sys.stderr.write(
                module_name +
                ": Error: Node name must be specified if Warden output is used (set param --name).\n"
            )
            exit(1)
        else:
            sys.stderr.write(module_name +
                             ": Warning: Node name is not specified.\n")

    # *** Initialize TRAP ***
    trap = pytrap.TrapCtx()
    trap.init(["-i", args.i], 1, 1 if args.trap else 0)
    #trap.setVerboseLevel(3)
    #trap.registerDefaultSignalHandler()

    # Set required input format
    trap.setRequiredFmt(0, req_type, req_format)

    # If TRAP output is enabled, set output format (JSON, format id "IDEA")
    if args.trap:
        trap.setDataFmt(0, pytrap.FMT_JSON, "IDEA")

    # *** Create output handles/clients/etc ***
    filehandle = None
    mongoclient = None
    mongocoll = None
    wardenclient = None

    if args.file:
        if args.file == '-':
            filehandle = sys.stdout
        else:
            filehandle = open(args.file, "a" if args.file_append else "w")

    if args.mongodb:
        import pymongo
        mongoclient = pymongo.MongoClient(args.mongodb_host, args.mongodb_port)
        mongocoll = mongoclient[args.mongodb][args.mongodb_coll]

    if args.warden:
        import warden_client
        try:
            config = warden_client.read_cfg(args.warden)
        except ValueError as e:
            sys.stderr.write(
                "{0}: Failed to load Warden config file '{1}'\n{2}\n".format(
                    module_name, args.warden, e))
            exit(1)
        config['name'] = args.name
        wardenclient = warden_client.Client(**config)

    # Check if a whitelist is set, parse the file and prepare context for binary search
    import ip_prefix_search
    if args.srcwhitelist_file:
        if 'ipaddr SRC_IP' in req_format.split(","):
            srcwhitelist = ip_prefix_search.IPPSContext.fromFile(
                args.srcwhitelist_file)
    else:
        srcwhitelist = None

    if args.dstwhitelist_file:
        if 'ipaddr DST_IP' in req_format.split(","):
            dstwhitelist = ip_prefix_search.IPPSContext.fromFile(
                args.dstwhitelist_file)
    else:
        dstwhitelist = None

    # *** Main loop ***
    URInputTmplt = None
    if req_type == pytrap.FMT_UNIREC and req_format != "":
        URInputTmplt = pytrap.UnirecTemplate(
            req_format
        )  # TRAP expects us to have predefined template for required set of fields
        rec = URInputTmplt

    stop = False
    while not stop:
        # *** Read data from input interface ***
        try:
            data = trap.recv()
        except pytrap.FormatMismatch:
            sys.stderr.write(
                module_name + ": Error: input data format mismatch\n"
            )  #Required: "+str((req_type,req_format))+"\nReceived: "+str(trap.get_data_fmt(trap.IFC_INPUT, 0))+"\n")
            break
        except pytrap.FormatChanged as e:
            # TODO: This should be handled by trap.recv transparently
            # Get negotiated input data format
            (fmttype, fmtspec) = trap.getDataFmt(0)
            # If data type is UniRec, create UniRec template
            if fmttype == pytrap.FMT_UNIREC:
                URInputTmplt = pytrap.UnirecTemplate(fmtspec)
            else:
                URInputTmplt = None
            rec = URInputTmplt
            data = e.data
        except pytrap.Terminated:
            break

        # Check for "end-of-stream" record
        if len(data) <= 1:
            # If we have output, send "end-of-stream" record and exit
            if args.trap:
                trap.send(0, b"0")
            break

        # Assert that if UniRec input is required, input template is set
        assert (req_type != pytrap.FMT_UNIREC or URInputTmplt is not None)

        # Convert raw input data to UniRec object (if UniRec input is expected)
        if req_type == pytrap.FMT_UNIREC:
            rec.setData(data)
        elif req_type == pytrap.FMT_JSON:
            rec = json.loads(data)
        else:  # TRAP_FMT_RAW
            rec = data

        # Check whitelists
        if srcwhitelist and srcwhitelist.ip_search(rec.SRC_IP):
            continue

        if dstwhitelist and dstwhitelist.ip_search(rec.DST_IP):
            continue

        # *** Convert input record to IDEA ***

        # Pass the input record to conversion function to create IDEA message
        idea = conv_func(rec, args)

        if idea is None:
            continue  # Record can't be converted - skip it (notice should be printed by the conv function)

        if args.name is not None:
            idea['Node'][0]['Name'] = args.name

        if args.test:
            idea['Category'].append('Test')

        # *** Send IDEA to outputs ***

        # File output
        if filehandle:
            filehandle.write(json.dumps(idea, indent=args.file_indent) + '\n')

        # TRAP output
        if args.trap:
            try:
                trap.send(json.dumps(idea), 0)
            except pytrap.TimeoutError:
                # skip this message
                pass
            except pytrap.Terminated:
                # don't exit immediately, first finish sending to other outputs
                stop = True

        # MongoDB output
        if mongocoll:
            # We need to change IDEA message here, but we may need it unchanged
            # later -> copy it (shallow copy is sufficient)
            idea2 = idea.copy()
            # Convert timestamps from string to Date format
            idea2['DetectTime'] = datetime.strptime(idea2['DetectTime'],
                                                    "%Y-%m-%dT%H:%M:%SZ")
            for i in ['CreateTime', 'EventTime', 'CeaseTime']:
                if idea2.has_key(i):
                    idea2[i] = datetime.strptime(idea2[i],
                                                 "%Y-%m-%dT%H:%M:%SZ")

            try:
                mongocoll.insert(idea2)
            except pymongo.errors.AutoReconnect:
                sys.stderr.write(module_name +
                                 ": Error: MongoDB connection failure.\n")
                stop = True

        # Warden output
        if wardenclient:
            wardenclient.sendEvents([idea])

    # *** Cleanup ***
    if filehandle and filehandle != sys.stdout:
        filehandle.close()
    if mongoclient:
        mongoclient.close()
    if wardenclient:
        wardenclient.close()
    trap.finalize()
Exemplo n.º 14
0
def Run(module_name,
        module_desc,
        req_type,
        req_format,
        conv_func,
        arg_parser=None):
    """Run the main loop of the reporter module called `module_name` with `module_desc` (used in help).

    The module requires data format of `req_type` type and `req_format` specifier - these must be given by author of the module.

    `conv_func(rec, args)` is a callback function that must translate given incoming alert `rec` (typically in UniRec according to `req_type`) into IDEA message. `args` contains CLI arguments parsed by ArgumentParser. `conv_func` must return dict().
    """

    # *** Parse command-line arguments ***
    if arg_parser is None:
        arg_parser = argparse.ArgumentParser()
    arg_parser.formatter_class = argparse.RawDescriptionHelpFormatter

    # Set description
    arg_parser.description = str.format(
        desc_template,
        name=module_name,
        type={
            pytrap.FMT_RAW: 'raw',
            pytrap.FMT_UNIREC: 'UniRec',
            pytrap.FMT_JSON: 'JSON'
        }.get(req_type, '???'),
        fmt=req_format,
        original_desc=module_desc + "\n\n  " if module_desc else "",
    )

    # Add arguments defining outputs
    # TRAP output
    arg_parser.add_argument(
        '-T',
        '--trap',
        action='store_true',
        help=
        'Enable output via TRAP interface (JSON type with format id "IDEA"). Parameters are set using "-i" option as usual.'
    )
    # Config file
    arg_parser.add_argument(
        '-c',
        '--config',
        metavar="FILE",
        default="./config.yaml",
        type=str,
        help='Specify YAML config file path which to load.')
    arg_parser.add_argument('-d',
                            '--dry',
                            action='store_true',
                            help="""Do not run, just print loaded config.""")
    # Warden3 output
    arg_parser.add_argument(
        '-W',
        '--warden',
        metavar="CONFIG_FILE",
        help=
        'Send IDEA messages to Warden server. Load configuration of Warden client from CONFIG_FILE.'
    )

    # Other options
    arg_parser.add_argument(
        '-n',
        '--name',
        metavar='NODE_NAME',
        help=
        'Name of the node, filled into "Node.Name" element of the IDEA message. Required argument.'
    )
    arg_parser.add_argument(
        '-v',
        '--verbose',
        metavar='VERBOSE_LEVEL',
        default=3,
        type=int,
        help=
        """Enable verbose mode (may be used by some modules, common part doesn't print anything).\nLevel 1 logs everything, level 5 only critical errors. Level 0 doesn't log."""
    )
    # TRAP parameters
    trap_args = arg_parser.add_argument_group('Common TRAP parameters')
    trap_args.add_argument(
        '-i',
        metavar="IFC_SPEC",
        required=True,
        help=
        'See http://nemea.liberouter.org/trap-ifcspec/ for more information.')
    # Parse arguments
    args = arg_parser.parse_args()

    # Set log level
    logging.basicConfig(level=(args.verbose * 10), format=FORMAT)

    # Check if node name is set if Warden output is enabled
    if args.name is None:
        #if args.warden:
        #    sys.stderr.write(module_name+": Error: Node name must be specified if Warden output is used (set param --name).\n")
        #    exit(1)
        logger.warning("Node name is not specified.")

    # *** Initialize TRAP ***
    trap = pytrap.TrapCtx()
    logger.info("Trap arguments: %s", args.i)
    trap.init(["-i", args.i], 1, 1 if args.trap else 0)
    #trap.setVerboseLevel(3)
    #trap.registerDefaultSignalHandler()

    # Set required input format
    trap.setRequiredFmt(0, req_type, req_format)
    if args.trap:
        trap.setDataFmt(0, pytrap.FMT_JSON, "IDEA")

    # *** Create output handles/clients/etc ***
    wardenclient = None

    if args.warden:
        try:
            import warden_client
        except:
            logger.error(
                "There is no available warden_client python module.  Install it or remove '--warden' from the module's arguments."
            )
            sys.exit(1)
        config = warden_client.read_cfg(args.warden)
        config['name'] = args.name
        wardenclient = warden_client.Client(**config)

    # Initialize configuration
    config = Config.Config(args.config, trap=trap, warden=wardenclient)

    if not args.dry:
        # *** Main loop ***
        URInputTmplt = None
        if req_type == pytrap.FMT_UNIREC and req_format != "":
            URInputTmplt = pytrap.UnirecTemplate(
                req_format
            )  # TRAP expects us to have predefined template for required set of fields
            rec = URInputTmplt

        stop = False
        while not stop:
            logger.info("Starting receiving")
            # *** Read data from input interface ***
            try:
                data = trap.recv()
            except pytrap.FormatMismatch:
                logger.error(
                    "Input data format mismatch in receiving from TRAP interface"
                )
                break
            except pytrap.FormatChanged as e:
                # Get negotiated input data format
                (fmttype, fmtspec) = trap.getDataFmt(0)
                # If data type is UniRec, create UniRec template
                if fmttype == pytrap.FMT_UNIREC:
                    URInputTmplt = pytrap.UnirecTemplate(fmtspec)
                else:
                    URInputTmplt = None
                rec = URInputTmplt
                data = e.data
            except pytrap.Terminated:
                break

            # Check for "end-of-stream" record
            if len(data) <= 1:
                if args.trap:
                    # If we have output, send "end-of-stream" record and exit
                    trap.send(0, b"0")
                break

            # Assert that if UniRec input is required, input template is set
            assert (req_type != pytrap.FMT_UNIREC or URInputTmplt is not None)

            # Convert raw input data to UniRec object (if UniRec input is expected)
            if req_type == pytrap.FMT_UNIREC:
                rec.setData(data)
            elif req_type == pytrap.FMT_JSON:
                rec = json.loads(data)
            else:  # TRAP_FMT_RAW
                rec = data

            # *** Convert input record to IDEA ***

            # Pass the input record to conversion function to create IDEA message
            idea = conv_func(rec, args)

            if idea is None:
                # Record can't be converted - skip it
                continue

            if args.name is not None:
                idea['Node'][0]['Name'] = args.name

            # *** Send IDEA to outputs ***
            # Perform rule matching and action running on the idea message
            try:
                config.match(idea)
            except pytrap.Terminated:
                logger.error("PyTrap was terminated")
                break
            except DropMsg:
                logger.info("Message was dropped by Drop action.")
                continue
            except Exception as e:
                logger.error(str(e))
                break
    else:
        # DRY argument given, just print config and exit
        print(config)

    if wardenclient:
        wardenclient.close()
    trap.finalize()
Exemplo n.º 15
0
    def runTest(self):
        import pytrap
        import os
        import time

        messages = 10000000

        urtempl = "ipaddr IP,uint16 PORT"

        # Start sender
        c1 = pytrap.TrapCtx()
        c1.init(["-i", "f:/tmp/pytrap_test3"], 0, 1)
        c1.setDataFmt(0, pytrap.FMT_UNIREC, urtempl)
        c1.ifcctl(0, False, pytrap.CTL_TIMEOUT, 500000)
        c1.ifcctl(0, False, pytrap.CTL_AUTOFLUSH, 500000)

        t = pytrap.UnirecTemplate(urtempl)
        t.createMessage()

        t.IP = pytrap.UnirecIPAddr("192.168.0.1")

        for i in range(messages):
            t.PORT = i
            c1.send(t.getData())
        c1.sendFlush()

        # Start Receiver
        c2 = pytrap.TrapCtx()
        c2.init(["-i", "f:/tmp/pytrap_test3"], 1)
        c2.setRequiredFmt(0, pytrap.FMT_UNIREC, urtempl)
        startt = time.process_time()
        data = c2.recvBulk(t, time=15, count=messages)
        elapsed_time = time.process_time() - startt
        print(
            f"recvBulk() Elapsed time for {messages} messages is: {elapsed_time}"
        )

        self.assertEqual(len(data), messages)
        ports = [i["PORT"] for i in data]
        self.assertEqual(ports, [i & 0xFFFF for i in range(messages)])

        # Start Receiver
        c2 = pytrap.TrapCtx()
        c2.init(["-i", "f:/tmp/pytrap_test3"], 1)
        c2.setRequiredFmt(0, pytrap.FMT_UNIREC, urtempl)
        startt = time.process_time()
        data = list()
        while True:
            d = c2.recv()
            if not d:
                break
            t.setData(d)
            data.append(t.getDict())

        elapsed_time = time.process_time() - startt
        print(
            f"recv() Elapsed time for {messages} messages is: {elapsed_time}")

        self.assertEqual(len(data), messages)
        ports = [i["PORT"] for i in data]
        self.assertEqual(ports, [i & 0xFFFF for i in range(messages)])

        c1.finalize()
        c2.finalize()

        os.unlink("/tmp/pytrap_test3")
Exemplo n.º 16
0
                send_to_reporter(detection_event)

    def tear_down(self):
        self.receiver.join_and_quit()
        self.processor.stop()


if __name__ == '__main__':
    options, args = parser.parse_args()
    signal.signal(signal.SIGINT, signal_h)
    g.blacklists = utils.load_blacklists(options.blacklist_config)
    g.botnet_blacklist_indexes = utils.get_botnet_blacklist_indexes(
        g.blacklists)
    logger.setLevel(options.log_level)

    trap = pytrap.TrapCtx()
    trap.init(sys.argv, 2, 2)

    trap.setDataFmt(0, pytrap.FMT_JSON, "aggregated_blacklist")
    trap.setDataFmt(1, pytrap.FMT_JSON, "blacklist_evidence")

    controller = Controller(options.adaptive_blacklist,
                            options.process_interval, options.evidence_timeout)

    # Run Controller until stopped by signal
    controller.run()

    controller.tear_down()

    # Free allocated memory
    trap.finalize()
Exemplo n.º 17
0
#!/usr/bin/env python3

import pytrap
import unirec
import sys
import pdb


#UR_Flow = unirec.CreateTemplate("UR_Flow", b"ipaddr DST_IP,ipaddr SRC_IP,double EVENT_SCALE,time TIME_FIRST,time TIME_LAST,uint16 DST_PORT,uint16 SRC_PORT,uint8 EVENT_TYPE,uint8 PROTOCOL")
UR_Flow = unirec.CreateTemplate("UR_Flow", b"double EVENT_SCALE,time TIME_FIRST,time TIME_LAST,uint16 DST_PORT,uint16 SRC_PORT,uint8 EVENT_TYPE,uint8 PROTOCOL")

ctx = pytrap.TrapCtx()

ctx.init(sys.argv)
#pytrap.setVerboseLevel(2)
print(ctx.getVerboseLevel())

ctx.setRequiredFmt(0)
ctx.ifcctl(0, True, 3, 1000000)

try:
    a = ctx.recv(0)
    raise Exception("recv should have raised Timeout exception")
except pytrap.TrapError as e:
    print("Caught Timeout exception: {0}".format(type(e)))


ctx.finalize()


def main():

    # Process arguments and initialize TRAP interface
    try:
        args, _ = process_arguments()
    except SystemExit as e:
        args = None  # Error in arguments or help message

    trap = pytrap.TrapCtx()
    try:
        trap.init(sys.argv,
                  1,
                  0,
                  module_name="Backscatter classifier common TRAP help")
        # Allow trap to print it's own help but terminate script anyway due to error in arguments
        if args is None:
            sys.exit(PYTHON_ARGS_ERROR_EXIT)
        trap.setRequiredFmt(0)
    except Exception as e:
        # Trap error message
        print(e)
        sys.exit(TRAP_ARGS_ERROR_EXIT)

    # Logging settings
    logger = logging.getLogger("backscatter_classifier")
    logging.basicConfig(
        level=logging.DEBUG,
        filename=args.logfile,
        filemode='w',
        format=
        "[%(levelname)s], %(asctime)s, %(name)s, %(funcName)s, line %(lineno)d: %(message)s"
    )

    # ASN and city databases
    try:
        geoip_db = Geoip2Wrapper(args.agp, args.cgp)
    except Exception as e:
        logger.error(e)
        logger.error("Error while create GeoIP2 wrapper")
        print(str(e), file=sys.stderr)
        sys.exit(EXIT_FAILURE)

    if args.export_to in ("c3isp", "c3isp-misp"):
        c3isp_upload.read_config(args.c3isp_config)

    if args.export_to == "misp":
        # MISP instance
        try:
            misp_instance = ExpandedPyMISP(args.url, args.key, args.ssl)
        except Exception as e:
            logger.error(e)
            logger.error("Error while creating MISP instance")
            print(str(e), file=sys.stderr)
            sys.exit(EXIT_FAILURE)

    # DDoS model
    ddos_model = pickle.load(args.model)
    ddos_classifier = DDoSClassifier(ddos_model, args.min_threshold)

    # *** MAIN PROCESSING LOOP ***
    while True:
        try:
            # Receive data
            try:
                data = trap.recv()
            except pytrap.FormatChanged as e:
                # Automatically detect format
                fmttype, fmtspec = trap.getDataFmt(0)
                rec = pytrap.UnirecTemplate(fmtspec)
                data = e.data
            if len(data) <= 1:
                # Terminating message
                break

            # Decode received data into python object
            rec.setData(data)

            # Only Ipv4
            try:
                victim_ip = IPv4Address(rec.SRC_IP)
            except Exception as e:
                logger.info("Received IPv6 address, skipping")
                continue

            # Predict class of backscatter like traffic
            try:
                duration = DDoSClassifier.DURATION(rec)
                if duration < args.min_duration:
                    # attack is too short lived to be reported
                    continue
                if duration > args.max_duration:
                    # attack is too long
                    continue
                if rec.FLOW_COUNT < args.min_flows:
                    # attack does not have enough flows
                    continue
                for subnet in CESNET_NET:
                    if victim_ip in subnet:
                        continue
                ddos = ddos_classifier.predict(rec)
            except Exception as e:
                logger.error(e)
                continue

            # Report attack using MISP
            try:
                if ddos:
                    try:
                        domain = gethostbyaddr(str(victim_ip))
                    except herror as e:
                        # Do not report for unknown domains
                        continue
                    event = create_ddos_event(rec, geoip_db, victim_ip,
                                              domain[0],
                                              args.misp_templates_dir)
                    if args.export_to == "misp":
                        try:
                            event_id = misp_instance.add_event(
                                event)['Event']['id']
                            misp_instance.publish(event_id)
                        except Exception as e:
                            logger.error(e)
                    elif args.export_to in ("c3isp", "c3isp-misp"):
                        logger.debug(f"Uploading event to C3ISP platform.")
                        event_file_path = Path(
                            "misp_event_to_c3isp.json").absolute()
                        with temporary_open(event_file_path,
                                            'w') as event_file:
                            json.dump(event.to_json(), event_file)
                            response = c3isp_upload.upload_to_c3isp(
                                event_file_path)
                            logger.debug(f"Response: {response}")

                        if not response or ('status' in response
                                            and response['status'] == 'ERROR'):
                            logger.error("ERROR during upload!")
                            continue
                        if args.export_to == "c3isp-misp":
                            dpo_id = response['content'][
                                'additionalProperties']['dposId']
                            logger.debug(f'Exporting DPO {dpo_id} to MISP.')
                            c3isp_upload.export_misp(dpo_id, logger)
            except Exception as e:
                logger.error(str(e))
                continue
        except Exception as e:
            # Log and re-raise exception
            logger.error(e)
            raise e
    # *** END OF MAIN PROCESSING LOOP ***
    trap.finalize()