lua 支持
1. Lua在Suricata中的使用
Lua脚本可以在Suricata的两个组件中使用。第一个是在输出。第二个是检测引擎的规则中。
这个两个功能都使用一系列函数来访问Suricata提取的数据。您可以在Lua函数页面中获取函数列表。
1.1 Lua 输出
Lua可以用来写任意输出。有关更多信息,请参阅Lua输出
1.2 Lua检测
Lua脚本可以用作规则中的过滤条件。有关更多信息,请查看下面的Lua函数
2. Lua函数
2.1 packet
初始化为:
function init(args)
localneeds={}
needs["type"]="packet"
return needs
end
2.1.1 SCPacketTimestamp
获取数据包时间戳为2个数字:自1970-01-01 00:00:00 UTC以来经过的秒数和微秒数.
function log(args)
localsec,usec=SCPacketTimestamp()
end
2.1.2 SCPacketTimeString
添加SCPacketTimeString以获取数据包时间字符串的格式为:11/24 / 2009-18:57:25.179869
function log(args)
ts=SCPacketTimeString()
end
2.1.3 SCPacketTuple
ipver,srcip,dstip,proto,sp,dp=SCPacketTuple()
2.1.4 SCPacketPayload
p = SCPacketPayload()
2.2 flow
function init(args)
localneeds={}
needs["type"]="flow"
return needs
end
2.2.1. SCFlowTimestamps
获取流中第一个和最后一个数据包的时间戳(秒和微秒)
startts,lastts=SCFlowTimestamps()
startts_s,lastts_s,startts_us,lastts_us=SCFlowTimestamps()
2.2.2. SCFlowTimeString
startts=SCFlowTimeString()
2.2.3. SCFlowTuple
ipver,srcip,dstip,proto,sp,dp=SCFlowTuple()
2.2.4. SCFlowAppLayerProto
从流程中获取alprotos作为字符串。如果alproto(尚未)已知,则返回“unknown”。
如:
function log(args)
alproto=SCFlowAppLayerProto()
if alproto~=nil then
print(alproto)
end
end
返回5个值:<alproto> <alproto_ts> <alproto_tc> <alproto_orig> <alproto_expect>
更改和升级协议时使用Orig和expect。在SMTP STARTTLS案例中,orig通常设置为“smtp”并期望“tls”。
2.2.5. SCFlowHasAlerts
如果流有警报,则返回true。
如:
function log(args)
has_alerts=SCFlowHasAlerts()
if has_alerts then
--do something
end
end
2.2.6. SCFlowStats
获取每个流的数据包和字节数。
tscnt,tsbytes,tccnt,tcbytes=SCFlowStats()
2.2.7. SCFlowId
获取流ID
id=SCFlowId()
请注意,简单地打印“id”可能会导致打印科学记数法。为避免这种情况,只需:
id=SCFlowId()
idstr=string.format("%.0f",id)
print("Flow ID: "..idstr.."\n")
2.3 HTTP
初始化
function init(args)
localneeds={}
needs["protocol"]="http"
return needs
end
2.3.1 HttpGetRequestBody和HttpGetResponseBody
通过HttpGetRequestBody和HttpGetResponseBody为脚本提供标准化的正文数据。
无法保证所有身体都可以使用.
如:
function log(args)
a,o,e=HttpGetResponseBody();
--print("offset "..o.." end "..e)
for n,vinipairs(a) do
print(v)
end
end
2.3.2 HttpGetRequestHost
从libhtp的tx-> request_hostname获取主机,它可以是url的主机部分,也可以是Host头的主机部分。
如:
http_host = HttpGetRequestHost()
if http_host==nil then
http_host="<hostname unknown>"
end
2.3.3 HttpGetRequestHeader
http_ua = HttpGetRequestHeader("User-Agent")
if http_ua==nil then
http_ua="<useragent unknown>"
end
2.3.4 HttpGetResponseHeader
server=HttpGetResponseHeader("Server");
print("Server: "..server);
2.3.5 HttpGetRequestLine
rl=HttpGetRequestLine();
print("Request Line: "..rl);
2.3.6 HttpGetResponseLine
rsl=HttpGetResponseLine();
print("Response Line: "..rsl);
2.3.7 HttpGetRawRequestHeaders
rh=HttpGetRawRequestHeaders();
print("Raw Request Headers: "..rh);
2.3.8 HttpGetRawResponseHeaders
rh=HttpGetRawResponseHeaders();
print("Raw Response Headers: "..rh);
2.3.9 HttpGetRequestUriRaw
http_uri=HttpGetRequestUriRaw()
if http_uri==nil then
http_uri="<unknown>"
end
2.3.10 HttpGetRequestUriNormalized
http_uri =HttpGetRequestUriNormalized()
if http_uri==nilthen
http_uri="<unknown>"
end
2.3.11 HttpGetRequestHeaders
a=HttpGetRequestHeaders();
for n,vinpairs(a) do
print(n,v)
end
2.3.12 HttpGetResponseHeaders
a=HttpGetResponseHeaders();
for n,vinpairs(a) do
print(n,v)
end
2.4 DNS
2.4.1 DnsGetQueries
dns_query=DnsGetQueries();
if dns_query~=nil then
for n,tinpairs(dns_query) do
rrname=t["rrname"]
rrtype=t["type"]
print("QUERY: "..ts.." "..rrname.." [**] "..rrtype.." [**] ".."TODO".." [**] "..srcip..":"..sp.." -> "..dstip..":"..dp)
end
end
返回一个表格的表
2.4.2 DnsGetAnswers
dns_answers = DnsGetAnswers();
if dns_answers ~= nil then
for n, t in pairs(dns_answers) do
rrname = t["rrname"]
rrtype = t["type"]
ttl = t["ttl"]
print ("ANSWER: " .. ts .. " " .. rrname .. " [**] " .. rrtype .. " [**] " ..
ttl .. " [**] " .. srcip .. ":" .. sp .. " -> " ..
dstip .. ":" .. dp)
end
end
返回一个表格的表
2.4.3 DnsGetAuthorities
dns_auth = DnsGetAuthorities();
if dns_auth ~= nil then
for n, t in pairs(dns_auth) do
rrname = t["rrname"]
rrtype = t["type"]
ttl = t["ttl"]
print ("AUTHORITY: " .. ts .. " " .. rrname .. " [**] " .. rrtype .. " [**] " ..
ttl .. " [**] " .. srcip .. ":" .. sp .. " -> " ..
dstip .. ":" .. dp)
end
end
返回一个表格的表
2.4.4 DnsGetRcode
rcode = DnsGetRcode();
if rcode == nil then
return 0
end
print (rcode)
返回带有错误消息的lua字符串,或者为nil
2.4.5 DnsGetRecursionDesired
if DnsGetRecursionDesired()==true then
print("RECURSION DESIRED")
end
2.5 TLS
初始化为:
function init (args)
local needs = {}
needs["protocol"] = "tls"
return needs
end
2.5.1 TlsGetVersion
通过TlsGetVersion将TLS会话中的协商版本作为字符串获取。
如:
function log(args)
version=TlsGetVersion()
if version then
-- do something
end
end
2.5.2 TlsGetCertInfo
通过TlsGetCertInfo为脚本提供证书信息。
如:
function log (args)
version, subject, issuer, fingerprint = TlsGetCertInfo()
if version == nil then
return 0
end
end
2.5.3 TlsGetCertChain
通过TlsGetCertChain使证书链可用于脚本。
输出是一个证书数组,每个证书都是带有数据和长度密钥的哈希。
如:
-- Use debian lua-luaossl coming from https://github.com/wahern/luaossl
local x509 = require"openssl.x509"
chain = TlsGetCertChain()
for k, v in pairs(chain) do
-- v.length is length of data
-- v.data is raw binary data of certificate
cert = x509.new(v["data"], "DER")
print(cert:text() .. "\n")
end
2.5.4 TlsGetCertNotAfter
获取证书有效期结束的Unix时间戳。
function log (args)
notafter = TlsGetCertNotAfter()
if notafter < os.time() then
-- expired certificate
end
end
2.5.5 TlsGetCertNotBefore
获取证书有效期开始的Unix时间戳。
如:
function log (args)
notbefore = TlsGetCertNotBefore()
if notbefore > os.time() then
-- not yet valid certificate
end
end
2.5.6 TlsGetCertSerial
通过TlsGetCertSerial获取TLS证书序列号。
如:
function log (args)
serial = TlsGetCertSerial()
if serial then
-- do something
end
end
2.5.7 TlsGetSNI
从TLS连接获取服务器名称指示。
如:
function log (args)
asked_domain = TlsGetSNI()
if string.find(asked_domain, "badguys") then
-- ok connection to bad guys let's do someting
end
end
2.6 JA3
必须在Suricata配置文件中启用JA3(将'app-layer.protocols.tls.ja3-fingerprints'设置为'yes')。
初始化为:
function init (args)
local needs = {}
needs["protocol"] = "tls"
return needs
end
2.6.1 Ja3GetHash
通过Ja3GetHash获取JA3哈希(JA3字符串的md5sum)。
如:
function log (args)
hash = Ja3GetHash()
if hash == nil then
return
end
end
2.6.2 Ja3GetString
通过Ja3GetString获取JA3字符串。
如:
function log (args)
str = Ja3GetString()
if str == nil then
return
end
end
2.7 SSH
初始为:
function init (args)
local needs = {}
needs["protocol"] = "ssh"
return needs
end
2.7.1 SshGetServerProtoVersion
通过SshGetServerProtoVersion获取服务器使用的SSH协议版本。
如:
function log (args)
version = SshGetServerProtoVersion()
if version == nil then
return 0
end
end
2.7.2 SshGetServerSoftwareVersion
通过SshGetServerSoftwareVersion获取服务器使用的SSH软件。
如:
function log (args)
software = SshGetServerSoftwareVersion()
if software == nil then
return 0
end
end
2.7.3 SshGetClientProtoVersion
通过SshGetClientProtoVersion获取客户端使用的SSH协议版本。
如:
function log (args)
version = SshGetClientProtoVersion()
if version == nil then
return 0
end
end
2.7.4 SshGetClientSoftwareVersion
通过SshGetClientSoftwareVersion获取客户端使用的SSH软件。
如:
function log (args)
software = SshGetClientSoftwareVersion()
if software == nil then
return 0
end
end
2.8 Files
要使用文件记录API,脚本的init()函数需要如下所示:
function init (args)
local needs = {}
needs['type'] = 'file'
return needs
end
2.8.1 SCFileInfo
fileid,txid,name,size,magic,md5=SCFileInfo()
返回fileid(数字),txid(数字),名称(字符串),大小(数字),magic(字符串),md5(十六进制)(字符串)
2.8.2 SCFileState
state,stored=SCFileState()
返回状态(字符串),存储(bool)
2.9 Alerts
警报是“packet”记录器的子集:
function init (args)
local needs = {}
needs["type"] = "packet"
needs["filter"] = "alerts"
return needs
end
2.9.1 SCRuleIds
sid,rev,gid=SCRuleIds()
2.9.2 SCRuleMsg
msg=SCRuleMsg()
2.9.3 SCRuleClass
class,prio=SCRuleClass()
2.10Streaming Data
流数据当前可以注销重组的TCP数据和规范化的HTTP数据。将为每个连续数据块调用该脚本。
在TCP重组数据的情况下,根据主机OS设置删除所有可能的重叠。
function init (args)
local needs = {}
needs["type"] = "streaming"
needs["filter"] = "tcp"
return needs
end
在HTTP正文数据的情况下,如果适用,将解压缩并释放正文
function init (args)
local needs = {}
needs["type"] = "streaming"
needs["protocol"] = "http"
return needs
end
2.10.1 SCStreamingBuffer
function log(args)
data = SCStreamingBuffer()
hex_dump(data)
end
2.11 Flow variables
可以从Lua访问,定义和修改Flow变量。为此,您必须使用本节中描述的函数并在init函数中声明计数器:
function init(args)
local needs = {}
needs["tls"] tostring(true)
needs["flowint"] = {"tls-cnt"}
return needs
end
在这里,我们定义了一个tls-cnt Flowint,现在可以通过专用函数在输出或签名中使用。对Flow变量的访问是通过索引完成的,所以在我们的例子中我们需要使用0。
function match(args)
a = ScFlowintGet(0);
if a then
ScFlowintSet(0, a + 1)
else
ScFlowintSet(0, 1)
end
2.11.1 ScFlowintGet
获取Flowint在参数给定的索引处
2.11.2 ScFlowintSet
将Flowint设置为第一个参数给定的索引。第二个参数是值。
2.11.3 ScFlowintIncr
在第一个参数给出的索引处递增Flowint。
2.11.4 ScFlowintDecr
在第一个参数给出的索引处减少Flowint。
2.11.5 ScFlowvarGet
获取参数给定的索引处的Flowvar。
2.11.6 ScFlowvarSet
设置Flowvar。第一个参数是索引,第二个是数据,第三个是数据长度。
您可以使用它来设置字符串
function init (args)
local needs = {}
needs["http.request_headers"] = tostring(true)
needs["flowvar"] = {"cnt"}
return needs
end
function match(args)
a = ScFlowvarGet(0);
if a then
a = tostring(tonumber(a)+1)
ScFlowvarSet(0, a, #a)
else
a = tostring(1)
ScFlowvarSet(0, a, #a)
end
2.12 Misc
2.12.1 SCThreadInfo
tid,tname,tgroup=SCThreadInfo()
它给出:tid(整数),tname(字符串),tgroup(字符串)
2.12.2 SCLogError, SCLogWarning, SCLogNotice, SCLogInfo, SCLogDebug
打印一条消息。它将进入yaml中定义的输出。是否打印取决于日志级别。
如:
SCLogError("some error message")
2.12.3 SCLogPath
公开日志路径
name = "fast_lua.log"
function setup (args)
filename = SCLogPath() .. "/" .. name
file = assert(io.open(filename, "a"))
end