Project

General

Profile

Actions

Feature #3285

closed

rules: XOR keyword

Added by Brandon Murphy about 5 years ago. Updated almost 3 years ago.

Status:
Closed
Priority:
Normal
Target version:
Effort:
Difficulty:
Label:

Description

Due to masked WebSocket usage with Masked payloads and XOR in general used by malware for network "encryption", I'm wondering if it would be possible to add support for XOR similar to the existing base64_decode/base64_data keywords.

The only existing method I am aware of for achieving this outcome using existing features is a Lua script/rule. However this depends heavily on user configuration to be useful. Providing an XOR keyword has the benefit of not requiring Lua support and provides a general purpose function that could be used with Masked Payloads within WebSockets and any other network communications using XOR.

WebSocket support has been requested here - https://redmine.openinfosecfoundation.org/issues/2695, but does not directly address the use of Masked Payloads.

An example of keyword usage might be

xor:key <xor key in hex>, bytes <value>, offset <value>, relative;
xor_data;

Thanks


Files


Related issues 3 (2 open1 closed)

Related to Suricata - Feature #2695: websocket supportClosedPhilippe AntoineActions
Related to Suricata - Task #4097: Suricon 2020 brainstormAssignedVictor JulienActions
Related to Suricata - Task #4762: Suricon 2021 brainstormAssignedVictor JulienActions
Actions #1

Updated by Brandon Murphy about 5 years ago

Having given a bit more thought, this solution would only work where XOR keys are known. This limitation moves the usefulness of this request to address Masked Paylaods of WebSockets as the XOR Key is supposed to be randomly per each WebSocket frame.

Actions #2

Updated by Jason Ish about 5 years ago

Actions #3

Updated by Victor Julien about 5 years ago

  • Status changed from New to Feedback
  • Assignee set to Community Ticket
  • Target version set to TBD

I suppose it would be useful to use the result of byte_extract as input to the key.

Actions #4

Updated by Brandon Murphy about 5 years ago

Adding a real world example of how this will be helpful.

AZORult 3.2 uses a static XOR key to encode network communications. PCAP is attached and taken from Any.Run

See Packet 74 for the initial checkin via POST. This traffic can be decoded as described in this CyberChef Recipe

The Initial Checkin of AZORult uses a unique ID generated from system details as documented by Cylance

Today, as the values change depending on each infected system, detecting the initial checkin is difficult and very prone to false negative, or is based on "circumstantial" detection, or based on post initial checkin activity.

The requested feature would allow for direct detection of this type of CnC communicators.

Using the attached pcap as an example. Here is a rule utilizing the proposed keyword.

alert http $HOME_NET any -> $EXTERNAL_NET any (http.method; content:"POST"; http.uri; content:".php"; endswith; http.request_body; content:"|4a 2f 2b|"; fast_pattern; depth:3; xor: key 0d0ac8, bytes 133, offset 0; xor_data; pcre:"/^G(?:[A-F]|%3[0-9]){7}%2D(?:[A-F]|%3[0-9]){8}%2D(?:[A-F]|%3[0-9]){8}%2D(?:[A-F]|%3[0-9]){8}%2D(?:[A-F]|%3[0-9]){9}$/"; sid:1; rev:1; classtype:command-and-control;)

notice the xor keyword is applied to the http.request_body buffer and xor_data is a sticky buffer.

also, while 133 bytes is longer than the buffer, 133 bytes is the longest possible encoded/xor'ed unique ID.

Note - this rule will cover any of the unique IDs that are G[0-9] when fully decoded. Additional rule(s) would be required to match G[A-F] while maintaining a maybe okish fast pattern;

Actions #5

Updated by Brandon Murphy about 5 years ago

Victor Julien wrote:

I suppose it would be useful to use the result of byte_extract as input to the key.

Yes, that would be very useful. It would address the WebSockets use case and, i've seen more than one sample where malware configs/c2 comms/stage 2 binaries, etc are XOR'ed but the key is at a specific offset in the stream.

Actions #6

Updated by Simon Dugas over 4 years ago

Here is a first attempt to implement this feature:
- https://github.com/OISF/suricata/pull/5015
- https://github.com/OISF/suricata-verify/pull/243

The only difference with the syntax discussed in this issue is the xor key is surrounded in double-quotes when specifying a hex string. This allows us to distinguish it from a byte_extract variable.

Actions #7

Updated by Victor Julien over 4 years ago

  • Status changed from Feedback to In Review
  • Assignee changed from Community Ticket to Simon Dugas
Actions #8

Updated by Jeff Lucovsky about 4 years ago

  • Related to Task #4097: Suricon 2020 brainstorm added
Actions #9

Updated by Victor Julien about 4 years ago

The idea at the 2020 brainstorm call was:

extend byte_extract to allow arbitrary length extract for the key
xor transform keyword that can either take the variable name from byte extract or a static key as input

Actions #10

Updated by Victor Julien about 4 years ago

  • Subject changed from XOR keyword to rules: XOR keyword
  • Target version changed from TBD to 7.0.0-beta1
Actions #11

Updated by Victor Julien over 3 years ago

Hi Simon, have you looked into doing this as a transform?

Actions #12

Updated by Victor Julien over 3 years ago

  • Status changed from In Review to In Progress
Actions #13

Updated by Victor Julien about 3 years ago

  • Related to Task #4762: Suricon 2021 brainstorm added
Actions #14

Updated by Philippe Antoine about 3 years ago

  • Status changed from In Progress to In Review
Actions #15

Updated by Simon Dugas about 3 years ago

Last time I was looking into transforms they didn't seem to support a "sticky buffer" on the entire TCP payload or holding on to variables such as keys. That was a while ago and I think the transforms API may have improved since then, I'll have a look at catenacyber's PR. I have plenty of test cases and suricata-verify tests that could be useful.

My apologies for the late response.

Actions #16

Updated by Victor Julien about 3 years ago

  • Assignee changed from Simon Dugas to Philippe Antoine
Actions #17

Updated by Philippe Antoine almost 3 years ago

  • Status changed from In Review to Closed
Actions

Also available in: Atom PDF