Bootstrap

Suricata-签名

Thank Zhihao Tao for your hard work. The document spent countless nights and weekends, using his hard work to make it convenient for everyone.
If you have any questions, please send a email to [email protected]


1. 概述

签名在Suricata中扮演着非常重要的角色。

规则(rule)/签名(signature)包括三部分内容:

  • action,签名匹配时的动作
  • header,限定规则的协议,IP地址,端口和方向。
  • rule options,定义规则的细节。

示例:

alert http $EXTERNAL_NET any -> $HTTP_SERVERS any (msg:"ET WEB_SPECIFIC_APPS Gazi Download Portal SQL Injection Attempt -- down_indir.asp id DELETE"; flow:established,to_server; content:"/down_indir.asp?"; nocase; http_uri; content:"id="; nocase; http_uri; content:"DELETE"; nocase; http_uri; pcre:"/DELETE.+FROM/Ui"; reference:cve,CVE-2007-2810; reference:url,www.securityfocus.com/bid/23714; reference:url,doc.emergingthreats.net/2004002; classtype:web-application-attack; sid:2004002; rev:9; metadata:affected_product Web_Server_Applications, attack_target Web_Server, created_at 2010_07_30, deployment Datacenter, signature_severity Major, tag SQL_Injection, updated_at 2019_09_27;)

后文皆以此示例为例。

2. 签名组成

2.1 签名结构体

typedef struct Signature_ {
    uint32_t flags;
    /* coccinelle: Signature:flags:SIG_FLAG_ */

    AppProto alproto;

    uint16_t dsize_low;
    uint16_t dsize_high;

    SignatureMask mask;
    SigIntId num; /**< signature number, internal id */

    /** inline -- action */
    uint8_t action;
    uint8_t file_flags;

    /** addresses, ports and proto this sig matches on */
    DetectProto proto;

    /** classification id **/
    uint16_t class_id;

    /** ipv4 match arrays */
    uint16_t addr_dst_match4_cnt;
    uint16_t addr_src_match4_cnt;
    uint16_t addr_dst_match6_cnt;
    uint16_t addr_src_match6_cnt;
    DetectMatchAddressIPv4 *addr_dst_match4;
    DetectMatchAddressIPv4 *addr_src_match4;
    /** ipv6 match arrays */
    DetectMatchAddressIPv6 *addr_dst_match6;
    DetectMatchAddressIPv6 *addr_src_match6;

    uint32_t id;  /**< sid, set by the 'sid' rule keyword */
    uint32_t gid; /**< generator id */
    uint32_t rev;
    int prio;

    /** port settings for this signature */
    DetectPort *sp, *dp;

#ifdef PROFILING
    uint16_t profiling_id;
#endif

    /** netblocks and hosts specified at the sid, in CIDR format */
    IPOnlyCIDRItem *CidrSrc, *CidrDst;

    DetectEngineAppInspectionEngine *app_inspect;
    DetectEnginePktInspectionEngine *pkt_inspect;

    /* Matching structures for the built-ins. The others are in
     * their inspect engines. */
    SigMatchData *sm_arrays[DETECT_SM_LIST_MAX];

    /* memory is still owned by the sm_lists/sm_arrays entry */
    const struct DetectFilestoreData_ *filestore_ctx;

    char *msg;

    /** classification message */
    char *class_msg;
    /** Reference */
    DetectReference *references;
    /** Metadata */
    DetectMetadata *metadata;

    char *sig_str;

    SignatureInitData *init_data;

    /** ptr to the next sig in the list */
    struct Signature_ *next;
} Signature;

2.1.1 flags

  • SIG_FLAG_SRC_ANYSIG_FLAG_DST_ANYSIG_FLAG_SP_ANYSIG_FLAG_DP_ANY分别代表源IP目的IP源端口目的端口any
#define SIG_FLAG_SRC_ANY                BIT_U32(0)  /**< source is any */
#define SIG_FLAG_DST_ANY                BIT_U32(1)  /**< destination is any */
#define SIG_FLAG_SP_ANY                 BIT_U32(2)  /**< source port is any */
#define SIG_FLAG_DP_ANY                 BIT_U32(3)  /**< destination port is any */
  • SIG_FLAG_NOALERT代表noalert选项被设置。
#define SIG_FLAG_NOALERT                BIT_U32(4)  /**< no alert flag is set */
  • SIG_FLAG_DSIZE代表dsize选项被设置。
#define SIG_FLAG_DSIZE                  BIT_U32(5)  /**< signature has a dsize setting */

  • SIG_FLAG_APPLAYER代表签名匹配基于应用层而不是基于报文。
#define SIG_FLAG_APPLAYER               BIT_U32(6)  /**< signature applies to app layer instead of packets */
  • SIG_FLAG_IPONLY代表初始化的签名仅限于IP。
#define SIG_FLAG_IPONLY                 BIT_U32(7)  /**< ip only signature */
  • SIG_FLAG_REQUIRE_PACKETSIG_FLAG_REQUIRE_STREAM代表检测仅基于packetstream
#define SIG_FLAG_REQUIRE_PACKET         BIT_U32(9)  /**< signature is requiring packet match */
#define SIG_FLAG_REQUIRE_STREAM         BIT_U32(10) /**< signature is requiring stream match */
  • SIG_FLAG_MPM_NEG代表content需要检测否定逻辑。
#define SIG_FLAG_MPM_NEG                BIT_U32(11)
  • SIG_FLAG_FLUSH代表客户端和服务端body需要尽可能与流同步检查,即检测逻辑需要流刷新通知。
#define SIG_FLAG_FLUSH                  BIT_U32(12) /**< detection logic needs stream flush notification */
  • SIG_FLAG_REQUIRE_FLOWVAR代表仅flowbitflowvarflowint生效时检测有效。
#define SIG_FLAG_REQUIRE_FLOWVAR        BIT_U32(17) /**< signature can only match if a flowbit, flowvar or flowint is available. */
  • SIG_FLAG_FILESTORE代表filestore被设置。如果签名匹配,则将文件存储到磁盘。
#define SIG_FLAG_FILESTORE              BIT_U32(18) /**< signature has filestore keyword */
  • SIG_FLAG_TOSERVERSIG_FLAG_TOCLIENT代表检测的方向。
#define SIG_FLAG_TOSERVER               BIT_U32(19)
#define SIG_FLAG_TOCLIENT               BIT_U32(20)
  • SIG_FLAG_TLSSTORE代表设置了tls.store选项,在磁盘上存储TLS/SSL证书。
#define SIG_FLAG_TLSSTORE               BIT_U32(21)
  • SIG_FLAG_BYPASS代表设置了bypass选项,将流量排除在进一步评估之外。
#define SIG_FLAG_BYPASS                 BIT_U32(22)
  • SIG_FLAG_PREFILTER代表签名是预过滤引擎的一部分。
#define SIG_FLAG_PREFILTER              BIT_U32(23) /**< sig is part of a prefilter engine */

  • SIG_FLAG_PDONLY代表初始化的签名只检测协议。
/** Proto detect only signature.
 *  Inspected once per direction when protocol detection is done. */
#define SIG_FLAG_PDONLY                 BIT_U32(24)
  • SIG_FLAG_SRC_IS_TARGETSIG_FLAG_DEST_IS_TARGET代表target选项被设置
/** Info for Source and Target identification */
#define SIG_FLAG_SRC_IS_TARGET          BIT_U32(25)
/** Info for Source and Target identification */
#define SIG_FLAG_DEST_IS_TARGET         BIT_U32(26)

#define SIG_FLAG_HAS_TARGET             (SIG_FLAG_DEST_IS_TARGET|SIG_FLAG_SRC_IS_TARGET)

2.1.2 alproto和proto

签名的应用层和三层协议。

enum AppProtoEnum {
    ALPROTO_UNKNOWN = 0,
    ALPROTO_HTTP,
    ALPROTO_FTP,
    ALPROTO_SMTP,
    ALPROTO_TLS, /* SSLv2, SSLv3 & TLSv1 */
...
    ALPROTO_MAX,
};

2.1.3 基础选项

  • num签名的内部唯一Id。

  • action签名的动作。

  • class_id签名的类类型。

  • dsize_lowdsize_high签名中dsize配置的size值。

  • IP地址相关:

    • addr_dst_match4_cnt
    • addr_src_match4_cnt
    • addr_dst_match6_cnt
    • addr_src_match6_cnt
    • addr_dst_match4
    • addr_src_match4
    • addr_dst_match6
    • addr_src_match6
  • id签名sid

  • gid签名组ID。

  • rev签名修订版本号。

  • prio签名优先级。

  • spdp签名端口号。

  • CidrSrcCidrDst以CIDR格式在sid处指定的netblockshosts

  • msg签名信息。

  • class_msg签名的类别消息。

  • references签名的引用信息。

  • metadata签名的元信息。

  • sig_str签名的完整信息。

    DetectEngineAppInspectionEngine *app_inspect;
    DetectEnginePktInspectionEngine *pkt_inspect;

    /* memory is still owned by the sm_lists/sm_arrays entry */
    const struct DetectFilestoreData_ *filestore_ctx;

    SignatureInitData *init_data;

2.1.5 mask

#define SIG_MASK_REQUIRE_PAYLOAD            BIT_U8(0)
#define SIG_MASK_REQUIRE_FLOW               BIT_U8(1)
#define SIG_MASK_REQUIRE_FLAGS_INITDEINIT   BIT_U8(2)    /* SYN, FIN, RST */
#define SIG_MASK_REQUIRE_FLAGS_UNUSUAL      BIT_U8(3)    /* URG, ECN, CWR */
#define SIG_MASK_REQUIRE_NO_PAYLOAD         BIT_U8(4)
#define SIG_MASK_REQUIRE_DCERPC             BIT_U8(5)    /* require either SMB+DCE or raw DCE */
// vacancy
#define SIG_MASK_REQUIRE_ENGINE_EVENT       BIT_U8(7)

2.1.6 file_flags

#define FILE_SIG_NEED_FILE          0x01
#define FILE_SIG_NEED_FILENAME      0x02
#define FILE_SIG_NEED_MAGIC         0x04    /**< need the start of the file */
#define FILE_SIG_NEED_FILECONTENT   0x08
#define FILE_SIG_NEED_MD5           0x10
#define FILE_SIG_NEED_SHA1          0x20
#define FILE_SIG_NEED_SHA256        0x40
#define FILE_SIG_NEED_SIZE          0x80

2.1.7 sm_arrays

SigMatchData *sm_arrays[DETECT_SM_LIST_MAX];

签名内置匹配数据分为多种:

  • DETECT_SM_LIST_MATCH: packet签名内置匹配。
  • DETECT_SM_LIST_PMATCH: payload签名内置匹配。
  • DETECT_SM_LIST_BASE64_DATA: base64_data签名内置匹配。
  • DETECT_SM_LIST_POSTMATCH: postmatch签名内置匹配。
  • DETECT_SM_LIST_TMATCH: tag签名内置匹配。
  • DETECT_SM_LIST_SUPPRESS: suppress签名内置匹配。
  • DETECT_SM_LIST_THRESHOLD: threshold签名内置匹配。

2.2 签名动作

所有签名具有不同的属性,action决定了签名匹配时会发生什么。有四种类型的动作。

#define ACTION_ALERT        0x01
#define ACTION_DROP         0x02
#define ACTION_REJECT       0x04
#define ACTION_REJECT_DST   0x08
#define ACTION_REJECT_BOTH  0x10
#define ACTION_PASS         0x20

IPS/inline可以两种方式阻止网络流量。一种方法是drop,另一种是reject

规则将按照它们在文件中出现的顺序进行加载。但是它们将以不同的顺序进行处理。签名具有不同的优先级。最重要的签名将首先被扫描。可以更改优先级顺序。默认顺序为:passdroprejectalert

action-order:
 - pass
 - drop
 - reject
 - alert

这意味着在drop规则之前先考虑pass规则,在reject规则之前先考虑drop规则,依此类推。

2.2.1 pass

如果签名匹配且包含pass,则Suricata停止扫描数据包并跳过后面所有规则(仅适用于当前数据包)。

2.2.2 drop

这仅涉及IPS/inline模式。如果程序找到匹配的签名(包含drop),它将立即停止。数据包将不再发送。缺点:接收器未收到正在发生的消息,导致超时(某些情况下使用TCP)。Suricata会为此数据包生成告警。

2.2.3 reject

这是对数据包的主动拒绝。接收方和发送方都接收拒绝数据包。有两种类型的拒绝数据包将被自动选择。如果有问题的数据包涉及TCP,它将是一个(RST)复位数据包。对于所有其他协议,它将是ICMP错误数据包。Suricata还会生成告警。在IPS/inline模式下,与drop操作一样,也将丢弃有问题的数据包。

2.2.4 alert

如果签名匹配并包含alert,则该数据包将被视为与其他任何非威胁性数据包一样,除了这一特征外,Suricata将生成告警。只有系统管理员才能注意到此警报。

2.3 签名协议

签名中关于协议的字段。
可以在四种基本协议和应用层协议之间进行选择:

  • tcpudpicmpip
  • httpftptlssmb

2.4 源和目的

目的地,分别指定流量的来源和流量的目的地。您可以分配IP地址(同时支持IPv4和IPv6)和IP范围。这些可以与运算符结合使用:

操作员描述
../..IP范围(CIDR表示法)
!否定
[.., ..]分组

通常,您还可以使用变量,例如$HOME_NET$EXTERNAL_NET。配置文件指定了这些关注的IP地址,这些设置将代替规则中的变量。

例如:

示例含义
!1.1.1.1每个IP地址,除1.1.1.1
![1.1.1.1, 1.1.1.2]除1.1.1.1和1.1.1.2之外的每个IP地址
$HOME_NETYaml中设置的HOME_NET
[10.0.0.0/24, !10.0.0.5]10.0.0.0/24,除了10.0.0.5

2.5 端口(源和目的)

注意,端口并不指示通信中使用的协议。相反,它确定哪个应用程序正在接收数据。

上面提到的端口通常是目标端口。通常,操作系统会为源端口(即发送数据包的应用程序)分配一个随机端口。为自己的HTTP服务编写规则时,通常会写,因为这意味着从任何源端口到HTTP应用程序(在端口80上运行)的任何数据包都将匹配。any -> 80

如上所述,在设置端口时,您也可以使用特殊的运算符。像这样的标志:

操作符描述
:端口范围
!否定
[.., ..]分组

例如:

示例含义
[80,81,82]端口80、81和82
[80:82]80至82
[1024:]从1024到最高端口号
!80每个端口,除80
[80:100, !99]80至100,但排除99

2.6 方向

方向指示签名必须以哪种方式匹配。几乎每个签名的右边都有一个箭头(->)。这意味着只有方向相同的数据包才能匹配。但是,也可以使规则与两种方式都匹配(<>)。

2.7 签名规则选项

签名规则选项用括号括起来,并用分号分隔。某些选项具有设置(例如msg),这些设置由选项的关键字指定,后跟冒号,然后是设置。其他人则没有设置,只是关键字(例如nocase)。

<keyword>: <settings>;
<keyword>;

2.7.1 签名基本关键字信息

2.7.1.1 签名ID

签名ID,即sid。每个签名都有一个自己的id。这个id用一个数字表示。

注意:
按照惯例,签名sid作为签名的最后一个关键字(如果有rev,则为倒数第二个关键字)。

2.7.1.1.1 代码
void DetectSidRegister (void)
{
    sigmatch_table[DETECT_SID].name = "sid";
    sigmatch_table[DETECT_SID].desc = "set rule ID";
    sigmatch_table[DETECT_SID].url = "/rules/meta.html#sid-signature-id";
    sigmatch_table[DETECT_SID].Match = NULL;
    sigmatch_table[DETECT_SID].Setup = DetectSidSetup;
    sigmatch_table[DETECT_SID].Free = NULL;
    sigmatch_table[DETECT_SID].RegisterTests = DetectSidRegisterTests;
}
void DetectSidRegister (void)
{
    sigmatch_table[DETECT_SID].name = "sid";
    sigmatch_table[DETECT_SID].desc = "set rule ID";
    sigmatch_table[DETECT_SID].url = "/rules/meta.html#sid-signature-id";
    sigmatch_table[DETECT_SID].Match = NULL;
    sigmatch_table[DETECT_SID].Setup = DetectSidSetup;
    sigmatch_table[DETECT_SID].Free = NULL;
    sigmatch_table[DETECT_SID].RegisterTests = DetectSidRegisterTests;
}
2.7.1.2 签名组ID

签名组ID,即gid。用于给不同的签名组另一个id值(类似于sid)。Suricata默认gid1

2.7.1.2.1 代码
void DetectGidRegister (void)
{
    sigmatch_table[DETECT_GID].name = "gid";
    sigmatch_table[DETECT_GID].desc = "give different groups of signatures another id value";
    sigmatch_table[DETECT_GID].url = "/rules/meta.html#gid-group-id";
    sigmatch_table[DETECT_GID].Match = NULL;
    sigmatch_table[DETECT_GID].Setup = DetectGidSetup;
    sigmatch_table[DETECT_GID].Free  = NULL;
    sigmatch_table[DETECT_GID].RegisterTests = GidRegisterTests;
}
static int DetectGidSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
{
    unsigned long gid = 0;
    char *endptr = NULL;
    gid = strtoul(rawstr, &endptr, 10);
    if (endptr == NULL || *endptr != '\0') {
        SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg "
                   "to gid keyword");
        goto error;
    }
    if (gid >= UINT_MAX) {
        SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "gid value to high, max %u", UINT_MAX);
        goto error;
    }

    s->gid = (uint32_t)gid;

    return 0;

 error:
    return -1;
}
2.7.1.3 签名信息

关键字msg提供有关签名和告警的文本信息。

2.7.1.3.1 代码
void DetectMsgRegister (void)
{
    sigmatch_table[DETECT_MSG].name = "msg";
    sigmatch_table[DETECT_MSG].desc = "information about the rule and the possible alert";
    sigmatch_table[DETECT_MSG].url = "/rules/meta.html#msg-message";
    sigmatch_table[DETECT_MSG].Match = NULL;
    sigmatch_table[DETECT_MSG].Setup = DetectMsgSetup;
    sigmatch_table[DETECT_MSG].Free = NULL;
    sigmatch_table[DETECT_MSG].RegisterTests = DetectMsgRegisterTests;
    sigmatch_table[DETECT_MSG].flags = SIGMATCH_QUOTES_MANDATORY;
}
static int DetectMsgSetup (DetectEngineCtx *de_ctx, Signature *s, const char *msgstr)
{
    size_t slen = strlen(msgstr);
    if (slen == 0)
        return -1;

    char input[slen + 1];
    strlcpy(input, msgstr, slen + 1);
    char *str = input;
    char converted = 0;

    {
        uint16_t i, x;
        uint8_t escape = 0;

        /* it doesn't matter if we need to escape or not we remove the extra "\" to mimic snort */
        for (i = 0, x = 0; i < slen; i++) {
            //printf("str[%02u]: %c\n", i, str[i]);
            if(!escape && str[i] == '\\') {
                escape = 1;
            } else if (escape) {
                if (str[i] != ':' &&
                        str[i] != ';' &&
                        str[i] != '\\' &&
                        str[i] != '\"')
                {
                    SCLogDebug("character \"%c\" does not need to be escaped but is" ,str[i]);
                }
                escape = 0;
                converted = 1;

                str[x] = str[i];
                x++;
            }else{
                str[x] = str[i];
                x++;
            }

        }
#if 0 //def DEBUG
        if (SCLogDebugEnabled()) {
            for (i = 0; i < x; i++) {
                printf("%c", str[i]);
            }
            printf("\n");
        }
#endif

        if (converted) {
            slen = x;
            str[slen] = '\0';
        }
    }

    if (s->msg != NULL) {
        SCLogError(SC_ERR_INVALID_SIGNATURE, "duplicated 'msg' keyword detected");
        goto error;
    }
    s->msg = SCStrdup(str);
    if (s->msg == NULL)
        goto error;
    return 0;

error:
    return -1;
}
2.7.1.4 签名修订

rev代表签名的版本。如果修改了签名,则签名作者将增加rev的数量。

2.7.1.4.1 代码
void DetectRevRegister (void)
{
    sigmatch_table[DETECT_REV].name = "rev";
    sigmatch_table[DETECT_REV].desc = "set version of the rule";
    sigmatch_table[DETECT_REV].url = "/rules/meta.html#rev-revision";
    sigmatch_table[DETECT_REV].Match = NULL;
    sigmatch_table[DETECT_REV].Setup = DetectRevSetup;
    sigmatch_table[DETECT_REV].Free  = NULL;
    sigmatch_table[DETECT_REV].RegisterTests = NULL;
}
static int DetectRevSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
{
    unsigned long rev = 0;
    char *endptr = NULL;
    rev = strtoul(rawstr, &endptr, 10);
    if (endptr == NULL || *endptr != '\0') {
        SCLogError(SC_ERR_INVALID_SIGNATURE, "invalid character as arg "
                   "to rev keyword");
        goto error;
    }
    if (rev >= UINT_MAX) {
        SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "rev value to high, max %u", UINT_MAX);
        goto error;
    }
    if (rev == 0) {
        SCLogError(SC_ERR_INVALID_NUMERIC_VALUE, "rev value 0 is invalid");
        goto error;
    }
    if (s->rev > 0) {
        SCLogError(SC_ERR_INVALID_RULE_ARGUMENT, "duplicated 'rev' keyword detected");
        goto error;
    }

    s->rev = (uint32_t)rev;

    return 0;

 error:
    return -1;
}
2.7.1.5 签名类类型

classtype提供有关规则和告警分类的信息。它由短名称长名称优先级组成。classtype定义在classification.config文件中。

2.7.1.5.1 代码
void DetectClasstypeRegister(void)
{
    sigmatch_table[DETECT_CLASSTYPE].name = "classtype";
    sigmatch_table[DETECT_CLASSTYPE].desc = "information about the classification of rules and alerts";
    sigmatch_table[DETECT_CLASSTYPE].url = "/rules/meta.html#classtype";
    sigmatch_table[DETECT_CLASSTYPE].Setup = DetectClasstypeSetup;
    sigmatch_table[DETECT_CLASSTYPE].RegisterTests = DetectClasstypeRegisterTests;

    DetectSetupParseRegexes(PARSE_REGEX, &regex, &regex_study);
}
  • 解析classtype,查找classtype配置信息
static int DetectClasstypeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
{
    char parsed_ct_name[CLASSTYPE_NAME_MAX_LEN] = "";

    if ((s->class_id > 0) || (s->class_msg != NULL)) {
        if (SigMatchStrictEnabled(DETECT_CLASSTYPE)) {
            SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "duplicated 'classtype' "
                    "keyword detected.");
            return -1;
        } else {
            SCLogWarning(SC_ERR_CONFLICTING_RULE_KEYWORDS, "duplicated 'classtype' "
                    "keyword detected. Using instance with highest priority");
        }
    }

    if (DetectClasstypeParseRawString(rawstr, parsed_ct_name, sizeof(parsed_ct_name)) < 0) {
        SCLogError(SC_ERR_PCRE_PARSE, "invalid value for classtype keyword: "
                "\"%s\"", rawstr);
        return -1;
    }

    bool real_ct = true;
    SCClassConfClasstype *ct = SCClassConfGetClasstype(parsed_ct_name, de_ctx);
    if (ct == NULL) {
        if (SigMatchStrictEnabled(DETECT_CLASSTYPE)) {
            SCLogError(SC_ERR_UNKNOWN_VALUE, "unknown classtype '%s'",
                    parsed_ct_name);
            return -1;
        }

        if (s->id > 0) {
            SCLogWarning(SC_ERR_UNKNOWN_VALUE, "signature sid:%u uses "
                    "unknown classtype: \"%s\", using default priority %d. "
                    "This message won't be shown again for this classtype",
                    s->id, parsed_ct_name, DETECT_DEFAULT_PRIO);
        } else if (de_ctx->rule_file != NULL) {
            SCLogWarning(SC_ERR_UNKNOWN_VALUE, "signature at %s:%u uses "
                    "unknown classtype: \"%s\", using default priority %d. "
                    "This message won't be shown again for this classtype",
                    de_ctx->rule_file, de_ctx->rule_line,
                    parsed_ct_name, DETECT_DEFAULT_PRIO);
        } else {
            SCLogWarning(SC_ERR_UNKNOWN_VALUE, "unknown classtype: \"%s\", "
                    "using default priority %d. "
                    "This message won't be shown again for this classtype",
                    parsed_ct_name, DETECT_DEFAULT_PRIO);
        }

        char str[256];
        snprintf(str, sizeof(str),
                "config classification: %s,Unknown Classtype,%d\n",
                parsed_ct_name, DETECT_DEFAULT_PRIO);

        if (SCClassConfAddClasstype(de_ctx, str, 0) < 0)
            return -1;
        ct = SCClassConfGetClasstype(parsed_ct_name, de_ctx);
        if (ct == NULL)
            return -1;
        real_ct = false;
    }

    /* set prio only if not already explicitly set by 'priority' keyword.
     * update classtype in sig, but only if it is 'real' (not undefined)
     * update sigs classtype if its prio is lower (but not undefined)
     */

    bool update_ct = false;
    if ((s->init_data->init_flags & SIG_FLAG_INIT_PRIO_EXPLICT) != 0) {
        /* don't touch Signature::prio */
        update_ct = true;
    } else if (s->prio == -1) {
        s->prio = ct->priority;
        update_ct = true;
    } else {
        if (ct->priority < s->prio) {
            s->prio = ct->priority;
            update_ct = true;
        }
    }

    if (real_ct && update_ct) {
        s->class_id = ct->classtype_id;
        s->class_msg = ct->classtype_desc;
    }
    return 0;
}
2.7.1.6 签名参考

reference关键字直接指向可以找到有关签名和签名要解决的问题的信息的地方。参考关键字可以在签名中多次出现。

2.7.1.6.1 代码
void DetectReferenceRegister(void)
{
    sigmatch_table[DETECT_REFERENCE].name = "reference";
    sigmatch_table[DETECT_REFERENCE].desc = "direct to places where information about the rule can be found";
    sigmatch_table[DETECT_REFERENCE].url = "/rules/meta.html#reference";
    sigmatch_table[DETECT_REFERENCE].Setup = DetectReferenceSetup;
#ifdef UNITTESTS
    sigmatch_table[DETECT_REFERENCE].RegisterTests = ReferenceRegisterTests;
#endif
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
}
static int DetectReferenceSetup(DetectEngineCtx *de_ctx, Signature *s,
                                const char *rawstr)
{
    SCEnter();

    DetectReference *sig_refs = NULL;

    DetectReference *ref = DetectReferenceParse(rawstr, de_ctx);
    if (ref == NULL)
        SCReturnInt(-1);

    SCLogDebug("ref %s %s", ref->key, ref->reference);

    if (s->references == NULL)  {
        s->references = ref;
    } else {
        sig_refs = s->references;
        while (sig_refs->next != NULL) {
            sig_refs = sig_refs->next;
        }
        sig_refs->next = ref;
        ref->next = NULL;
    }

    SCReturnInt(0);
}
2.7.1.7 签名优先级

priority关键字带有一个必填数字,范围从1255。具有较高优先级的签名将首先被检查。最高优先级为1。通常,签名已经具有通过类类型的优先级。可以用关键字priority来代替。

2.7.1.7.1 代码
void DetectPriorityRegister (void)
{
    sigmatch_table[DETECT_PRIORITY].name = "priority";
    sigmatch_table[DETECT_PRIORITY].desc = "rules with a higher priority will be examined first";
    sigmatch_table[DETECT_PRIORITY].url = "/rules/meta.html#priority";
    sigmatch_table[DETECT_PRIORITY].Setup = DetectPrioritySetup;
    sigmatch_table[DETECT_PRIORITY].RegisterTests = SCPriorityRegisterTests;

    DetectSetupParseRegexes(PARSE_REGEX, &regex, &regex_study);
}
static int DetectPrioritySetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
{
    char copy_str[128] = "";

#define MAX_SUBSTRINGS 30
    int ret = 0;
    int ov[MAX_SUBSTRINGS];

    ret = pcre_exec(regex, regex_study, rawstr, strlen(rawstr), 0, 0, ov, 30);
    if (ret < 0) {
        SCLogError(SC_ERR_PCRE_MATCH, "Invalid Priority in Signature "
                     "- %s", rawstr);
        return -1;
    }

    ret = pcre_copy_substring((char *)rawstr, ov, 30, 1, copy_str, sizeof(copy_str));
    if (ret < 0) {
        SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_copy_substring failed");
        return -1;
    }

    long prio = 0;
    char *endptr = NULL;
    prio = strtol(copy_str, &endptr, 10);
    if (endptr == NULL || *endptr != '\0') {
        SCLogError(SC_ERR_INVALID_SIGNATURE, "Saw an invalid character as arg "
                   "to priority keyword");
        return -1;
    }

    if (s->init_data->init_flags & SIG_FLAG_INIT_PRIO_EXPLICT) {
        SCLogWarning(SC_ERR_CONFLICTING_RULE_KEYWORDS, "duplicate priority "
                "keyword. Using highest priority in the rule");
        s->prio = MIN(s->prio, prio);
    } else {
        s->prio = prio;
        s->init_data->init_flags |= SIG_FLAG_INIT_PRIO_EXPLICT;
    }
    return 0;
}
  • priority关键字如果被设置,需要设置SIG_FLAG_INIT_PRIO_EXPLICT
2.7.1.8 签名元数据

metadata关键字允许将其他非功能性信息添加到签名中。格式为自由格式时,建议坚持使用键值对,因为Suricata可以在eve告警中包含这些值。格式为:

metadata: key value;
metadata: key value, key value;
2.7.1.8.1 代码
void DetectMetadataRegister (void)
{
    sigmatch_table[DETECT_METADATA].name = "metadata";
    sigmatch_table[DETECT_METADATA].desc = "used for logging";
    sigmatch_table[DETECT_METADATA].url = "/rules/meta.html#metadata";
    sigmatch_table[DETECT_METADATA].Match = NULL;
    sigmatch_table[DETECT_METADATA].Setup = DetectMetadataSetup;
    sigmatch_table[DETECT_METADATA].Free  = NULL;
    sigmatch_table[DETECT_METADATA].RegisterTests = DetectMetadataRegisterTests;
}
static int DetectMetadataSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
{
    if (DetectEngineMustParseMetadata()) {
        DetectMetadataParse(de_ctx, s, rawstr);
    }

    return 0;
}


static int DetectMetadataParse(DetectEngineCtx *de_ctx, Signature *s, const char *metadatastr)
{
...
    while (key != NULL) {
...
        DetectMetadata *dkv = SCMalloc(sizeof(DetectMetadata));
        if (dkv == NULL) {
            goto next;
        }
        dkv->key = hkey;
        dkv->value = hval;
        dkv->next = s->metadata;
        s->metadata = dkv;

    next:
        key = strtok_r(NULL, ",", &xsaveptr);
    }

    return 0;
}
2.7.1.9 签名目标

target关键字允许规则编写者指定告警地址的是攻击的目标。如果指定,警报事件将增强,以包含有关源和目标的信息。

target:[src_ip|dest_ip]

如果值为src_ip,则生成的事件(JSON中的src_ip字段)中的源IP是攻击的目标。如果目标设置为dest_ip,则目标是生成的事件中的目标IP。

2.7.1.9.1 代码
void DetectTargetRegister(void) {
    /* keyword name: this is how the keyword is used in a rule */
    sigmatch_table[DETECT_TARGET].name = "target";
    /* description: listed in "suricata --list-keywords=all" */
    sigmatch_table[DETECT_TARGET].desc = "indicate to output module which side is the target of the attack";
    /* link to further documentation of the keyword. Normally on the Suricata redmine/wiki */
    sigmatch_table[DETECT_TARGET].url =  "/rules/meta.html#target";
    /* match function is called when the signature is inspected on a packet */
    sigmatch_table[DETECT_TARGET].Match = NULL;
    /* setup function is called during signature parsing, when the target
     * keyword is encountered in the rule */
    sigmatch_table[DETECT_TARGET].Setup = DetectTargetSetup;
    /* free function is called when the detect engine is freed. Normally at
     * shutdown, but also during rule reloads. */
    sigmatch_table[DETECT_TARGET].Free = NULL;
    /* registers unittests into the system */
    sigmatch_table[DETECT_TARGET].RegisterTests = DetectTargetRegisterTests;

    /* set up the PCRE for keyword parsing */
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
}
static int DetectTargetSetup(DetectEngineCtx *de_ctx, Signature *s, const char *targetstr)
{
    int ret = DetectTargetParse(s, targetstr);
...
}

static int DetectTargetParse(Signature *s, const char *targetstr)
{
...
    if (!strcmp(value, "src_ip")) {
...
        s->flags |= SIG_FLAG_SRC_IS_TARGET;
    } else if (!strcmp(value, "dest_ip")) {
...
        s->flags |= SIG_FLAG_DEST_IS_TARGET;
    }
...
}

2.8 SignatureInitData

签名保存最初的配置,SigMatchPrepare处释放。

typedef struct SignatureInitData_ {
    /** Number of sigmatches. Used for assigning SigMatch::idx */
    uint16_t sm_cnt;

    /** option was prefixed with '!'. Only set for sigmatches that
     *  have the SIGMATCH_HANDLE_NEGATION flag set. */
    bool negated;

    /* track if we saw any negation in the addresses. If so, we
     * skip it for ip-only */
    bool src_contains_negation;
    bool dst_contains_negation;

    /* used to hold flags that are used during init */
    uint32_t init_flags;
    /* coccinelle: SignatureInitData:init_flags:SIG_FLAG_INIT_ */

    /* used at init to determine max dsize */
    SigMatch *dsize_sm;

    /* the fast pattern added from this signature */
    SigMatch *mpm_sm;
    /* used to speed up init of prefilter */
    SigMatch *prefilter_sm;

    /* SigMatch list used for adding content and friends. E.g. file_data; */
    int list;
    bool list_set;

    int transforms[DETECT_TRANSFORMS_MAX];
    int transform_cnt;

    /** score to influence rule grouping. A higher value leads to a higher
     *  likelyhood of a rulegroup with this sig ending up as a contained
     *  group. */
    int whitelist;

    /** address settings for this signature */
    const DetectAddressHead *src, *dst;

    int prefilter_list;

    uint32_t smlists_array_size;
    /* holds all sm lists */
    struct SigMatch_ **smlists;
    /* holds all sm lists' tails */
    struct SigMatch_ **smlists_tail;
} SignatureInitData;
;