The Complexities of byte_jump

Background

byte_jump is considered one of the more difficult to use Snort/Suricata keywords. It seems that every time byte_jump is called for it requires re-learning the specific syntax and nuances of this keyword.

byte_jump is used when the length of a packet, or the length of a “segment” of data is encoded into the packet itself. In relation to malware, this behavior can be observed in many malware families, and we can make use of it to reduce false positives by ensuring the packet length is exactly that which is encoded in the packet.

Many protocols also include payload/data lengths, and byte_jump can be used to test for buffer overflows.

Resources

Feel free to use the public Dalton instance located at https://doesmyrule.work to replicate the examples findings.

Official Documentation of byte_jump

Snort
Suricata

Helpful blogs/mailing list posts

Cisco Talos - Using byte_jump as Detection Mechanism

Detection Pointer

In order to understand byte_jump, it’s helpful to think of a “detection pointer” which corresponds to the current location within the packet/payload/buffer/etc the IDS engine has progressed to while matching content. It can be moved forward and backward depending on which keywords and options are used.

This “detection pointer” is known formally as the “Detection Offset End Pointer” or the doe_ptr.

This detection pointer exists before, after or between bytes. Some keywords, such as content, offset and distance move the detection pointer.

  • content
    • moves the detection pointer when it finds a positive match
  • offset
    • statically moves the detection pointer relative to the start of the packet/payload/buffer
  • distance
    • moves the detection pointer relative to a previous positive content match.
    • negative values move the pointer backward

byte_jump is another keyword that moves the detection pointer. In order to determine how far to move the detection pointer, byte_jump will read bytes from the packet/payload/buffer.

Important Behaviors

Location of the start of jump

By default byte_jump will jump from just after the last byte used to determine the distance of the jump.
Consider the payload used in Example 1 below:

\x00\x00\x00\x2bWindows 10 Pro|x64|12 GB|John-PC|John|Admin

If byte_jump is configured to use the first four bytes of this payload (byte_jump:4,0;), the jump wil start from just after \x2b and before W. As the detection pointer exists between bytes, the engine will start the jump between two bytes.

No alert if there is no data beyond the jump

If the jump causes the detection pointer to move to the end of, or beyond, the packet/payload/buffer, no alert will be produced. See Example 1 for more details.

Syntax

There are many options to byte_jump and not all will be discussed here. There are only two required options, which are positional in nature.

  1. num of bytes
  2. offset

The most common optional arguments used are:

  1. post_offset
    • steps backwards or forward a number of bytes after making the jump
  2. from_beginning
    • makes the “jump” from before the first byte of the packet/payload/buffer
  3. relative
    • adjust the offset “relative” to the last positive content match

Examples

The following examples will provide a simple introduction to byte_jump using the mentioned common optional arguments. The examples will focus on using the byte_jump keyword to detection a malware checkin. Example rules will focus on the use of the byte_jump keyword and less on the best practices of rule writing, as a result some rules will be intentionally sub-optimal to not distract from the specifics of the byte_jump.

Example 1

Example 1 will demonstrate the basic use of byte_jump in combination with the post_offset option and the isdataat keyword to ensure that a payload or buffer is an exact length included in the packet itself.

Payload

Consider pcap_1.pcap with the following raw content:

\x00\x00\x00\x2bWindows 10 Pro|x64|12 GB|John-PC|John|Admin

The first 4 bytes are a “length header” (\x00\x00\x00\x2b) indicating the length of the remaining payload in hex*. Note that it does not include the “length header” bytes themselves. For the sake of this example, assume the length is variable but the start of the data (Windows\x20) is always the same. As other elements in the payload are variable length, as such, the packet will not have a predetermined length. In effort to reduce FPs, the signature should ensure the packet length matches the length in the “length header”.

*byte_jump does allow for an option (dec) to treat the bytes used to determine the distance of the jump as decimal instead of hex.

In this example, the “length header” indicates there are 43 bytes of data.

The signature logic being attempted here is:

  1. Ensure Windows\x20is in the packet (for simplicities the exact location of those bytes will not be considered)
  2. Ensure the data after the “length header” is exactly the length set forth in the “length header”.

Signature Attempt #1

alert tcp-pkt $HOME_NET any -> $EXTERNAL_NET any (flow:established,to_server; content:"Windows|20|"; byte_jump:4,0; sid:1;)

The content match provided will move the pointer right between the \x20 and 1 of 10 Pro. The byte_jump options indicate to interpret four bytes, starting at “absolute” (no relative option) offset of 0. byte_jump will then move the pointer to before the first byte (0x00), read in four bytes (\x00\x00\x00\x2b) and then make the jump of 43 bytes (hex \x2b) starting at the end of the four bytes read. This moves the pointer to the end of the payload.

Step 1: Content Match
Step 2: back to offset 0
Step 3: “Consume” the bytes
Step 4: Make the jump

Visual Analysis

z_byte_jump_1

Outcome

sid:1 does not produce an alert. This lack of alert is due to the behavior of byte_jump which only evaluates to true (causing an alert) when there is data beyond the location of the detection pointer, after the jump.

Two rules below will demonstrate this behavior by using the
isdataat keyword in attempt to prove there either is or is not data after the jump. Based on the rules below (sid:2 and sid:3), it’s reasonable to assume at least one of them should produce an alert.

alert tcp-pkt $HOME_NET any -> $EXTERNAL_NET any (flow:established,to_server; content:"Windows|20|"; byte_jump:4,0; isdataat:1,relative; sid:2;)
alert tcp-pkt $HOME_NET any -> $EXTERNAL_NET any (flow:established,to_server; content:"Windows|20|"; byte_jump:4,0; isdataat:!1,relative; sid:3;)

However, neither of them do. This demonstrates an important behavior of byte_jump. It will not alert if there is no data beyond the detection pointer after the jump as been completed.

Signature Attempt #2

alert tcp-pkt $HOME_NET any -> $EXTERNAL_NET any (flow:established,to_server; content:"Windows|20|"; byte_jump:4,0, post_offset -1; sid:4;)

In order to cause an alert, we can use the post_offset option with a value of -1 to “step back” one byte, which will move the detection pointer back into the buffer between i and n as demonstrated by the gif below.

Visual Analysis

z_byte_jump_2

Outcome

While sid:4 does produce an alert, there is no logic which ensures that the next byte is end of the
packet/payload/buffer.

Signature Attempt #3

In order to ensure the byte_jump has actually jumped to the end of the packet/payload/buffer, the isdataat keyword, along with a negated match can be used.

In practice, this will ensure that there is NOT data, two byes away from the current detection pointer location.

alert tcp-pkt $HOME_NET any -> $EXTERNAL_NET any (flow:established,to_server; content:"Windows|20|"; byte_jump:4,0, post_offset -1; isdataat:!2,relative; sid:5;)
Visual Analysis

z_byte_jump_3

Outcome

sid:5 contains logic which matches on:

  1. Windows\x20is in the payload
  2. The payload, not including the “length header”, is exactly the length indicated in the “length header”.

Example 2

Example 2 will demonstrate the use of from_beginning. This option is useful when the “length header” includes the length of the “length header”.

Payload

Consider pcap_2.pcap with the following raw content:

\x00\x00\x00\x2fWindows 10 Pro|x64|12 GB|John-PC|John|Admin

This content includes the length of the header in the “length header” field. Given there is only a single “length header” this is a good candidate to demonstrate the use of the from_beginning option.

Using from_beginning causes the engine jump from the start of the packet/payload/buffer. As demonstrated in Example 1 the post_offset option and isdataat keyword will be leveraged again.

Signature Attempt #1

alert tcp-pkt $HOME_NET any -> $EXTERNAL_NET any (flow:established,to_server; content:"Windows|20|"; byte_jump:4,0, from_beginning, post_offset -1; isdataat:!2,relative; sid:6;)

Example 3

Example 3 will demonstrate the use of the relative option.

The relative options effects the offset of the byte_jump command. That is to stay, it effects which bytes the engines uses to determine the length of the jump, it does not make the start of the jump relative to the last content match.

Payload

Consider pcap_3.pcap with the raw data of:

\x00\x01\x25\x00\x00\x00\x2bWindows 10 Pro|x64|12 GB|John-PC|John|Admin

This content a total of 4 different fields:

  • Packet Type Length - \x00\x01
  • Packet Type - \x25
  • Data Length - \x00\x00\x00\x2b
  • Data - Windows 10 Pro|x64|12 GB|John-PC|John|Admin

Signature Attempt #1

As the “data length header” is at a variable location from the start of the packet, the relative option can be leveraged. In this case, the relative keyword will be used to “back up” to the start of the “data length” field after finding the start of the Data. As in previous example, assume the length is variable but the start of the data (Windows\x20) is always the same.

alert tcp-pkt $HOME_NET any -> $EXTERNAL_NET any (flow:established,to_server; content:"Windows|20|"; byte_jump:4,-12,relative, post_offset -1; isdataat:!2,relative; sid:6;)

Visual Analysis

z_byte_jump_4

Outcome

sid:6 produces an alert and contains all the required logic to ensure the “Data” is exactly the length indicated in the “Data Length” header and that there is nothing after it in the payload.f

3 Likes