BitBlazr: Blocking Rules

Those familiar with eBPF will know that it comes with several restrictions, such as limited looping. eBPF programs have restrictions on looping to prevent infinite loops that could potentially hang the kernel. Loops must be bounded, meaning they must have a predetermined number of iterations.
These restrictions pose challenges for kernel-level rule engines, as security rule logic often relies on heavy looping for tasks such as matching patterns within strings.
Creating this rule engine was challenging, to say the least, and it comes with more restrictions than one may be accustomed to in a typical security engine, however, since it is at the kernel level it is very fast and all decisions are made upon kernel system call execution.

Rule Engine

The rule engine is based on YAML. YAML addresses drawbacks found in regular JSON and allows for features like comments. Example rules can be found in the config/blocking_rules.yaml file.  

- class: file
  event: exec
  action: allow
  rules:
    path:
    - starts_with: /usr/bin
    - starts_with: /usr/sbin

Example Rule

The rule will match on execution of files with a path starting with "/usr/bin" and "/usr/sbin". The action of "allow" here, will result in a log entry, and no blocking will be applied.

Each event type supports different matches, as seen above "exec" event supports a "path" match which cannot be used in socket-related events.

Here are the descriptions of supported root-level fields:
class - event class to which the rule will be applied
event - event type, the type depends on a specific class
context - an array of labels that can be defined in config.yaml file. If t his variable is set, the rule will match only on children of a process associated with a specific label.
rules - this is where desired matches are defined. It is an object where matches supported by an event are keys, and matching logic is defined in an array.
action - rule action

The "rules" object acts as a "and" match, all of the target matches need to fire for the rule to match. As the statement array under each target acts as "or" match, only one of the statements in the array needs to match for the target match to be counted. 

​Classes and event types

​"File" class

File class supports two event types: "exec" and "open".

"exec" allows matches on command execution and "open" allows tracking of files being opened by various applications.

The class supports the following match targets: 

path - match on the path of a file that is being executed or opened.

"Socket" class

Socket class supports two event types: "listen" and "connect". "listen" detects new ports being open and "connect" matches on connections initiated from the monitored device.

The class supports the following match targets:

ip_type - a type of the IP a connection is attempted to. The values can be: "private", "public", "loopback", and "multicast".
ip_version - can be either "4" or "6".
ip_proto - IP protocol, at this time "tcp", "udp", and "icmp" are supported.
ip_addr - IP address in string form, can include netmask.
port - destination port number (integer) of the connection being made. Or the port number of the new socket.

 

​Matches

Each match target can have an array of matches associated with it. Most of the currently supported targets such as "path" are string, with exception of "ip_version" and "port". Different matches can be applied to strings and integers.

String matches

The following string matches are currently supported by kernel rules:
starts_with - matches a string that starts with provided "needle".
ends_with - matches a string that ends with provided "needle".
eq - matches an entire string against provided "needle".
neq - negative match, only matches if a string does not match provided "needle".

Integer matches

Only two matches are supported by integers:

eq - checks if integer is equal to provided value.
neq - checks if integer is not equal to provided value.

not:
  starts_with: /usr/bin

Negative match

Match can be flipped to negative match by wrapping it in "not" object