allow to use lists of md5s to block connections

Besides domains, net ranges and IPs, now it's possible to
filter connections by the MD5 of a binary, if it's enabled.
This commit is contained in:
Gustavo Iñiguez Goia 2025-01-24 00:47:02 +01:00
parent 5184c45086
commit ced8410d43
Failed to generate hash of commit
2 changed files with 76 additions and 47 deletions

View file

@ -15,6 +15,7 @@ import (
"github.com/evilsocket/opensnitch/daemon/conman" "github.com/evilsocket/opensnitch/daemon/conman"
"github.com/evilsocket/opensnitch/daemon/core" "github.com/evilsocket/opensnitch/daemon/core"
"github.com/evilsocket/opensnitch/daemon/log" "github.com/evilsocket/opensnitch/daemon/log"
"github.com/evilsocket/opensnitch/daemon/procmon"
) )
// Type is the type of rule. // Type is the type of rule.
@ -64,8 +65,9 @@ const (
OpDomainsRegexpLists = Operand("lists.domains_regexp") OpDomainsRegexpLists = Operand("lists.domains_regexp")
OpIPLists = Operand("lists.ips") OpIPLists = Operand("lists.ips")
OpNetLists = Operand("lists.nets") OpNetLists = Operand("lists.nets")
OpHashMD5Lists = Operand("lists.hash.md5")
// TODO // TODO
//OpHashMD5Lists = Operand("lists.hash.md5")
//OpQuota = Operand("quota") //OpQuota = Operand("quota")
//OpQuotaTxOver = Operand("quota.sent.over") // 1000b, 1kb, 1mb, 1gb, ... //OpQuotaTxOver = Operand("quota.sent.over") // 1000b, 1kb, 1mb, 1gb, ...
//OpQuotaRxOver = Operand("quota.recv.over") // 1000b, 1kb, 1mb, 1gb, ... //OpQuotaRxOver = Operand("quota.recv.over") // 1000b, 1kb, 1mb, 1gb, ...
@ -225,7 +227,7 @@ func (o *Operator) Compile() error {
return fmt.Errorf("Operand lists is empty, nothing to load: %s", o) return fmt.Errorf("Operand lists is empty, nothing to load: %s", o)
} }
o.loadLists() o.loadLists()
o.cb = o.domainsListCmp o.cb = o.domainsListsCmp
} else if o.Operand == OpDomainsRegexpLists { } else if o.Operand == OpDomainsRegexpLists {
if o.Data == "" { if o.Data == "" {
return fmt.Errorf("Operand regexp lists is empty, nothing to load: %s", o) return fmt.Errorf("Operand regexp lists is empty, nothing to load: %s", o)
@ -237,13 +239,19 @@ func (o *Operator) Compile() error {
return fmt.Errorf("Operand ip lists is empty, nothing to load: %s", o) return fmt.Errorf("Operand ip lists is empty, nothing to load: %s", o)
} }
o.loadLists() o.loadLists()
o.cb = o.ipListCmp o.cb = o.simpleListsCmp
} else if o.Operand == OpNetLists { } else if o.Operand == OpNetLists {
if o.Data == "" { if o.Data == "" {
return fmt.Errorf("Operand net lists is empty, nothing to load: %s", o) return fmt.Errorf("Operand net lists is empty, nothing to load: %s", o)
} }
o.loadLists() o.loadLists()
o.cb = o.ipNetCmp o.cb = o.ipNetCmp
} else if o.Operand == OpHashMD5Lists {
if o.Data == "" {
return fmt.Errorf("Operand lists.hash.md5 is empty, nothing to load: %s", o)
}
o.loadLists()
o.cb = o.simpleListsCmp
} else if o.Operand == OpProcessHashMD5 || o.Operand == OpProcessHashSHA1 { } else if o.Operand == OpProcessHashMD5 || o.Operand == OpProcessHashSHA1 {
o.cb = o.hashCmp o.cb = o.hashCmp
} }
@ -288,7 +296,15 @@ func (o *Operator) cmpNetwork(destIP interface{}) bool {
return o.netMask.Contains(destIP.(net.IP)) return o.netMask.Contains(destIP.(net.IP))
} }
func (o *Operator) domainsListCmp(v interface{}) bool { func (o *Operator) matchListsCmp(msg, what string) bool {
if item, found := o.lists[what]; found {
log.Debug("%s: %s, %s", log.Red(msg), what, item)
return true
}
return false
}
func (o *Operator) domainsListsCmp(v interface{}) bool {
dstHost := v.(string) dstHost := v.(string)
if dstHost == "" { if dstHost == "" {
return false return false
@ -299,26 +315,18 @@ func (o *Operator) domainsListCmp(v interface{}) bool {
o.RLock() o.RLock()
defer o.RUnlock() defer o.RUnlock()
if _, found := o.lists[dstHost]; found { return o.matchListsCmp("domains list match", dstHost)
log.Debug("%s: %s, %s", log.Red("domain list match"), dstHost, o.lists[dstHost])
return true
}
return false
} }
func (o *Operator) ipListCmp(v interface{}) bool { func (o *Operator) simpleListsCmp(v interface{}) bool {
dstIP := v.(string) what := v.(string)
if dstIP == "" { if what == "" {
return false return false
} }
o.RLock() o.RLock()
defer o.RUnlock() defer o.RUnlock()
if _, found := o.lists[dstIP]; found { return o.matchListsCmp("simple list match", what)
log.Debug("%s: %s, %s", log.Red("IP list match"), dstIP, o.lists[dstIP].(string))
return true
}
return false
} }
func (o *Operator) ipNetCmp(dstIP interface{}) bool { func (o *Operator) ipNetCmp(dstIP interface{}) bool {
@ -393,6 +401,8 @@ func (o *Operator) Match(con *conman.Connection, hasChecksums bool) bool {
return o.cb(con.DstHost) return o.cb(con.DstHost)
} else if o.Operand == OpIPLists { } else if o.Operand == OpIPLists {
return o.cb(con.DstIP.String()) return o.cb(con.DstIP.String())
} else if o.Operand == OpHashMD5Lists {
return o.cb(con.Process.Checksums[procmon.HashMD5])
} else if o.Operand == OpUserID || o.Operand == OpUserName { } else if o.Operand == OpUserID || o.Operand == OpUserName {
return o.cb(strconv.Itoa(con.Entry.UserId)) return o.cb(strconv.Itoa(con.Entry.UserId))
} else if o.Operand == OpDstNetwork { } else if o.Operand == OpDstNetwork {

View file

@ -104,32 +104,46 @@ func (o *Operator) StopMonitoringLists() {
} }
} }
func (o *Operator) readDomainsList(raw, fileName string) (dups uint64) { func filterDomains(line, defValue string) (bool, string, string) {
log.Debug("Loading domains list: %s, size: %d", fileName, len(raw)) if len(line) < 9 {
lines := strings.Split(string(raw), "\n") return true, line, defValue
for _, domain := range lines { }
if len(domain) < 9 { // exclude not valid lines
continue if line[:7] != "0.0.0.0" && line[:9] != "127.0.0.1" {
} return true, line, defValue
// exclude not valid lines }
if domain[:7] != "0.0.0.0" && domain[:9] != "127.0.0.1" { host := line[8:]
continue // exclude localhost entries
} if line[:9] == "127.0.0.1" {
host := domain[8:] host = line[10:]
// exclude localhost entries }
if domain[:9] == "127.0.0.1" { if host == "local" || host == "localhost" || host == "localhost.localdomain" || host == "broadcasthost" {
host = domain[10:] return true, line, defValue
} }
if host == "local" || host == "localhost" || host == "localhost.localdomain" || host == "broadcasthost" {
continue
}
host = core.Trim(host) return false, host, defValue
if _, found := o.lists[host]; found { }
func filterSimple(line, hashPath string) (bool, string, string) {
// XXX: some lists may use TABs as separator
hash := strings.SplitN(line, " ", 2)
return false, hash[0], hash[1]
}
func (o *Operator) readTupleList(raw, fileName string, filter func(line, defValue string) (bool, string, string)) (dups uint64) {
log.Debug("Loading list: %s, size: %d", fileName, len(raw))
lines := strings.Split(string(raw), "\n")
for _, line := range lines {
skip, key, value := filter(line, fileName)
if skip || len(line) < 9 {
continue
}
key = core.Trim(key)
if _, found := o.lists[key]; found {
dups++ dups++
continue continue
} }
o.lists[host] = fileName o.lists[key] = value
} }
lines = nil lines = nil
log.Info("%d domains loaded, %s", len(o.lists), fileName) log.Info("%d domains loaded, %s", len(o.lists), fileName)
@ -187,22 +201,25 @@ func (o *Operator) readRegexpList(raw, fileName string) (dups uint64) {
return dups return dups
} }
func (o *Operator) readIPList(raw, fileName string) (dups uint64) { // A simple list is a list composed of one column with several entries, that
log.Debug("Loading IPs list: %s, size: %d", fileName, len(raw)) // don't require manipulation.
// It can be a list of IPs, domains, etc.
func (o *Operator) readSimpleList(raw, fileName string) (dups uint64) {
log.Debug("Loading simple list: %s, size: %d", fileName, len(raw))
lines := strings.Split(string(raw), "\n") lines := strings.Split(string(raw), "\n")
for _, line := range lines { for _, line := range lines {
if line == "" || line[0] == '#' { if line == "" || line[0] == '#' {
continue continue
} }
ip := core.Trim(line) what := core.Trim(line)
if _, found := o.lists[ip]; found { if _, found := o.lists[what]; found {
dups++ dups++
continue continue
} }
o.lists[ip] = fileName o.lists[what] = fileName
} }
lines = nil lines = nil
log.Info("%d IPs loaded, %s", len(o.lists), fileName) log.Info("%d entries loaded, %s", len(o.lists), fileName)
return dups return dups
} }
@ -236,13 +253,15 @@ func (o *Operator) readLists() error {
} }
if o.Operand == OpDomainsLists { if o.Operand == OpDomainsLists {
dups += o.readDomainsList(string(raw), fileName) dups += o.readTupleList(string(raw), fileName, filterDomains)
} else if o.Operand == OpDomainsRegexpLists { } else if o.Operand == OpDomainsRegexpLists {
dups += o.readRegexpList(string(raw), fileName) dups += o.readRegexpList(string(raw), fileName)
} else if o.Operand == OpNetLists { } else if o.Operand == OpNetLists {
dups += o.readNetList(string(raw), fileName) dups += o.readNetList(string(raw), fileName)
} else if o.Operand == OpIPLists { } else if o.Operand == OpIPLists {
dups += o.readIPList(string(raw), fileName) dups += o.readSimpleList(string(raw), fileName)
} else if o.Operand == OpHashMD5Lists {
dups += o.readSimpleList(string(raw), fileName)
} else { } else {
log.Warning("Unknown lists operand type: %s", o.Operand) log.Warning("Unknown lists operand type: %s", o.Operand)
} }