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_ANY
、SIG_FLAG_DST_ANY
、SIG_FLAG_SP_ANY
、SIG_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_PACKET
和SIG_FLAG_REQUIRE_STREAM
代表检测仅基于packet
或stream
。
#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
代表仅flowbit
、flowvar
、flowint
生效时检测有效。
#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_TOSERVER
和SIG_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_TARGET
和SIG_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_low
和dsize_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
签名优先级。 -
sp
和dp
签名端口号。 -
CidrSrc
和CidrDst
以CIDR格式在sid处指定的netblocks
和hosts
-
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
。
规则将按照它们在文件中出现的顺序进行加载。但是它们将以不同的顺序进行处理。签名具有不同的优先级。最重要的签名将首先被扫描。可以更改优先级顺序。默认顺序为:pass
,drop
,reject
,alert
。
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 签名协议
签名中关于协议的字段。
可以在四种基本协议和应用层协议之间进行选择:
tcp
、udp
、icmp
、ip
http
、ftp
、tls
、smb
等
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_NET | Yaml中设置的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默认gid
是1
。
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, ®ex, ®ex_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
关键字带有一个必填数字,范围从1
到255
。具有较高优先级的签名将首先被检查。最高优先级为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, ®ex, ®ex_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;