Skip to content

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: 
    - "https://nvd.nist.gov/vuln/detail/CVE-2020-14882"
    - "https://github.com/jas502n/CVE-2020-14882"
    - ...

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.