Rules
Melody rules are used to apply tags on matching packets. They have multiple use cases, such as monitoring emerging threats, automated droppers, vulnerability scanners...
Take a look in the $melody/rule-available
and $melody/internal/rules/test_resources
folders to quickly find working examples.
First look#
A rule file can contain multiple rule descriptions.
Example
This example detects CVE-2020-14882 (Oracle Weblogic RCE) scans or exploitation attempts by matching either of the two URI on the HTTP level :
CVE-2020-14882 Oracle Weblogic Server RCE:
layer: http
meta:
id: 3e1d86d8-fba6-4e15-8c74-941c3375fd3e
version: 1.0
author: BonjourMalware
status: stable
created: 2020/11/07
modified: 2020/20/07
description: "Checking or trying to exploit CVE-2020-14882"
references:
- "https://nvd.nist.gov/vuln/detail/CVE-2020-14882"
match:
http.uri:
startswith|any|nocase:
- "/console/css/"
- "/console/images"
contains|any|nocase:
- "console.portal"
- "consolejndi.portal?test_handle="
tags:
cve: "cve-2020-14882"
vendor: "oracle"
product: "weblogic"
impact: "rce"
Tip
Check the the whitelist
and blacklist
section to filter ports and IP addresses.
Structure#
The rules have 7 sections : layer
, meta
, match
, whitelist
, blacklist
, tags
and embed
.
layer#
The rule will look for matches in the specified layer
's protocol data.
Each layer
expose different fields depending on the protocol they represent. They're detailed in the Layers page.
The following layers are supported :
Key | IPv4 | IPv6 |
---|---|---|
http | ✅ | ✅ |
tcp | ✅ | ✅ |
udp | ✅ | ✅ |
icmpv4 | ✅ | ❌ |
icmpv6 | ❌ | ✅ |
Important
A single rule only applies to the targeted layer. Use multiple rules if you want to match multiple layers.
meta#
The meta
section contains all the rule's metadata. Every keys are mandatory, except references
.
Key | Type | Description | Values | Examples |
---|---|---|---|---|
id | string | Rule's unique identifier. Each rule must have a unique UUIDv4 | - | id: c30370f7-aaa8-41d0-a392-b56c94869128 |
version | string | Rule syntax version | 1.0 | version: 1.0 |
author | string | The name of the rule's author | - | author: BonjourMalware |
status | string | The status gives an indication of the usability of the rule | stable, experimental | status: stable |
created | yyyy/mm/dd | Creation date | - | created: 2020/11/07 |
modified | yyyy/mm/dd | Last modification date | - | modified: 2020/11/07 |
description | string | A quick description of what the rule is attempting to match | - | description: Checking or trying to exploit CVE-2020-14882 |
references | array | The status gives an indication of the usability of the rule | - | references: |
Important
You must generate a new UUIDv4 for the id
of every rule you create.
Sample code for Python :
import uuid
print(uuid.uuid4())
Go (playground) :
package main
import (
"fmt"
"github.com/google/uuid"
)
func main(){
fmt.Println(uuid.New())
}
match#
The match
block contains a set of conditions that will be checked on every packet of the rule's layer
type.
Here is the structure of the match
section :
match:
any: [true|false] # false by default
field1: # complex condition
any: [true|false] # false by default
operator1|modifier1|modifier2: # matching operator with its modifiers
- value1
- value2
operator2:
- value
field2: # array condition
- value1
- value2
field3: value # string or number condition
Conditions#
A condition corresponds to a field in a packet, specified by its name.
The available conditions depends on the layer
key. The keys are namespaced according to the type they belong to.
Example
udp.payload
, tcp.flags
, http.uri
...
There are 3 types of conditions : number
, flags
or complex
.
Number#
A number.
Example
tcp.window: 512
Note
The number
types takes advantage of YAML to support octal (0o1234), hex (0x1234) and decimal (1234) representation.
Flags#
flags
condition are made of a list of flag combination to match.
The condition is valid as soon as a match is found (OR).
Example
tcp.flags:
- "PA"
- "S"
This rule will match a TCP packet with its flag bits set to "PA" (PSH-ACK, 0x18) or "S" (SYN, 0x2).
Note
Only two fields support flags
condition : tcp.flags
and tcp.fragbits
.
Complex#
The complex
condition type supports matching operators and inline modifiers.
To check which fields support complex
conditions, take a look at the layers documentation.
Matching operators#
The matching operator specifies how to handle data.
A single condition can be made of a set of matching operators.
Important
By default, a rule needs to validate all the conditions to match. However, you can specify any: true
to force a rule to test all of its conditions and return a match as soon as it find a valid one.
Example
udp.payload:
contains:
- "after all, we're all alike."
startswith:
- "Damn kids"
any: true
In this example, the condition key is udp.payload
and the matching operators are contains
and startswith
.
This rule will match if the payload of an UDP packet startswith the string "Damn kids" OR contains "after all, we're all alike.".
The rule needs both to match if we remove the any: true
option.
Name | Description |
---|---|
is | The packet's field value is strictly equal to the condition's value |
contains | The packet's field value contains the condition's value |
startswith | The packet's field value starts with the condition's value |
endswith | The packet's field value ends with the condition's value |
Modifiers#
Modifiers are a way to quickly set options for the matching operator.
They live on the same line, split by |
. All modifiers can be mixed at once.
Important
By default, a condition needs to match all of the given values (AND). However, you can use the |any
modifier to reverse it and force it to test all the values and to return on the first match.
Example
http.body:
contains|any|nocase:
- "Enter my world"
- "the beauty of the baud"
In this example, the modifiers are any
and nocase
. This rule will match if the URI field of an HTTP packet contains any item in the list.
Name | Description | Example |
---|---|---|
any | The rule match if any of the values in the list matches | - |
nocase | The match is case insensitive | abcd == aBcD == ABCD |
regex | The value is a regular expression | '(?:[0-9]{1,3}.){3}[0-9]{1,3}' == 192.0.2.1 |
Danger
Although the regex is compiled only once, it can cause severe overhead while matching packets. Use it with caution.
Hybrid pattern#
complex
condition's support hex values by wrapping them between two |
.
You can mix hex and ascii in a single string as well.
Example
http.body:
contains:
- "|45 6e 74 65 72206d79| world"
Note
'0x' hex notation (|0xbe 0xef|
) is invalid. You can mix spaced and not spaced hex bytes though.
tags#
Each of the key/value pair in the tags
object will be appended to the matches
field of each of the matching packets.
embed#
This is a block where the user can will embed any data in the embedded
key of the matching packet. It can be used as an alternative to tags
to add contextual information.
Example
embed:
my_crime: "curiosity"
...
whitelist and blacklist#
These two fields can be used to filter the packets on which the rule is applied.
IP source addresses and ports are supported.
IP address#
Example
whitelist:
ips:
- 127.0.0.1
This example only tries to match the packets coming from 127.0.0.1.
blacklist:
ips:
- 127.0.0.1
Use the blacklist keyword to reverse the logic and apply the rule to all packets but the one coming from 127.0.0.1.
Example
whitelist:
ips:
- 127.0.0.0/24
CIDR notation supported.
Ports#
Example
whitelist:
ports:
- 80
This example only tries to match the packets going to port 80 .
blacklist:
ports:
- 80
Use the blacklist keyword to reverse the logic and apply the rule to all packets but the one going to port 80.
Example
whitelist:
ports:
- 8000 - 9000
Port ranges are supported. You can choose to put spaces or not.