Exemplo n.º 1
0
def download_signatures(**kwargs):
    """
    Download signatures from the system.
    
    Variables:
    None 
    
    Arguments: 
    query       => SOLR query to filter the signatures
                   Default: All deployed signatures
    safe        => Get a ruleset that will work in yara
                   Default: False
    
    Data Block:
    None
    
    Result example:
    <A .YAR SIGNATURE FILE>
    """
    user = kwargs['user']
    query = request.args.get('query', 'meta.al_status:DEPLOYED')

    safe = request.args.get('safe', "false")
    if safe.lower() == 'true':
        safe = True
    else:
        safe = False

    access = user['access_control']
    last_modified = STORAGE.get_signatures_last_modified()

    query_hash = md5(query + access + last_modified).hexdigest() + ".yar"

    signature_cache = TransportLocal(
        base=os.path.join(config.system.root, 'var', 'cache', 'signatures')
    )

    response = _get_cached_signatures(
        signature_cache, last_modified, query_hash
    )

    if response:
        return response

    with ExclusionWindow(query_hash, 30):
        response = _get_cached_signatures(
            signature_cache, last_modified, query_hash
        )
        if response:
            return response

        keys = STORAGE.list_filtered_signature_keys(query, access_control=access)
        signature_list = STORAGE.get_signatures(keys)
    
        # Sort rules to satisfy dependencies
        duplicate_rules = []
        error_rules = []
        global_rules = []
        private_rules_no_dep = []
        private_rules_dep = []
        rules_no_dep = []
        rules_dep = []

        if safe:
            rules_map = {}
            for s in signature_list:
                name = s.get('name', None)
                if not name:
                    continue

                version = int(s.get('meta', {}).get('rule_version', '1'))

                p = rules_map.get(name, {})
                pversion = int(p.get('meta', {}).get('rule_version', '0'))

                if version < pversion:
                    duplicate_rules.append(name)
                    continue
 
                rules_map[name] = s
            signature_list = rules_map.values()

        name_map = {}
        for s in signature_list:
            if s['type'].startswith("global"):
                global_rules.append(s)
                name_map[s['name']] = True
            elif s['type'].startswith("private"):
                if s['depends'] is None or len(s['depends']) == 0:
                    private_rules_no_dep.append(s)
                    name_map[s['name']] = True
                else:
                    private_rules_dep.append(s)
            else:
                if s['depends'] is None or len(s['depends']) == 0:
                    rules_no_dep.append(s)
                    name_map[s['name']] = True
                else:
                    rules_dep.append(s)

        global_rules = sorted(global_rules, key=lambda k: k['meta']['id'])
        private_rules_no_dep = sorted(private_rules_no_dep, key=lambda k: k['meta']['id'])
        rules_no_dep = sorted(rules_no_dep, key=lambda k: k['meta']['id'])
        private_rules_dep = sorted(private_rules_dep, key=lambda k: k['meta']['id'])
        rules_dep = sorted(rules_dep, key=lambda k: k['meta']['id'])
    
        signature_list = global_rules + private_rules_no_dep
        while private_rules_dep:
            new_private_rules_dep = []
            for r in private_rules_dep:
                found = False
                for d in r['depends']:
                    if not name_map.get(d, False):
                        new_private_rules_dep.append(r)
                        found = True
                        break
                if not found:
                    name_map[r['name']] = True
                    signature_list.append(r)
            
            if private_rules_dep == new_private_rules_dep:
                for x in private_rules_dep:
                    error_rules += [d for d in x["depends"]]

                if not safe:
                    for s in private_rules_dep:
                        name_map[s['name']] = True
                    signature_list += private_rules_dep

                new_private_rules_dep = []
            
            private_rules_dep = new_private_rules_dep

        signature_list += rules_no_dep
        while rules_dep:
            new_rules_dep = []
            for r in rules_dep:
                found = False
                for d in r['depends']:
                    if not name_map.get(d, False):
                        new_rules_dep.append(r)
                        found = True
                        break
                if not found:
                    name_map[r['name']] = True
                    signature_list.append(r)
        
            if rules_dep == new_rules_dep:
                error_rules += [x["name"] for x in rules_dep]
                if not safe:
                    for s in rules_dep:
                        name_map[s['name']] = True
                    signature_list += rules_dep

                new_rules_dep = []

            rules_dep = new_rules_dep    
        # End of sort
    
        error = ""
        if duplicate_rules:
            if safe:
                err_txt = "were skipped"
            else:
                err_txt = "exist"
            error += dedent("""\
            
                // [ERROR] Duplicates rules {msg}:
                //
                //	{rules}
                //
                """).format(msg=err_txt, rules="\n//\t".join(duplicate_rules))
        if error_rules:
            if safe:
                err_txt = "were skipped due to"
            else:
                err_txt = "are"
            error += dedent("""\
            
                // [ERROR] Some rules {msg} missing dependencies:
                //
                //	{rules}
                //
                """).format(msg=err_txt, rules="\n//\t".join(error_rules))
        # noinspection PyAugmentAssignment

        header = dedent("""\
            // Signatures last updated: {last_modified}
            // Signatures matching filter:
            //
            //	{query}
            // {error}
            // Number of rules in file:
            //
            """).format(query=query, error=error, last_modified=last_modified)

        rule_file_bin = header + YARA_PARSER().dump_rule_file(signature_list)
        rule_file_bin = rule_file_bin

        signature_cache.save(query_hash, rule_file_bin)

        return make_file_response(
            rule_file_bin, "al_yara_signatures.yar",
            len(rule_file_bin), content_type="text/yara"
        )
Exemplo n.º 2
0
def change_status(sid, rev, status, **kwargs):
    """
    [INCOMPLETE]
       - DISABLE OTHER REVISION OF THE SAME SIGNTURE WHEN DEPLOYING ONE
    Change the status of a signature
       
    Variables:
    sid    =>  ID of the signature
    rev    =>  Revision number of the signature
    status  =>  New state
    
    Arguments: 
    None
    
    Data Block:
    None
    
    Result example:
    { "success" : true }      #If saving the rule was a success or not
    """
    DEPLOYED_STATUSES = ['DEPLOYED', 'NOISY', 'DISABLED']
    DRAFT_STATUSES = ['STAGING', 'TESTING']
    STALE_STATUSES = ['INVALID']
    user = kwargs['user']
    if status == 'INVALID':
        return make_api_response("",
                                 "INVALID signature status is reserved for service use only.",
                                 403)
    if not user['is_admin'] and status in DEPLOYED_STATUSES:
        return make_api_response("",
                                 "Only admins are allowed to change the signature status to a deployed status.",
                                 403)
    
    key = "%sr.%s" % (sid, rev)
    data = STORAGE.get_signature(key)
    if data:
        if not Classification.is_accessible(user['classification'], data['meta'].get('classification',
                                                                                     Classification.UNRESTRICTED)):
            return make_api_response("", "You are not allowed change status on this signature", 403)
    
        if data['meta']['al_status'] in STALE_STATUSES and status not in DRAFT_STATUSES:
            return make_api_response("",
                                     "Only action available while signature in {} status is to change "
                                     "signature to a DRAFT status"
                                     .format(data['meta']['al_status']),
                                     403)

        if data['meta']['al_status'] in DEPLOYED_STATUSES and status in DRAFT_STATUSES:
            return make_api_response("", "You cannot change the status of signature %s r.%s from %s to %s." %
                                     (sid, rev, data['meta']['al_status'], status), 403)

        query = "meta.al_status:{status} AND _yz_rk:{sid}* AND NOT _yz_rk:{key}"
        today = datetime.date.today().isoformat()
        uname = user['uname']

        if status not in ['DISABLED', 'INVALID', 'TESTING']:
            for other in STORAGE.get_signatures(
                STORAGE.list_filtered_signature_keys(
                    query.format(key=key, sid=sid, status=status)
                )
            ):
                other['meta']['al_state_change_date'] = today
                other['meta']['al_state_change_user'] = uname
                other['meta']['al_status'] = 'DISABLED'

                other_sid = other['meta']['id']
                other_rev = other['meta']['rule_version']
                other_key = "%sr.%s" % (other_sid, other_rev)
                STORAGE.save_signature(other_key, other)

        data['meta']['al_state_change_date'] = today
        data['meta']['al_state_change_user'] = uname
        data['meta']['al_status'] = status

        STORAGE.save_signature(key, data)
        return make_api_response({"success": True})
    else:
        return make_api_response("", "Signature not found. (%s r.%s)" % (sid, rev), 404)