Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,11 +470,14 @@ func (r *Rule) protocol(key item, l *lexer) error {

// network decodes an IDS rule network (networks and ports) based on its key.
func (r *Rule) network(key item, l *lexer) error {
// Identify if the whole network component is negated.
tmp := strings.TrimPrefix(key.value, "!")
negated := len(tmp) < len(key.value)

// This is a hack. We use a regexp to replace the outer `,` with `___`
// to give us a discrete string to split on, avoiding the inner `,`.

// Specify TrimSuffix and TrimPrefix to ensure only one instance of `[` and `]` are trimmed.
tmp := strings.TrimSuffix(strings.TrimPrefix(key.value, "["), "]")
tmp = strings.TrimSuffix(strings.TrimPrefix(tmp, "["), "]")
items := strings.Split(nestedNetRE.ReplaceAllString(tmp, "___${1}"), "___")

// Validate that no items contain spaces.
Expand All @@ -485,24 +488,28 @@ func (r *Rule) network(key item, l *lexer) error {
}
switch key.typ {
case itemSourceAddress:
r.Source.NegateNets = negated
if validNetworks(items) {
r.Source.Nets = append(r.Source.Nets, items...)
} else {
return fmt.Errorf("some or all source ips are invalid: %v", items)
}
case itemSourcePort:
r.Source.NegatePorts = negated
if portsValid(items) {
r.Source.Ports = append(r.Source.Ports, items...)
} else {
return fmt.Errorf("some or all source ports are invalid: %v", items)
}
case itemDestinationAddress:
r.Destination.NegateNets = negated
if validNetworks(items) {
r.Destination.Nets = append(r.Destination.Nets, items...)
} else {
return fmt.Errorf("some or all destination ips are invalid: %v", items)
}
case itemDestinationPort:
r.Destination.NegatePorts = negated
if portsValid(items) {
r.Destination.Ports = append(r.Destination.Ports, items...)
} else {
Expand Down
50 changes: 50 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1980,6 +1980,56 @@ func TestParseRule(t *testing.T) {
},
},
},
{
name: "negated port list",
rule: `alert tcp any any -> any ![80,443,9000] (msg:"negated port list"; content:"123"; sid:1; rev:1;)`,
want: &Rule{
Action: "alert",
Protocol: "tcp",
Source: Network{
Nets: []string{"any"},
Ports: []string{"any"},
},
Destination: Network{
Nets: []string{"any"},
NegatePorts: true,
Ports: []string{"80,443,9000"},
},
Description: "negated port list",
Matchers: []orderedMatcher{
&Content{
Pattern: []byte("123"),
},
},
SID: 1,
Revision: 1,
},
},
{
name: "negated network list",
rule: `alert tcp ![$HOME_NET,192.168.1.1] any -> any $HTTP_PORTS (msg:"negated port list"; content:"123"; sid:1; rev:1;)`,
want: &Rule{
Action: "alert",
Protocol: "tcp",
Source: Network{
NegateNets: true,
Nets: []string{"$HOME_NET,192.168.1.1"},
Ports: []string{"any"},
},
Destination: Network{
Nets: []string{"any"},
Ports: []string{"$HTTP_PORTS"},
},
Description: "negated port list",
Matchers: []orderedMatcher{
&Content{
Pattern: []byte("123"),
},
},
SID: 1,
Revision: 1,
},
},
{
name: "raw network list",
rule: `alert tcp [174.129.0.0/16,67.202.0.0/18] any -> $HOME_NET any (msg:"raw network list"; content:"hi"; sid:12345; rev:1;)`,
Expand Down
21 changes: 17 additions & 4 deletions rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ type Metadatas []*Metadata

// Network describes the IP addresses and port numbers used in a rule.
// TODO: Ensure all values either begin with $ (variable) or they are valid IPNet/int.
// TODO: Refactor Nets and Ports into structs, each with their own bool for negation.
type Network struct {
Nets []string // Currently just []string because these can be variables $HOME_NET, not a valid IPNet.
Ports []string // Currently just []string because these can be variables $HTTP_PORTS, not just ints.
NegateNets bool // Negate the full set of Networks.
Nets []string // Currently just []string because these can be variables $HOME_NET, not a valid IPNet.
NegatePorts bool // Negate the full set of ports.
Ports []string // Currently just []string because these can be variables $HTTP_PORTS, not just ints.
}

// DataPos indicates the data position for content matches. These should be referenced for creation
Expand Down Expand Up @@ -664,9 +667,19 @@ func netString(netPart []string) string {
return s.String()
}

// String retunrs a string for a Network.
// String returns a string for a Network.
func (n Network) String() string {
return fmt.Sprintf("%s %s", netString(n.Nets), netString(n.Ports))
var s strings.Builder
if n.NegateNets {
s.WriteString("!")
}
s.WriteString(netString(n.Nets))
s.WriteString(" ")
if n.NegatePorts {
s.WriteString("!")
}
s.WriteString(netString(n.Ports))
return s.String()
}

// String returns a string for a FastPattern.
Expand Down
19 changes: 14 additions & 5 deletions rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,9 @@ func TestNetworkString(t *testing.T) {
name: "complex net",
input: Network{
Nets: []string{"$HOME_NET", "!$FOO_NET", "192.168.0.0/16"},
Ports: []string{"$HTTP_PORTS", "!53", "$BAR_NET"},
Ports: []string{"$HTTP_PORTS", "!53", "$BAR_PORTS"},
},
want: "[$HOME_NET,!$FOO_NET,192.168.0.0/16] [$HTTP_PORTS,!53,$BAR_NET]",
want: "[$HOME_NET,!$FOO_NET,192.168.0.0/16] [$HTTP_PORTS,!53,$BAR_PORTS]",
},
{
name: "grouped ports",
Expand All @@ -358,10 +358,19 @@ func TestNetworkString(t *testing.T) {
{
name: "grouped networks",
input: Network{
Nets: []string{"192.168.0.0/16", "![192.168.86.0/24,192.168.87.0/24]"},
Ports: []string{"$HTTP_PORTS", "!53", "$BAR_NET"},
Nets: []string{"192.168.0.0/16", "[192.168.86.0/24,192.168.87.0/24]"},
Ports: []string{"$HTTP_PORTS", "!53", "$BAR_PORTS"},
},
want: "[192.168.0.0/16,![192.168.86.0/24,192.168.87.0/24]] [$HTTP_PORTS,!53,$BAR_NET]",
want: "[192.168.0.0/16,[192.168.86.0/24,192.168.87.0/24]] [$HTTP_PORTS,!53,$BAR_PORTS]",
},
{
name: "negated networks",
input: Network{
NegateNets: true,
Nets: []string{"192.168.0.0/16", "[192.168.86.0/24,192.168.87.0/24]"},
Ports: []string{"$HTTP_PORTS", "!53", "$BAR_PORTS"},
},
want: "![192.168.0.0/16,[192.168.86.0/24,192.168.87.0/24]] [$HTTP_PORTS,!53,$BAR_PORTS]",
},
} {
got := tt.input.String()
Expand Down
Loading