10.9. 转换操作¶
转换关键字用于将粘性缓冲区中的数据转换为其他形式。部分转换操作支持选项参数,以便更精细地控制转换过程。
示例:
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;)
10.9.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
。
10.9.2. 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
将被匹配。
10.9.3. 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
将被匹配。
10.9.4. strip_whitespace¶
移除所有C语言 isspace()
函数认定的空白字符。
示例:
alert http any any -> any any (file_data; strip_whitespace; \
content:"window.navigate("; sid:1;)
10.9.5. compress_whitespace¶
将所有连续空白字符压缩为单个空格。
10.9.6. 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;)
10.9.7. 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;)
10.9.8. 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;)
10.9.9. 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;)
10.9.10. 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;)
10.9.11. 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;)
10.9.12. url_decode¶
解码URL编码数据(将'+'替换为空格,'%HH'替换为对应值),不支持Unicode '%uZZZZ'编码解码。
10.9.13. xor¶
对缓冲区应用异或解码。
此示例在 http.uri
包含用4字节密钥 0d0ac8ff
异或编码的 password=
时告警:
示例:
alert http any any -> any any (msg:"HTTP with xor"; http.uri; \
xor:"0d0ac8ff"; content:"password="; sid:1;)
10.9.14. 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;)
10.9.15. 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;)
10.9.16. from_base64¶
此转换类似关键字 base64_decode
:使用可选的 mode
、offset
和 bytes
参数解码缓冲区,解码后的数据可用于匹配。
转换完成后,缓冲区仅包含可被base64解码的字节。若解码过程遇到无效字节,这些字节不会包含在缓冲区中。
选项值必须以 ,
分隔且可任意排序。
格式:
from_base64: [[bytes <value>] [, offset <offset_value> [, 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";
10.9.17. 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 参数的转换,使用这些值(若无参数则使用默认值)对缓冲区应用大写转换:
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