2020-05-15 09:39:16 +00:00
|
|
|
package types
|
|
|
|
|
|
|
|
import (
|
2023-07-18 16:12:17 +00:00
|
|
|
"fmt"
|
2023-07-13 14:20:04 +00:00
|
|
|
"regexp"
|
2023-07-24 12:50:08 +00:00
|
|
|
"strings"
|
2020-05-15 09:39:16 +00:00
|
|
|
"time"
|
2020-11-30 09:37:17 +00:00
|
|
|
|
2020-12-02 16:15:48 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
2020-11-30 09:37:17 +00:00
|
|
|
"github.com/antonmedv/expr/vm"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
2020-05-15 09:39:16 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
LOG = iota
|
|
|
|
OVFLW
|
|
|
|
)
|
|
|
|
|
2023-07-13 14:20:04 +00:00
|
|
|
/*
|
|
|
|
1. If user triggered a rule that is for a CVE, that has high confidence and that is blocking, ban
|
|
|
|
2. If user triggered 3 distinct rules with medium confidence accross 3 different requests, ban
|
|
|
|
|
|
|
|
|
|
|
|
any(evt.Waf.ByTag("CVE"), {.confidence == "high" && .action == "block"})
|
|
|
|
|
|
|
|
len(evt.Waf.ByTagRx("*CVE*").ByConfidence("high").ByAction("block")) > 1
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
type MatchedRules []map[string]interface{}
|
2023-07-13 14:20:04 +00:00
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
type WaapEvent struct {
|
|
|
|
MatchedRules
|
|
|
|
Vars map[string]string
|
|
|
|
}
|
2023-07-18 16:12:17 +00:00
|
|
|
type Field string
|
|
|
|
|
|
|
|
func (f Field) String() string {
|
|
|
|
return fmt.Sprintf("%s", f)
|
|
|
|
}
|
2023-07-13 14:20:04 +00:00
|
|
|
|
2023-07-18 16:12:17 +00:00
|
|
|
const (
|
|
|
|
ID Field = "id"
|
|
|
|
RuleType Field = "rule_type"
|
|
|
|
Tags Field = "tags"
|
|
|
|
File Field = "file"
|
|
|
|
Confidence Field = "confidence"
|
|
|
|
Revision Field = "revision"
|
|
|
|
SecMark Field = "secmark"
|
|
|
|
Accuracy Field = "accuracy"
|
|
|
|
Msg Field = "msg"
|
|
|
|
Severity Field = "severity"
|
|
|
|
Kind Field = "kind"
|
|
|
|
)
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w WaapEvent) GetVar(varName string) string {
|
|
|
|
if w.Vars == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
parsed := strings.Split(varName, ".")
|
|
|
|
if len(parsed) == 1 {
|
|
|
|
//no subkey
|
|
|
|
return w.Vars[varName]
|
|
|
|
} else if len(parsed) == 2 {
|
|
|
|
//subkey
|
|
|
|
if w.Vars[parsed[0]] == "" {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return w.Vars[parsed[0]][parsed[1]]
|
|
|
|
}
|
|
|
|
log.Warningf("invalid variable name %s", varName)
|
|
|
|
return ""
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-07-18 16:12:17 +00:00
|
|
|
// getters
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) GetField(field Field) []interface{} {
|
2023-07-18 16:12:17 +00:00
|
|
|
ret := make([]interface{}, 0)
|
2023-07-13 14:20:04 +00:00
|
|
|
for _, rule := range w {
|
2023-07-18 16:12:17 +00:00
|
|
|
ret = append(ret, rule[field.String()])
|
2023-07-13 14:20:04 +00:00
|
|
|
}
|
2023-07-18 16:12:17 +00:00
|
|
|
return ret
|
2023-07-13 14:20:04 +00:00
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) GetURI() string {
|
2023-07-13 14:20:04 +00:00
|
|
|
for _, rule := range w {
|
|
|
|
return rule["uri"].(string)
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) GetMethod() string {
|
2023-07-13 14:20:04 +00:00
|
|
|
for _, rule := range w {
|
|
|
|
return rule["method"].(string)
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) GetRuleIDs() []int {
|
2023-07-13 14:20:04 +00:00
|
|
|
ret := make([]int, 0)
|
|
|
|
for _, rule := range w {
|
|
|
|
ret = append(ret, rule["id"].(int))
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) Kinds() []string {
|
2023-07-13 14:20:04 +00:00
|
|
|
ret := make([]string, 0)
|
|
|
|
for _, rule := range w {
|
|
|
|
exists := false
|
|
|
|
for _, val := range ret {
|
|
|
|
if val == rule["kind"] {
|
|
|
|
exists = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !exists {
|
|
|
|
ret = append(ret, rule["kind"].(string))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2023-07-18 16:12:17 +00:00
|
|
|
// filters
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) ByID(id int) MatchedRules {
|
|
|
|
waap := MatchedRules{}
|
2023-07-18 16:12:17 +00:00
|
|
|
|
|
|
|
for _, rule := range w {
|
|
|
|
if rule["id"] == id {
|
|
|
|
waap = append(waap, rule)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return waap
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) ByKind(kind string) MatchedRules {
|
|
|
|
waap := MatchedRules{}
|
2023-07-18 16:12:17 +00:00
|
|
|
for _, rule := range w {
|
|
|
|
if rule["kind"] == kind {
|
|
|
|
waap = append(waap, rule)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return waap
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) ByTags(match []string) MatchedRules {
|
|
|
|
waap := MatchedRules{}
|
2023-07-18 16:12:17 +00:00
|
|
|
for _, rule := range w {
|
|
|
|
for _, tag := range rule["tags"].([]string) {
|
|
|
|
for _, match_tag := range match {
|
|
|
|
if tag == match_tag {
|
|
|
|
waap = append(waap, rule)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return waap
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) ByTag(match string) MatchedRules {
|
|
|
|
waap := MatchedRules{}
|
2023-07-13 14:20:04 +00:00
|
|
|
for _, rule := range w {
|
|
|
|
for _, tag := range rule["tags"].([]string) {
|
|
|
|
if tag == match {
|
|
|
|
waap = append(waap, rule)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return waap
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) ByTagRx(rx string) MatchedRules {
|
|
|
|
waap := MatchedRules{}
|
2023-07-13 14:20:04 +00:00
|
|
|
re := regexp.MustCompile(rx)
|
|
|
|
if re == nil {
|
|
|
|
return waap
|
|
|
|
}
|
|
|
|
for _, rule := range w {
|
|
|
|
for _, tag := range rule["tags"].([]string) {
|
2023-07-20 15:10:01 +00:00
|
|
|
log.Infof("ByTagRx: %s = %s -> %t", rx, tag, re.MatchString(tag))
|
2023-07-13 14:20:04 +00:00
|
|
|
if re.MatchString(tag) {
|
|
|
|
waap = append(waap, rule)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return waap
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) ByDisruptiveness(is bool) MatchedRules {
|
2023-07-13 14:20:04 +00:00
|
|
|
log.Infof("%s", w)
|
2023-07-24 12:50:08 +00:00
|
|
|
wap := MatchedRules{}
|
2023-07-13 14:20:04 +00:00
|
|
|
for _, rule := range w {
|
|
|
|
if rule["disruptive"] == is {
|
|
|
|
wap = append(wap, rule)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log.Infof("ByDisruptiveness(%t) -> %d", is, len(wap))
|
|
|
|
|
|
|
|
return wap
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) BySeverity(severity string) MatchedRules {
|
|
|
|
wap := MatchedRules{}
|
2023-07-13 14:20:04 +00:00
|
|
|
for _, rule := range w {
|
|
|
|
if rule["severity"] == severity {
|
|
|
|
wap = append(wap, rule)
|
|
|
|
}
|
|
|
|
}
|
2023-07-18 16:12:17 +00:00
|
|
|
log.Infof("BySeverity(%s) -> %d", severity, len(wap))
|
|
|
|
return wap
|
|
|
|
}
|
|
|
|
|
2023-07-24 12:50:08 +00:00
|
|
|
func (w MatchedRules) ByAccuracy(accuracy string) MatchedRules {
|
|
|
|
wap := MatchedRules{}
|
2023-07-18 16:12:17 +00:00
|
|
|
for _, rule := range w {
|
|
|
|
if rule["accuracy"] == accuracy {
|
|
|
|
wap = append(wap, rule)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log.Infof("ByAccuracy(%s) -> %d", accuracy, len(wap))
|
2023-07-13 14:20:04 +00:00
|
|
|
return wap
|
|
|
|
}
|
|
|
|
|
2023-03-16 15:25:50 +00:00
|
|
|
// Event is the structure representing a runtime event (log or overflow)
|
2020-05-15 09:39:16 +00:00
|
|
|
type Event struct {
|
|
|
|
/* is it a log or an overflow */
|
2020-11-30 09:37:17 +00:00
|
|
|
Type int `yaml:"Type,omitempty" json:"Type,omitempty"` //Can be types.LOG (0) or types.OVFLOW (1)
|
2023-03-16 15:25:50 +00:00
|
|
|
ExpectMode int `yaml:"ExpectMode,omitempty" json:"ExpectMode,omitempty"` //how to buckets should handle event : types.TIMEMACHINE or types.LIVE
|
2020-11-30 09:37:17 +00:00
|
|
|
Whitelisted bool `yaml:"Whitelisted,omitempty" json:"Whitelisted,omitempty"`
|
2021-10-04 15:14:52 +00:00
|
|
|
WhitelistReason string `yaml:"WhitelistReason,omitempty" json:"whitelist_reason,omitempty"`
|
2020-05-15 09:39:16 +00:00
|
|
|
//should add whitelist reason ?
|
|
|
|
/* the current stage of the line being parsed */
|
2020-11-30 09:37:17 +00:00
|
|
|
Stage string `yaml:"Stage,omitempty" json:"Stage,omitempty"`
|
2020-05-15 09:39:16 +00:00
|
|
|
/* original line (produced by acquisition) */
|
2020-11-30 09:37:17 +00:00
|
|
|
Line Line `yaml:"Line,omitempty" json:"Line,omitempty"`
|
2020-05-15 09:39:16 +00:00
|
|
|
/* output of groks */
|
2020-11-30 09:37:17 +00:00
|
|
|
Parsed map[string]string `yaml:"Parsed,omitempty" json:"Parsed,omitempty"`
|
2020-05-15 09:39:16 +00:00
|
|
|
/* output of enrichment */
|
2020-11-30 09:37:17 +00:00
|
|
|
Enriched map[string]string `yaml:"Enriched,omitempty" json:"Enriched,omitempty"`
|
2022-12-06 12:47:29 +00:00
|
|
|
/* output of Unmarshal */
|
|
|
|
Unmarshaled map[string]interface{} `yaml:"Unmarshaled,omitempty" json:"Unmarshaled,omitempty"`
|
2020-05-15 09:39:16 +00:00
|
|
|
/* Overflow */
|
2021-10-04 15:14:52 +00:00
|
|
|
Overflow RuntimeAlert `yaml:"Overflow,omitempty" json:"Alert,omitempty"`
|
2020-11-30 09:37:17 +00:00
|
|
|
Time time.Time `yaml:"Time,omitempty" json:"Time,omitempty"` //parsed time `json:"-"` ``
|
|
|
|
StrTime string `yaml:"StrTime,omitempty" json:"StrTime,omitempty"`
|
2022-07-28 14:41:41 +00:00
|
|
|
StrTimeFormat string `yaml:"StrTimeFormat,omitempty" json:"StrTimeFormat,omitempty"`
|
2020-11-30 09:37:17 +00:00
|
|
|
MarshaledTime string `yaml:"MarshaledTime,omitempty" json:"MarshaledTime,omitempty"`
|
|
|
|
Process bool `yaml:"Process,omitempty" json:"Process,omitempty"` //can be set to false to avoid processing line
|
2023-07-13 14:20:04 +00:00
|
|
|
Waap WaapEvent `yaml:"Waap,omitempty" json:"Waap,omitempty"`
|
2020-05-15 09:39:16 +00:00
|
|
|
/* Meta is the only part that will make it to the API - it should be normalized */
|
2020-11-30 09:37:17 +00:00
|
|
|
Meta map[string]string `yaml:"Meta,omitempty" json:"Meta,omitempty"`
|
|
|
|
}
|
|
|
|
|
2020-12-02 16:15:48 +00:00
|
|
|
func (e *Event) GetType() string {
|
|
|
|
if e.Type == OVFLW {
|
|
|
|
return "overflow"
|
|
|
|
} else if e.Type == LOG {
|
|
|
|
return "log"
|
|
|
|
} else {
|
|
|
|
log.Warningf("unknown event type for %+v", e)
|
|
|
|
return "unknown"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-07 08:11:39 +00:00
|
|
|
func (e *Event) GetMeta(key string) string {
|
|
|
|
if e.Type == OVFLW {
|
|
|
|
for _, alert := range e.Overflow.APIAlerts {
|
|
|
|
for _, event := range alert.Events {
|
|
|
|
if event.GetMeta(key) != "" {
|
|
|
|
return event.GetMeta(key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if e.Type == LOG {
|
|
|
|
for k, v := range e.Meta {
|
|
|
|
if k == key {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2023-03-16 15:25:50 +00:00
|
|
|
// Move in leakybuckets
|
2020-11-30 09:37:17 +00:00
|
|
|
const (
|
|
|
|
Undefined = ""
|
|
|
|
Ip = "Ip"
|
|
|
|
Range = "Range"
|
|
|
|
Filter = "Filter"
|
2021-05-31 13:07:09 +00:00
|
|
|
Country = "Country"
|
|
|
|
AS = "AS"
|
2020-11-30 09:37:17 +00:00
|
|
|
)
|
|
|
|
|
2023-03-16 15:25:50 +00:00
|
|
|
// Move in leakybuckets
|
2020-11-30 09:37:17 +00:00
|
|
|
type ScopeType struct {
|
|
|
|
Scope string `yaml:"type"`
|
|
|
|
Filter string `yaml:"expression"`
|
|
|
|
RunTimeFilter *vm.Program
|
|
|
|
}
|
|
|
|
|
|
|
|
type RuntimeAlert struct {
|
|
|
|
Mapkey string `yaml:"MapKey,omitempty" json:"MapKey,omitempty"`
|
|
|
|
BucketId string `yaml:"BucketId,omitempty" json:"BucketId,omitempty"`
|
|
|
|
Whitelisted bool `yaml:"Whitelisted,omitempty" json:"Whitelisted,omitempty"`
|
|
|
|
Reprocess bool `yaml:"Reprocess,omitempty" json:"Reprocess,omitempty"`
|
|
|
|
Sources map[string]models.Source `yaml:"Sources,omitempty" json:"Sources,omitempty"`
|
|
|
|
Alert *models.Alert `yaml:"Alert,omitempty" json:"Alert,omitempty"` //this one is a pointer to APIAlerts[0] for convenience.
|
|
|
|
//APIAlerts will be populated at the end when there is more than one source
|
|
|
|
APIAlerts []models.Alert `yaml:"APIAlerts,omitempty" json:"APIAlerts,omitempty"`
|
2020-05-15 09:39:16 +00:00
|
|
|
}
|
2021-10-04 15:14:52 +00:00
|
|
|
|
|
|
|
func (r RuntimeAlert) GetSources() []string {
|
|
|
|
ret := make([]string, 0)
|
2022-02-01 21:08:06 +00:00
|
|
|
for key := range r.Sources {
|
2021-10-04 15:14:52 +00:00
|
|
|
ret = append(ret, key)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|