Exemple #1
0
def main():
    '''Entry point'''
    ndr_config = ndr.Config('/etc/ndr/config.yml')
    status_message = ndr.StatusMessage(ndr_config)

    status_message.sign_report()
    status_message.load_into_queue()
def main():
    # Get our list of log files
    parser = argparse.ArgumentParser(
        description="Converts a TCPDump PCAP file to a traffic report message and sends it")
    parser.add_argument('-c', '--config',
                        default='/etc/ndr/config.yml',
                        help='NDR Configuration File')
    parser.add_argument('-k', '--keep', help='keep pcap file', action='store_true')
    parser.add_argument('pcaps', nargs='+',
                        help='Packet Capture Files')
    args = parser.parse_args()

    ndr_config = ndr.Config(args.config)
    logger = ndr_config.logger

    for pcap in args.pcaps:
        trm = ndr.TrafficReportMessage(ndr_config)
        logger.info("Processing %s", pcap)
        trm.parse_pcap_file(pcap)

        logger.debug("Processed %d entries", len(trm.traffic_entries))
        logger.debug("Skipped %d local LAN entries", trm.filtered_entries)

        trm.sign_report()
        trm.load_into_queue()

        # Because TCPDump doesn't clean up after itself after processing
        if args.keep is not True:
            os.remove(pcap)
Exemple #3
0
def main():
    '''Starts the scan network'''

    # We need the NDR Network config for this scan
    ndr_config = ndr.Config('/etc/ndr/config.yml')

    parser = argparse.ArgumentParser(
        description="Intelligently scans the network with NMAP")
    parser.add_argument('--net-config',
                        default=ndr_config.ndr_netconfig_file,
                        help='Network Configuration File')

    parser.add_argument('--nmap-config',
                        default=ndr_config.nmap_configuration_file,
                        help='NMAP Configuration File')

    # Load in the NDR configuration
    args = parser.parse_args()

    if os.getuid() != 0:
        print("ERROR: must be run as root")
        return

    nmap_config = ndr.NmapConfig(args.net_config)
    nmap_runner = ndr.NmapRunner(ndr_config, nmap_config)

    nmap_runner.run_network_scans()
Exemple #4
0
    def setUpClass(cls):
        '''Sets everything up for testing status messages'''
        cls._current_time = int(time.time())

        cls._ncc = ndr.Config(NDR_CONFIG)

        # Create a temporary file for the OTA timestamp
        file_descriptor, cls._ota_timestamp = tempfile.mkstemp()

        with os.fdopen(file_descriptor, 'w') as ts_file:
            ts_file.write(str(cls._current_time))

        cls._ncc.image_timestamp_file = cls._ota_timestamp
Exemple #5
0
def main():
    parser = argparse.ArgumentParser(
        description=
        "Uploads SNORT all-traffic CSV files. If directory is specified, all files in that directory"
    )
    parser.add_argument('-f',
                        "--filename",
                        default='log.csv',
                        help='base name of the log file to upload')
    parser.add_argument('logs', nargs='+', help='log files to upload')
    args = parser.parse_args()

    ndr_config = ndr.Config('/etc/ndr/config.yml')

    snort_traffic_log = ndr.SnortTrafficLog(ndr_config)
    logger = ndr_config.logger

    for logpath in args.logs:
        if os.path.isfile(logpath):
            logger.debug("Processing log: %s", logpath)
            snort_traffic_log.append_log(logpath)
            continue

        logger.debug("Processing log directory: %s", logpath)
        for _, _, filelist in os.walk(logpath):
            filelist.sort()  # Causes the files to be spat out oldest first
            # We're not interested in subdirectories so
            for filename in filelist:
                logfile = logpath + "/" + filename

                try:
                    if filename == args.filename:
                        logger.debug("Skipping base log file: %s", filename)
                        continue
                    logger.debug("Parsing: %s", filename)

                    snort_traffic_log.append_log(logfile)
                    snort_traffic_log.consolate()

                    # Delete the log file after we're done with it
                    os.remove(logfile)

                except:
                    trace = traceback.format_exc()
                    logger.error("log parse died with error: %s", trace)
                    # Can't do this in a finally block as we need to skip the base log file
                    # or SNORT ABENDs
                    os.remove(logfile)

    snort_traffic_log.sign_report()
    snort_traffic_log.load_into_queue()
Exemple #6
0
def main():
    # Get our list of log files
    parser = argparse.ArgumentParser(
        description="Upload a JSON-formatted syslog file for processing")
    parser.add_argument('-c', '--config',
                        default='/etc/ndr/config.yml',
                        help='NDR Configuration File')
    parser.add_argument('logs', nargs='+',
                        help='log files to upload')
    args = parser.parse_args()

    ndr_config = ndr.Config(args.config)
    logger = ndr_config.logger

    # This is slagging ugly. Nothing stops us from getting
    # a bad file in so we need to handle that case, and then
    # pop a message off for each log

    # YAML is a superset of JSON so we should be able to load
    # each line of the log file and pull the object out of with
    # Because of the way syslog-ng logs JSON, we can't simply
    # grab the entire thing because there isn't array markers in
    # the file ..,

    for log in args.logs:
        log_upload = SyslogUploadMessage(ndr_config)
        try:
            with open(log, 'r') as f:
                for line in f:
                    # It's also possible we can get a bad entry. In that case, skip it and report
                    # it into the log. It will get dumped into syslog_upload's syslog error log
                    # for autospy

                    try:
                        yaml_line = yaml.safe_load(line)
                        entry = SyslogEntry.from_dict(yaml_line)
                        if entry == None: # Was a newline, discarding
                            continue
                        log_upload.add_entry(entry)
                    except ValueError:
                        logger.error("failed to parse %s in %s", line, log)
                    except:
                        logger.error("cataphoric error %s %s %s", log, line, sys.exc_info()[0])

            # With everything loaded, queue the magic
            log_upload.sign_report()
            log_upload.load_into_queue()

        finally:
            pass
Exemple #7
0
def main():
    parser = argparse.ArgumentParser(
        description="Upload a status report to the server")
    parser.add_argument('-c', '--config',
                        default='/etc/ndr/config.yml',
                        help='NDR Configuration File')
    args = parser.parse_args()

    ndr_config = ndr.Config(args.config)
    ingest_message = ndr.IngestMessage(
        ndr_config, ndr.IngestMessageTypes.TEST_ALERT
    )

    ingest_message.sign_report()
    ingest_message.load_into_queue()
Exemple #8
0
def main():
    '''Entry point'''
    parser = argparse.ArgumentParser(
        description="Upload a status report to the server")
    parser.add_argument('-c',
                        '--config',
                        default='/etc/ndr/config.yml',
                        help='NDR Configuration File')
    args = parser.parse_args()

    ndr_config = ndr.Config(args.config)
    status_message = ndr.StatusMessage(ndr_config)

    status_message.sign_report()
    status_message.load_into_queue()
Exemple #9
0
def setup_ndr_client_config(self):
    logging.getLogger().addHandler(logging.NullHandler())
    self._ncc = ndr.Config(NDR_CONFIG_FILE)
    self._ncc.logger = logging.getLogger()
    self._ncc.image_information_file = IMAGE_CONFIG

    self._created_files = []
    config_ndr_for_signing_and_local_queue(self)

    # Create a temporary directory for handling config files that are optional but may get
    # written out or updated or something.

    self._ncc_config_dir = tempfile.mkdtemp()

    # Override optional config files with paths that won't conflict
    self._ncc.nmap_configuration_file = self._ncc_config_dir + "/nmap_config.yml"

    # Write out the test config for testing mainrun programs
    self._ndr_config_file = create_temp_file(self)
    with open(self._ndr_config_file, 'w') as f:
        yaml_content = yaml.dump(self._ncc.to_dict())
        f.write(yaml_content)
Exemple #10
0
def main():
    # Do our basic setup work
    logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s')
    logger = logging.getLogger(name=__name__)
    logger.setLevel(logging.DEBUG)

    # We need both configs
    ncc = ndr.Config("/etc/ndr/config.yml")  # NDR Client Config
    nsc = ndr_server.Config(logger, "/etc/ndr/ndr_server.yml")
    db_connection = nsc.database.get_connection()

    parser = argparse.ArgumentParser(
        description="Requests that a recorder restart to install OTAs updates")
    parser.add_argument('recorders', nargs='+', help='recorders to reboot')
    args = parser.parse_args()
    for recorder in args.recorders:
        # Make sure the recorder exists
        try:
            # We'll try to initialize a Recorder object. We don't need it but it confirms
            # that the recorder exists in the datbase

            ndr_server.Recorder.read_by_hostname(nsc,
                                                 recorder,
                                                 db_conn=db_connection)
            msg = ndr.IngestMessage(
                config=ncc, message_type=ndr.IngestMessageTypes.REBOOT_REQUEST)

            msg.destination = recorder
            msg.upload_method = 'uux'
            msg.sign_report()
            msg.load_into_queue()
            logger.info("Queued recorder %s to reboot", recorder)

        except psycopg2.DatabaseError:
            logger.error("recorder %s does not exist", recorder)

    db_connection.close()
Exemple #11
0
def main():
    # Get our list of log files
    parser = argparse.ArgumentParser(
        description="Process a remote message")
    parser.add_argument('-k', "--keep", help='base name of the log file to upload', action='store_true')
    parser.add_argument('messages', nargs='+',
                        help='remote messages to process')
    parser.add_argument('-c', '--config',
                        default='/etc/ndr/config.yml',
                        help='NDR Configuration File')

    args = parser.parse_args()

    ndr_config = ndr.Config(args.config)
    logger = ndr_config.logger

    validated_messages = []

    for message_file in args.messages:
        # DEBUG, write message to file
        if not os.path.isdir("/tmp/incoming_messages"):
            os.makedirs("/tmp/incoming_messages")
        logger.info("Processing %s", message_file)
        shutil.copy(
            message_file, "/tmp/incoming_messages/" + os.path.basename(message_file))

        # If we're operating in UUCP mode, then we only accept messages signed
        # by our ingest
        accepted_cns = None

        if ndr_config.upload_method == 'uucp':
            accepted_cns = ndr_config.ingest_uucp_host
        else:
            logger.warning("accepting any signed messages due to local mode!")

        message = ndr.IngestMessage.verify_and_load_message(
            ndr_config, message_file, only_accept_cn=accepted_cns)
        if message is not None:
            logger.info("Successfully parsed and validated the message!")

        # Now some magic is required. If we're running via uux, we're running in the context of
        # the uucp user and not under NDR. The UUCP user can run ndr-process-message as sudo so
        # we need to copy the message over to a temporary directory if we're not root, and then
        # recurse into ourselves. We'll do this for all the messages in the stack.
        validated_messages.append(message_file)

        # If we're root, just process it
        if os.getuid() == 0:
            logger.info("Running in root context")

            # Now do things with it based on the type of message it is
            if message.message_type == ndr.IngestMessageTypes.CERTIFICATE_REQUEST:
                logger.info("Got a certificate request message ...")

                cert_request_msg = ndr.CertificateRequest()
                cert_request_msg.from_message(message)

                # Write out the signed certificates to the root filesystem
                if cert_request_msg.certificate is not None:
                    logger.info("Writing out signed certificate ...")
                    with open(ndr_config.ssl_certfile, 'w') as f:
                        f.write(cert_request_msg.certificate)
                if cert_request_msg.certificate_chain is not None:
                    logger.info("Writing out certificate chain ...")
                    with open(ndr_config.ssl_bundle, 'w') as f:
                        f.write(cert_request_msg.certificate_chain)
                logger.info("Updated certificate chain for device")

            elif message.message_type == ndr.IngestMessageTypes.REBOOT_REQUEST:
                logger.info("Got a reboot request")
                shutdown_args = [ "shutdown", "-r", "now"]

                shutdown_process = subprocess.run(
                    args=shutdown_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                    check=False)

                if shutdown_process.returncode != 0:
                    logger.error("shutdown failed: %s", str(shutdown_process.stderr, 'utf-8'))
                    return

            elif message.message_type == ndr.IngestMessageTypes.FILE_UPDATE:
                logger.info("Got a file update message")
                file_update_message = ndr.FileUpdateMessage(ndr_config)
                file_update_message.from_message(message)
                file_update_message.write_updates()
            else:
                logger.error("Got non-client accepted %s message", message.message_type.value)
        else:
            logger.info("Not running as root, saving messages to run in sudo")

    # Non-root fall through. We should have a pile of validated messages
    if os.getuid() != 0:
        files_for_sudo = []
        for valid_message in validated_messages:
            # The messages will be revalidated on the second go around
            msg_fd, vm_file = tempfile.mkstemp()
            os.close(msg_fd)
            shutil.copy(valid_message, vm_file)
            files_for_sudo.append(vm_file)

        # Attempt to call ourselves via sudo
        sudo_process_message = [ "sudo", "ndr-process-message"]
        sudo_process_message += files_for_sudo

        # Here goes nothing
        sudo_proc = subprocess.run(
            args=sudo_process_message, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
            check=False)

        if sudo_proc.returncode != 0:
            logger.error("sudo run failed: %s", str(sudo_proc.stderr, 'utf-8'))
            return

        # And done (we log this here so it only prints once)
        logger.info("Finished processing messages")

    # Make a final loop through to clean up our files
    if args.keep is False:
        for message in args.messages:
            os.remove(message)

    return
Exemple #12
0
import ndr

# Testing data from a live system running syslog-ng in JSON reporting mode
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
TEST_SURFACE_SCAN_DATA = THIS_DIR + '/data/nmap_v4_surface.xml'
TEST_V6_LINK_LOCAL_SCAN_DATA = THIS_DIR + '/data/nmap_v6_link_local.xml'
TEST_HOSTNAME_SCAN_DATA = THIS_DIR + '/data/nmap_hostname_test.xml'
TEST_SD_SCAN_DATA = THIS_DIR + '/data/nmap_service_discovery.xml'
TEST_MULTIHOME_DATA = THIS_DIR + '/data/nmap_multihome.xml'
TEST_MULTIHOME_V6_DATA = THIS_DIR + '/data/nmap_multihome_v6.xml'

TEST_LINK_LOCAL_PARSING = THIS_DIR + '/data/nmap_ipv6_link_local_scan.yml'

OUTPUT_PRETTY_PRINT_SD_SCAN = THIS_DIR + '/data/expected_outputs/pretty_print_sd_scan'

NDR_CONFIG = ndr.Config(THIS_DIR + '/data/test_config.yml')


def load_nmap_xml_data(file_name):
    '''Helper function to load XML data'''
    with open(file_name, 'r') as xml_file:
        return xml_file.read()


class NmapTest(unittest.TestCase):
    '''Tests functionality related to NMAP scanning and parsing'''

    maxDiff = None

    def test_scandata_parsing(self):
        '''Tests parsing of the basic surface scan'''
Exemple #13
0
def main():
    config = ndr.Config('/etc/ndr/config.yml')

    # Unlike the majority of NDR tools, this one is meant to run interactivity
    print("Network Data Recorder Enlistment Tool")
    print("Copyright (C) 2017 - Secured By THEM")

    if os.getuid() != 0:
        print("ERROR: This utility must be run as root!")
        sys.exit(-1)

    hostname = config.hostname
    print("Hostname: ", hostname)

    # Print out some helpful messages, then ask questions
    print('''
This script will automatically generate a certificate signing request
and other security keys for this recorder. The information used in the CSR
will be used to create an organization and site if necessary, and the pseudonym
will become the default human readable name used to refer to this recorder.

Manual processing of the CSR is required server side. After uploading the CSR,
we'll poll to wait for the CSR to be signed and downloaded. If you don't wish to
enlist this recorder now, press Ctrl-C

NOTE: Names are case sensitive!
''')

    got_info = False
    while got_info is False:
        organization = ""
        while organization == "":
            organization = input("Organization: ")
            if organization == "":
                print("Organization can not be blank!")

        org_unit = ""
        while org_unit == "":
            org_unit = input("Organization Unit/Site: ")
            if organization == "":
                org_unit("OU can not be blank!")

        pseudonym = ""
        while pseudonym == "":
            pseudonym = input("Pseudonym/Human Name: ")
            if organization == "":
                pseudonym("Pseudonym can not be blank!")

        print()
        print("=== CSR to be generated ===")
        print("Organization:", organization)
        print("Organization Unit (Site):", org_unit)
        print("Pseudonym (Human Name):", pseudonym)
        print("Common Name:", hostname)
        print()

        confirmation = input("Is this information correct? [N/y] ")
        if confirmation and confirmation.lower()[0] == 'y':
            got_info = True

    # Check if we have a private key
    print("STEP 1: Checking cryptography keys ...")
    key = None
    if os.path.isfile(config.ssl_private_key) is False:
        print("No private key found, generating. This may take some time ...")
        key = rsa.generate_private_key(public_exponent=65537,
                                       key_size=2048,
                                       backend=default_backend())

        with open(config.ssl_private_key, "wb") as f:
            f.write(
                key.private_bytes(
                    encoding=serialization.Encoding.PEM,
                    format=serialization.PrivateFormat.TraditionalOpenSSL,
                    encryption_algorithm=serialization.NoEncryption()))
        print("Generated ", config.ssl_private_key)

    else:
        print("Have recorder private key")
        with open(config.ssl_private_key, "rb") as f:
            key = load_pem_private_key(f.read(),
                                       password=None,
                                       backend=default_backend())

    # Now generate a certificate signing request (we can always generate a new
    # one safely)
    print("STEP 2: Generating CSR ...")
    csr = x509.CertificateSigningRequestBuilder().subject_name(
        x509.Name([
            x509.NameAttribute(NameOID.COMMON_NAME, hostname),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, organization),
            x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, org_unit),
            x509.NameAttribute(NameOID.PSEUDONYM, pseudonym)
        ])).sign(key, hashes.SHA256(), default_backend())

    with open(config.ssl_csr, "wb") as f:
        f.write(csr.public_bytes(serialization.Encoding.PEM))
    print("CSR written to ", config.ssl_csr)

    # Uploading the report is a bit different since we don't have a local certificate to sign it
    # with. First, let's create a CertificateRequest message

    csr_msg = ndr.CertificateRequest(config)
    csr_msg.csr = str(csr.public_bytes(serialization.Encoding.PEM), 'utf-8')

    csr_msg.destination_queue = ndr.IngestMessageDestinations.ENROLLMENT_QUEUE

    # Now we create the report, and load it into signed message and send it on
    # its way
    csr_msg.create_report()
    csr_msg.signed_message = csr_msg.message
    csr_msg.load_into_queue()