Exemple #1
0
def resubmit(uuid):
    try:
        root = RootAnalysis(storage_dir=storage_dir_from_uuid(uuid))
        root.load()
        root.reset()
        root.schedule()
        return json_result({'result': 'success'})
    except Exception as e:
        return json_result({'result': 'failed', 'error': str(e)})
Exemple #2
0
def submit():

    if KEY_ANALYSIS not in request.values:
        abort(
            Response(
                "missing {} field (see documentation)".format(KEY_ANALYSIS),
                400))

    r = json.loads(request.values[KEY_ANALYSIS])

    # the specified company needs to match the company of this node
    # TODO eventually we'll have a single node that serves API to all configured companies

    if KEY_COMPANY_NAME in r and r[KEY_COMPANY_NAME] != saq.CONFIG['global'][
            'company_name']:
        abort(
            Response(
                "wrong company {} (are you sending to the correct system?)".
                format(r[KEY_COMPANY_NAME]), 400))

    if KEY_DESCRIPTION not in r:
        abort(
            Response("missing {} field in submission".format(KEY_DESCRIPTION),
                     400))

    root = RootAnalysis()
    root.uuid = str(uuid.uuid4())

    # does the engine use a different drive for the workload?
    analysis_mode = r[
        KEY_ANALYSIS_MODE] if KEY_ANALYSIS_MODE in r else saq.CONFIG['engine'][
            'default_analysis_mode']
    if analysis_mode != ANALYSIS_MODE_CORRELATION:
        root.storage_dir = workload_storage_dir(root.uuid)
    else:
        root.storage_dir = storage_dir_from_uuid(root.uuid)

    root.initialize_storage()

    try:

        root.analysis_mode = r[
            KEY_ANALYSIS_MODE] if KEY_ANALYSIS_MODE in r else saq.CONFIG[
                'engine']['default_analysis_mode']
        root.company_id = saq.CONFIG['global'].getint('company_id')
        root.tool = r[KEY_TOOL] if KEY_TOOL in r else 'api'
        root.tool_instance = r[
            KEY_TOOL_INSTANCE] if KEY_TOOL_INSTANCE in r else 'api({})'.format(
                request.remote_addr)
        root.alert_type = r[KEY_TYPE] if KEY_TYPE in r else saq.CONFIG['api'][
            'default_alert_type']
        root.description = r[KEY_DESCRIPTION]
        root.event_time = LOCAL_TIMEZONE.localize(datetime.datetime.now())
        if KEY_EVENT_TIME in r:
            try:
                root.event_time = parse_event_time(r[KEY_EVENT_TIME])
            except ValueError as e:
                abort(
                    Response(
                        "invalid event time format for {} (use {} format)".
                        format(r[KEY_EVENT_TIME],
                               event_time_format_json_tz), 400))

        root.details = r[KEY_DETAILS] if KEY_DETAILS in r else {}

        # go ahead and allocate storage
        # XXX use temp dir instead...

        if KEY_TAGS in r:
            for tag in r[KEY_TAGS]:
                root.add_tag(tag)

        # add the observables
        if KEY_OBSERVABLES in r:
            for o in r[KEY_OBSERVABLES]:
                # check for required fields
                for field in [KEY_O_TYPE, KEY_O_VALUE]:
                    if field not in o:
                        abort(
                            Response(
                                "an observable is missing the {} field".format(
                                    field), 400))

                o_type = o[KEY_O_TYPE]
                o_value = o[KEY_O_VALUE]
                o_time = None
                if KEY_O_TIME in o:
                    try:
                        o_time = parse_event_time(o[KEY_O_TIME])
                    except ValueError:
                        abort(
                            Response(
                                "an observable has an invalid time format {} (use {} format)"
                                .format(o[KEY_O_TIME],
                                        event_time_format_json_tz), 400))

                observable = root.add_observable(o_type,
                                                 o_value,
                                                 o_time=o_time)

                if KEY_O_TAGS in o:
                    for tag in o[KEY_O_TAGS]:
                        observable.add_tag(tag)

                if KEY_O_DIRECTIVES in o:
                    for directive in o[KEY_O_DIRECTIVES]:
                        # is this a valid directive?
                        if directive not in VALID_DIRECTIVES:
                            abort(
                                Response(
                                    "observable {} has invalid directive {} (choose from {})"
                                    .format('{}:{}'.format(o_type,
                                                           o_value), directive,
                                            ','.join(VALID_DIRECTIVES)), 400))

                        observable.add_directive(directive)

                if KEY_O_LIMITED_ANALYSIS in o:
                    for module_name in o[KEY_O_LIMITED_ANALYSIS]:
                        observable.limit_analysis(module_name)

        # save the files to disk and add them as observables of type file
        for f in request.files.getlist('file'):
            logging.debug("recording file {}".format(f.filename))
            #temp_dir = tempfile.mkdtemp(dir=saq.CONFIG.get('api', 'incoming_dir'))
            #_path = os.path.join(temp_dir, secure_filename(f.filename))
            try:
                #if os.path.exists(_path):
                #logging.error("duplicate file name {}".format(_path))
                #abort(400)

                #logging.debug("saving file to {}".format(_path))
                #try:
                #f.save(_path)
                #except Exception as e:
                #logging.error("unable to save file to {}: {}".format(_path, e))
                #abort(400)

                full_path = os.path.join(root.storage_dir, f.filename)

                try:
                    dest_dir = os.path.dirname(full_path)
                    if not os.path.isdir(dest_dir):
                        try:
                            os.makedirs(dest_dir)
                        except Exception as e:
                            logging.error(
                                "unable to create directory {}: {}".format(
                                    dest_dir, e))
                            abort(400)

                    logging.debug("saving file {}".format(full_path))
                    f.save(full_path)

                    # add this as a F_FILE type observable
                    root.add_observable(
                        F_FILE,
                        os.path.relpath(full_path, start=root.storage_dir))

                except Exception as e:
                    logging.error(
                        "unable to copy file from {} to {} for root {}: {}".
                        format(_path, full_path, root, e))
                    abort(400)

            except Exception as e:
                logging.error("unable to deal with file {}: {}".format(f, e))
                report_exception()
                abort(400)

            #finally:
            #try:
            #shutil.rmtree(temp_dir)
            #except Exception as e:
            #logging.error("unable to delete temp dir {}: {}".format(temp_dir, e))

        try:
            if not root.save():
                logging.error("unable to save analysis")
                abort(
                    Response(
                        "an error occured trying to save the alert - review the logs",
                        400))

            # if we received a submission for correlation mode then we go ahead and add it to the database
            if root.analysis_mode == ANALYSIS_MODE_CORRELATION:
                ALERT(root)

            # add this analysis to the workload
            root.schedule()

        except Exception as e:
            logging.error("unable to sync to database: {}".format(e))
            report_exception()
            abort(
                Response(
                    "an error occured trying to save the alert - review the logs",
                    400))

        return json_result({'result': {'uuid': root.uuid}})

    except Exception as e:
        logging.error("error processing submit: {}".format(e))
        report_exception()

        try:
            if os.path.isdir(root.storage_dir):
                logging.info("removing failed submit dir {}".format(
                    root.storage_dir))
                shutil.rmtree(root.storage_dir)
        except Exception as e2:
            logging.error("unable to delete failed submit dir {}: {}".format(
                root.storage_dir, e))

        raise e
Exemple #3
0
def _create_analysis(url, reprocess, details, db, c):
    assert isinstance(url, str)
    assert isinstance(reprocess, bool)
    assert isinstance(details, dict)

    sha256_url = hash_url(url)

    if reprocess:
        # if we're reprocessing the url then we clear any existing analysis
        # IF the current analysis has completed
        # it's OK if we delete nothing here
        execute_with_retry("""DELETE FROM cloudphish_analysis_results 
                              WHERE sha256_url = UNHEX(%s) AND status = 'ANALYZED'""", 
                          (sha256_url,), commit=True)

    # if we're at this point it means that when we asked the database for an entry from cloudphish_analysis_results
    # it was empty, OR, we cleared existing analysis
    # however, we could have multiple requests coming in at the same time for the same url
    # so we need to take that into account here

    # first we'll generate our analysis uuid we're going to use
    _uuid = str(uuid.uuid4())

    # so first we try to insert it
    try:
        execute_with_retry(db, c, ["""INSERT INTO cloudphish_analysis_results ( sha256_url, uuid, insert_date ) 
                                      VALUES ( UNHEX(%s), %s, NOW() )""",
                                   """INSERT INTO cloudphish_url_lookup ( sha256_url, url )
                                      VALUES ( UNHEX(%s), %s )"""],
                           [(sha256_url, _uuid),
                            (sha256_url, url)], commit=True)
    except pymysql.err.IntegrityError as e:
        # (<class 'pymysql.err.IntegrityError'>--(1062, "Duplicate entry
        # if we get a duplicate key entry here then it means that an entry was created between when we asked
        # and now
        if e.args[0] != 1062:
            raise e

        # so just return that one that was already created
        return get_cached_analysis(url)

    # at this point we've inserted an entry into cloudphish_analysis_results for this url
    # now at it's processing to the workload

    root = RootAnalysis()
    root.uuid = _uuid
    root.storage_dir = storage_dir_from_uuid(root.uuid)
    root.initialize_storage()
    root.analysis_mode = ANALYSIS_MODE_CLOUDPHISH
    # this is kind of a kludge but,
    # the company_id initially starts out as whatever the default is for this node
    # later, should the analysis turn into an alert, the company_id changes to whatever
    # is stored as the "d" field in the KEY_DETAILS_CONTEXT
    root.company_id = saq.COMPANY_ID
    root.tool = 'ACE - Cloudphish'
    root.tool_instance = saq.SAQ_NODE
    root.alert_type = ANALYSIS_TYPE_CLOUDPHISH
    root.description = 'ACE Cloudphish Detection - {}'.format(url)
    root.event_time = datetime.datetime.now()
    root.details = {
        KEY_DETAILS_URL: url,
        KEY_DETAILS_SHA256_URL: sha256_url,
        # this used to be configurable but it's always true now
        KEY_DETAILS_ALERTABLE: True,
        KEY_DETAILS_CONTEXT: details, # <-- optionally contains the source company_id
    }

    url_observable = root.add_observable(F_URL, url)
    if url_observable:
        url_observable.add_directive(DIRECTIVE_CRAWL)

    root.save()
    root.schedule()

    return get_cached_analysis(url)
Exemple #4
0
def upload(uuid):
    
    validate_uuid(uuid)

    if KEY_UPLOAD_MODIFIERS not in request.values:
        abort(Response("missing key {} in request".format(KEY_UPLOAD_MODIFIERS), 400))

    if KEY_ARCHIVE not in request.files:
        abort(Response("missing files key {}".format(KEY_ARCHIVE), 400))

    upload_modifiers = json.loads(request.values[KEY_UPLOAD_MODIFIERS])
    if not isinstance(upload_modifiers, dict):
        abort(Response("{} should be a dict".format(KEY_UPLOAD_MODIFIERS), 400))

    overwrite = False
    if KEY_OVERWRITE in upload_modifiers:
        overwrite = upload_modifiers[KEY_OVERWRITE]
        if not isinstance(overwrite, bool):
            abort(Response("{} should be a boolean".format(KEY_OVERWRITE), 400))

    sync = False
    if KEY_SYNC in upload_modifiers:
        sync = upload_modifiers[KEY_SYNC]
        if not isinstance(sync, bool):
            abort(Response("{} should be a boolean".format(KEY_SYNC), 400))

    logging.info("requested upload for {}".format(uuid))

    # does the target directory already exist?
    target_dir = storage_dir_from_uuid(uuid)
    if os.path.exists(target_dir):
        # are we over-writing it?
        if not overwrite:
            abort(Response("{} already exists (specify overwrite modifier to replace the data)".format(target_dir), 400))

        # if we are overwriting the entry then we need to completely clear the 
        # TODO implement this

    try:
        os.makedirs(target_dir)
    except Exception as e:
        logging.error("unable to create directory {}: {}".format(target_dir, e))
        report_exception()
        abort(Response("unable to create directory {}: {}".format(target_dir, e), 400))

    logging.debug("target directory for {} is {}".format(uuid, target_dir))

    # save the tar file so we can extract it
    fp, tar_path = tempfile.mkstemp(suffix='.tar', prefix='upload_{}'.format(uuid), dir=saq.TEMP_DIR)
    os.close(fp)

    try:
        request.files[KEY_ARCHIVE].save(tar_path)

        t = tarfile.open(tar_path, 'r|')
        t.extractall(path=target_dir)

        logging.debug("extracted {} to {}".format(uuid, target_dir))

        # update the root analysis to indicate it's new location 
        root = RootAnalysis(storage_dir=target_dir)
        root.load()

        root.location = saq.SAQ_NODE
        root.company_id = saq.COMPANY_ID
        root.company_name = saq.COMPANY_NAME

        root.save()

        if sync:
            root.schedule()

        # looks like it worked
        return json_result({'result': True})

    except Exception as e:
        logging.error("unable to upload {}: {}".format(uuid, e))
        report_exception()
        abort(Response("unable to upload {}: {}".format(uuid, e)))

    finally:
        try:
            os.remove(tar_path)
        except Exception as e:
            logging.error("unable to remove {}: {}".format(tar_path,e ))