Project

General

Profile

Actions

Feature #7847

open

rules: extend byte_extract named variables for use in other keywords/transformations such as xor

Added by James Emery-Callcott 6 months ago. Updated 7 days ago.

Status:
Assigned
Priority:
Normal
Assignee:
Target version:
Effort:
Difficulty:
Label:

Description

We have seen several cases in which a packet is XOR'd with a single byte and this byte can be found at X offset. Currently, the only solution for detection (aside from Lua) is to write a signature for that XOR key which is incredibly static.

We are asking for byte_extract & xor support so that we can specify the location of the XOR key, extract it and store it in a named variable, and then use that extracted byte with the XOR transformation.

ex.

http.request_body; byte_extract:1,0,xor_key; xor:xor_key; content:"infected";

I suspect this becomes more difficult because we would now need to tell the XOR transformation that we are only interested in part of the buffer instead of the whole buffer. Maybe we'd also need the ability to tell the XOR transformation where to begin processing data with an offset value?

http.request_body; byte_extract:1,0,xor_key; xor:offset 1,xor_key; content:"infected";


Files

xor_packet_test.pcap (955 Bytes) xor_packet_test.pcap James Emery-Callcott, 01/12/2026 09:41 PM

Subtasks 1 (1 open0 closed)

Feature #8234: rules: extend byte_extract named variables for use in other keywords/transformations such as xor (8.0.x backport)AssignedJeff LucovskyActions

Related issues 2 (2 open0 closed)

Related to Suricata - Feature #6831: rules: support extraction of bytes of non-numeric valuesNewVictor JulienActions
Related to Suricata - Feature #7321: rules: cross buffer byte_* keyword supportNewOISF DevActions
Actions #1

Updated by Stuart DC 5 months ago

❤️ +1 to making XOR flexible

Actions #2

Updated by Victor Julien about 1 month ago

  • Subject changed from extend byte_extract named variables for use in other keywords/transformations such as xor to rules: extend byte_extract named variables for use in other keywords/transformations such as xor

Can you share a case like this, ideally with the a lua script to do the same if you have that?

Actions #3

Updated by Victor Julien about 1 month ago

  • Related to Feature #6831: rules: support extraction of bytes of non-numeric values added
Actions #4

Updated by Victor Julien about 1 month ago

  • Related to Feature #7321: rules: cross buffer byte_* keyword support added
Actions #5

Updated by James Emery-Callcott 11 days ago

I sure can. Here is a Lua script that extracts the 5th byte from the payload, which is always a random XOR key and then uses it to decrypt the bytes following.

Generic rule used in this test case:

alert tcp any any -> any any (msg:"ET OISF XOR Lua demonstration"; flow:established,to_server; lua:xor1.lua; classtype:misc-activity; rev:1; sid:1;)

Demo PCAP attached.

function manual_xor(a, b)
    local result = 0
    local bit = 1
    while a > 0 or b > 0 do
        local bit_a = a % 2
        local bit_b = b % 2
        if bit_a ~= bit_b then
            result = result + bit
        end
        a = math.floor(a / 2)
        b = math.floor(b / 2)
        bit = bit * 2
    end
    return result
end

function init(args)
    local needs = {}
    needs["payload"] = tostring(true)
    return needs
end

function match(args)
    local payload = args["payload"]
    if not payload then
        return 0
    end

    if #payload < 6 then
        return 0
    end

    local byte1 = payload:byte(1)
    local byte2 = payload:byte(2) 
    local byte3 = payload:byte(3)
    local byte4 = payload:byte(4)

    if byte1 ~= 0x00 or byte2 ~= 0x00 or byte3 ~= 0x04 or byte4 ~= 0x01 then
        return 0
    end

    local xor_key = payload:byte(5)

    local encrypted_data = payload:sub(6)

    local decrypted = "" 
    for i = 1, #encrypted_data do
        local encrypted_byte = encrypted_data:byte(i)
        local decrypted_byte = manual_xor(encrypted_byte, xor_key)
        decrypted = decrypted .. string.char(decrypted_byte)
    end

    local traffic_found = string.find(decrypted:lower(), "traffic")

    if traffic_found then
        SCLogInfo("XOR Traffic Detected - 'traffic' keyword found!")
        SCLogInfo("XOR Key: 0x" .. string.format("%02x", xor_key))
        SCLogInfo("Decrypted Message: " .. decrypted)
        SCLogInfo("Payload Length: " .. #encrypted_data .. " bytes")
        SCLogInfo("Validation Method: Keyword 'traffic' found at position " .. traffic_found)

        return 1
    end

    return 0
end

Victor Julien wrote in #note-2:

Can you share a case like this, ideally with the a lua script to do the same if you have that?

Actions #6

Updated by Victor Julien 8 days ago

Thanks @jcallcott, I created 2 SV tests for this example to make it easier to analyze it: https://github.com/OISF/suricata-verify/pull/2862

Actions #7

Updated by Victor Julien 7 days ago

  • Status changed from New to Assigned
  • Assignee changed from OISF Dev to Jeff Lucovsky
  • Target version changed from TBD to 9.0.0-beta1
Actions #8

Updated by Victor Julien 7 days ago

  • Label Needs backport to 8.0 added
Actions #9

Updated by OISF Ticketbot 7 days ago

  • Subtask #8234 added
Actions #10

Updated by OISF Ticketbot 7 days ago

  • Label deleted (Needs backport to 8.0)
Actions

Also available in: Atom PDF