转换操作 =============== 转换关键字用于将粘性缓冲区中的数据转换为其他形式。部分转换操作支持选项参数,以便更精细地控制转换过程。 示例:: alert http any any -> any any (file_data; strip_whitespace; \ content:"window.navigate("; sid:1;) 此示例即使在 ``navigate`` 和 ``(`` 之间存在一个或多个空格时也能匹配流量。 转换操作可以链式调用。它们按照规则中出现的顺序依次处理,每个转换的输出将作为下一个转换的输入。 示例:: alert http any any -> any any (http_request_line; compress_whitespace; to_sha256; \ content:"|54A9 7A8A B09C 1B81 3725 2214 51D3 F997 F015 9DD7 049E E5AD CED3 945A FC79 7401|"; sid:1;) .. 注意:: 并非所有粘性缓冲区都支持转换操作 dotprefix --------- 在缓冲区内容前添加 ``.`` 字符,便于精简域名检查。例如,输入字符串 ``hello.google.com`` 会被修改为 ``.hello.google.com``。添加点号后,``google.com`` 能匹配 ``content:".google.com"`` 示例:: alert dns any any -> any any (dns.query; dotprefix; \ content:".microsoft.com"; sid:1;) 此示例将匹配 ``windows.update.microsoft.com`` 和 ``maps.microsoft.com.au``,但不匹配 ``windows.update.fakemicrosoft.com``。 此规则可仅匹配域名:示例:: alert dns any any -> any any (dns.query; dotprefix; \ content:".microsoft.com"; endswith; sid:1;) 此示例将匹配 ``windows.update.microsoft.com`` 但不匹配 ``windows.update.microsoft.com.au``。 最后,此规则可仅匹配顶级域名:示例:: alert dns any any -> any any (dns.query; dotprefix; \ content:".co.uk"; endswith; sid:1;) 此示例将匹配 ``maps.google.co.uk`` 但不匹配 ``maps.google.co.nl``。 domain ------ 从缓冲区提取域名。域名定义基于 `Mozilla公共后缀列表 `_,这意味着既包含传统顶级域名如 ``.com``,也包含特定域名如 ``airport.aero`` 或 ``execute-api.cn-north-1.amazonaws.com.cn``(这些域名允许用户在下方声明子域名)。 示例:: alert tls any any -> any any (tls.sni; domain; \ dataset:isset,domains,type string,load domains.lst; sid:1;) 此示例将匹配文件 ``domains.lst`` 中的所有域名。例如若文件包含 ``oisf.net``,则 ``webshop.oisf.net`` 将被匹配。 tld --- 从缓冲区提取顶级域名(TLD)。TLD定义基于 `Mozilla公共后缀列表 `_,包含传统TLD如 ``com`` 和特定域名如 ``airport.aero`` 或 ``execute-api.cn-north-1.amazonaws.com.cn``(这些域名允许用户在下方声明子域名)。 示例:: alert tls any any -> any any (tls.sni; tld; \ dataset:isset,tlds,type string,load tlds.lst; sid:1;) 此示例将匹配文件 ``tlds.lst`` 中的所有TLD。例如若文件包含 ``net``,则 ``oisf.net`` 将被匹配。 strip_whitespace ---------------- 移除所有C语言 ``isspace()`` 函数认定的空白字符。 示例:: alert http any any -> any any (file_data; strip_whitespace; \ content:"window.navigate("; sid:1;) compress_whitespace ------------------- 将所有连续空白字符压缩为单个空格。 to_lowercase ------------ 将缓冲区内容转为小写后传递。 此示例在 ``http.uri`` 包含 ``this text has been converted to lowercase`` 时告警: 示例:: alert http any any -> any any (http.uri; to_lowercase; \ content:"this text has been converted to lowercase"; sid:1;) to_md5 ------ 计算缓冲区的MD5哈希值并传递原始哈希值。 示例:: alert http any any -> any any (http_request_line; to_md5; \ content:"|54 A9 7A 8A B0 9C 1B 81 37 25 22 14 51 D3 F9 97|"; sid:1;) to_uppercase ------------ 将缓冲区内容转为大写后传递。 此示例在 ``http.uri`` 包含 ``THIS TEXT HAS BEEN CONVERTED TO UPPERCASE`` 时告警: 示例:: alert http any any -> any any (http.uri; to_uppercase; \ content:"THIS TEXT HAS BEEN CONVERTED TO UPPERCASE"; sid:1;) to_sha1 --------- 计算缓冲区的SHA-1哈希值并传递原始哈希值。 示例:: alert http any any -> any any (http_request_line; to_sha1; \ content:"|54A9 7A8A B09C 1B81 3725 2214 51D3 F997 F015 9DD7|"; sid:1;) to_sha256 --------- 计算缓冲区的SHA-256哈希值并传递原始哈希值。 示例:: alert http any any -> any any (http_request_line; to_sha256; \ content:"|54A9 7A8A B09C 1B81 3725 2214 51D3 F997 F015 9DD7 049E E5AD CED3 945A FC79 7401|"; sid:1;) pcrexform --------- 对缓冲区应用正则表达式,输出*第一个捕获组*。 .. 注意:: 此转换需要包含正则表达式的必选选项字符串。 此示例在 ``http.request_line`` 包含 ``/dropper.php`` 时告警: 示例:: alert http any any -> any any (msg:"HTTP with pcrexform"; http.request_line; \ pcrexform:"[a-zA-Z]+\s+(.*)\s+HTTP"; content:"/dropper.php"; sid:1;) url_decode ---------- 解码URL编码数据(将'+'替换为空格,'%HH'替换为对应值),不支持Unicode '%uZZZZ'编码解码。 xor --- 对缓冲区应用异或解码。 .. 注意:: 此转换需要十六进制编码的异或密钥作为必选选项。 此示例在 ``http.uri`` 包含用4字节密钥 ``0d0ac8ff`` 异或编码的 ``password=`` 时告警: 示例:: alert http any any -> any any (msg:"HTTP with xor"; http.uri; \ xor:"0d0ac8ff"; content:"password="; sid:1;) header_lowercase ---------------- 用于HTTP/1和HTTP/2头部名称规范化。将头部名称转为小写,同时保持头部值不变。 实现采用状态机: - 在找到 ``:`` 前转为小写 - 在找到换行符前保持原状,之后切换回初始状态 此示例对带有authorization头的HTTP/1和HTTP/2流量告警: 示例:: alert http any any -> any any (msg:"HTTP authorization"; http.header_names; \ header_lowercase; content:"authorization:"; sid:1;) strip_pseudo_headers -------------------- 用于HTTP/1和HTTP/2头部名称规范化。移除HTTP2伪头部(名称和值)。 实现方式为移除所有以 ``:`` 开头的行。 此示例对仅含User-Agent的HTTP/1和HTTP/2流量告警: 示例:: alert http any any -> any any (msg:"HTTP ua only"; http.header_names; \ bsize:16; content:"|0d 0a|User-Agent|0d 0a 0d 0a|"; nocase; sid:1;) .. _from_base64: from_base64 ----------- 此转换类似关键字 ``base64_decode``:使用可选的 ``mode``、``offset`` 和 ``bytes`` 参数解码缓冲区,解码后的数据可用于匹配。 转换完成后,缓冲区仅包含可被base64解码的字节。若解码过程遇到无效字节,这些字节不会包含在缓冲区中。 选项值必须以 ``,`` 分隔且可任意排序。 .. 注意:: 默认情况下 ``from_base64`` 遵循RFC 4648,即遇到任何非base64字母表字符时会拒绝该字符及后续字符串。 格式:: from_base64: [[bytes ] [, offset [, mode: strict|rfc4648|rfc2045]]] 各选项默认值: - ``bytes`` 默认为输入缓冲区长度 - ``offset`` 默认为 ``0`` 且必须小于 ``65536`` - ``mode`` 默认为 ``rfc4648`` 注意:未来版本中 ``bytes`` 和 ``offset`` 可能支持来自 `byte_extract` 和/或 `byte_math` 的变量,当前暂不支持。 模式 ``rfc4648`` 应用RFC 4648解码逻辑,适用于可安全通过电子邮件发送、用于URL或包含在HTTP POST请求中的二进制数据编码。 模式 ``rfc2045`` 应用RFC 2045解码逻辑,支持包含空格、换行符等非base64字母表字符的字符串。 模式 ``strict`` 在编码字节中发现无效字符时将失败。 以下示例将在缓冲区内容匹配时告警(参见最后一个 ``content`` 值的预期字符串): 此示例使用默认参数将 `"VGhpcyBpcyBTdXJpY2F0YQ=="` 转换为 `"This is Suricata"`:: content: "VGhpcyBpcyBTdXJpY2F0YQ=="; from_base64; content:"This is Suricata"; 此示例将 `"dGhpc2lzYXRlc3QK"` 转换为 `"thisisatest"`:: content:"/?arg=dGhpc2lzYXRlc3QK"; from_base64: offset 6, mode rfc4648; \ content:"thisisatest"; 此示例将 `"Zm 9v Ym Fy"` 转换为 `"foobar"`:: content:"/?arg=Zm 9v Ym Fy"; from_base64: offset 6, mode rfc2045; \ content:"foobar"; .. _lua-transform: luaxform -------- 此转换允许Lua脚本对缓冲区应用转换。 用于转换的Lua脚本*必须*包含名为 ``transform`` 的函数。 Lua转换可接收可选参数(参见下方示例),但非必需。参数以逗号分隔。 若缓冲区为空或Lua框架不可用(罕见情况),则不会调用Lua转换函数。 Lua转换函数必须返回两个值(见下文),否则缓冲区不会被修改。 注意参数和值未经验证或解释直接传递,最多支持10个参数。 Lua转换函数调用参数: * `input` 提供给转换的缓冲区 * `arguments` 参数列表 Lua转换函数必须返回两个值[Lua数据类型如下]: * `buffer` [Lua字符串] 返回包含原始输入缓冲区或经转换修改后的缓冲区 * `bytes` [Lua整数] 返回缓冲区的字节数 此示例将HTTP数据提供给Lua转换,并用 `content` 检查转换结果: 示例:: alert http any any -> any any (msg:"Lua Xform example"; flow:established; \ file.data; luaxform:./lua/lua-transform.lua; content: "abc"; sid: 2;) 此示例向Lua转换提供HTTP数据及指定转换偏移量和字节数的参数,随后用 `content` 匹配结果缓冲区: 示例:: alert http any any -> any any (msg:"Lua Xform example"; flow:established; \ file.data; luaxform:./lua/lua-transform.lua, bytes 12, offset 13; content: "abc"; sid: 1;) 以下Lua脚本展示了一个处理 `bytes` 和 `offset` 参数的转换,使用这些值(若无参数则使用默认值)对缓冲区应用大写转换: .. code-block:: lua function init (args) local needs = {} return needs end local function get_value(item, key) if string.find(item, key) then local _, value = string.match(item, "(%a+)%s*(%d*)") if value ~= "" then return tonumber(value) end end return nil end -- 支持的参数 local bytes_key = "bytes" local offset_key = "offset" function transform(input_len, input, argc, args) local bytes = #input local offset = 0 -- 查找可选的bytes和offset参数 for i, item in ipairs(args) do local value = get_value(item, bytes_key) if value ~= nil then bytes = value else local value = get_value(item, offset_key) if value ~= nil then offset = value end end end local str_len = #input if offset < 0 or offset > str_len then print("offset is out of bounds: " .. offset) return nil end str_len = str_len - offset if bytes < 0 or bytes > str_len then print("invalid bytes " .. bytes .. " or bytes > length " .. bytes .. " length " .. str_len) return nil end local sub = string.sub(input, offset + 1, offset + bytes) return string.upper(sub), bytes end