mirror of https://github.com/OISF/suricata
doc: add a lua support top level section
Both output and signature are using lua. So lua functions should be displayed in a single section.pull/3330/head
parent
293b00798e
commit
0c4bf2d332
@ -0,0 +1,7 @@
|
||||
Lua support
|
||||
===========
|
||||
|
||||
.. toctree::
|
||||
|
||||
lua-usage
|
||||
lua-functions
|
@ -0,0 +1,773 @@
|
||||
.. _lua-functions:
|
||||
|
||||
Lua functions
|
||||
=============
|
||||
|
||||
packet
|
||||
------
|
||||
|
||||
Initialize with:
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["type"] = "packet"
|
||||
return needs
|
||||
end
|
||||
|
||||
SCPacketTimestamp
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get packets timestamp as 2 numbers: seconds & microseconds elapsed since
|
||||
1970-01-01 00:00:00 UTC.
|
||||
|
||||
::
|
||||
|
||||
function log(args)
|
||||
local sec, usec = SCPacketTimestamp()
|
||||
end
|
||||
|
||||
SCPacketTimeString
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Add SCPacketTimeString to get the packets time string in the format:
|
||||
11/24/2009-18:57:25.179869
|
||||
|
||||
::
|
||||
|
||||
function log(args)
|
||||
ts = SCPacketTimeString()
|
||||
|
||||
SCPacketTuple
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
ipver, srcip, dstip, proto, sp, dp = SCPacketTuple()
|
||||
|
||||
SCPacketPayload
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
p = SCPacketPayload()
|
||||
|
||||
flow
|
||||
----
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["type"] = "flow"
|
||||
return needs
|
||||
end
|
||||
|
||||
SCFlowTimestamps
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Get timestamps (seconds and microseconds) of the first and the last packet from
|
||||
the flow.
|
||||
|
||||
::
|
||||
|
||||
startts, lastts = SCFlowTimestamps()
|
||||
startts_s, lastts_s, startts_us, lastts_us = SCFlowTimestamps()
|
||||
|
||||
SCFlowTimeString
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
startts = SCFlowTimeString()
|
||||
|
||||
SCFlowTuple
|
||||
~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
ipver, srcip, dstip, proto, sp, dp = SCFlowTuple()
|
||||
|
||||
SCFlowAppLayerProto
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get alprotos as string from the flow. If a alproto is not (yet) known, it
|
||||
returns "unknown".
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log(args)
|
||||
alproto = SCFlowAppLayerProto()
|
||||
if alproto ~= nil then
|
||||
print (alproto)
|
||||
end
|
||||
end
|
||||
|
||||
Returns 5 values: <alproto> <alproto_ts> <alproto_tc> <alproto_orig> <alproto_expect>
|
||||
|
||||
Orig and expect are used when changing and upgrading protocols. In a SMTP STARTTLS
|
||||
case, orig would normally be set to "smtp" and expect to "tls".
|
||||
|
||||
|
||||
SCFlowHasAlerts
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Returns true if flow has alerts.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log(args)
|
||||
has_alerts = SCFlowHasAlerts()
|
||||
if has_alerts then
|
||||
-- do something
|
||||
end
|
||||
end
|
||||
|
||||
SCFlowStats
|
||||
~~~~~~~~~~~
|
||||
|
||||
Gets the packet and byte counts per flow.
|
||||
|
||||
::
|
||||
|
||||
tscnt, tsbytes, tccnt, tcbytes = SCFlowStats()
|
||||
|
||||
SCFlowId
|
||||
~~~~~~~~
|
||||
|
||||
Gets the flow id.
|
||||
|
||||
::
|
||||
|
||||
id = SCFlowId()
|
||||
|
||||
Note that simply printing 'id' will likely result in printing a scientific
|
||||
notation. To avoid that, simply do:
|
||||
|
||||
::
|
||||
|
||||
id = SCFlowId()
|
||||
idstr = string.format("%.0f",id)
|
||||
print ("Flow ID: " .. idstr .. "\n")
|
||||
|
||||
|
||||
http
|
||||
----
|
||||
|
||||
Init with:
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["protocol"] = "http"
|
||||
return needs
|
||||
end
|
||||
|
||||
HttpGetRequestBody and HttpGetResponseBody.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Make normalized body data available to the script through
|
||||
HttpGetRequestBody and HttpGetResponseBody.
|
||||
|
||||
There no guarantees that all of the body will be availble.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log(args)
|
||||
a, o, e = HttpGetResponseBody();
|
||||
--print("offset " .. o .. " end " .. e)
|
||||
for n, v in ipairs(a) do
|
||||
print(v)
|
||||
end
|
||||
end
|
||||
|
||||
HttpGetRequestHost
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get the host from libhtp's tx->request_hostname, which can either be
|
||||
the host portion of the url or the host portion of the Host header.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
http_host = HttpGetRequestHost()
|
||||
if http_host == nil then
|
||||
http_host = "<hostname unknown>"
|
||||
end
|
||||
|
||||
HttpGetRequestHeader
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
http_ua = HttpGetRequestHeader("User-Agent")
|
||||
if http_ua == nil then
|
||||
http_ua = "<useragent unknown>"
|
||||
end
|
||||
|
||||
HttpGetResponseHeader
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
server = HttpGetResponseHeader("Server");
|
||||
print ("Server: " .. server);
|
||||
|
||||
HttpGetRequestLine
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
rl = HttpGetRequestLine();
|
||||
print ("Request Line: " .. rl);
|
||||
|
||||
HttpGetResponseLine
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
rsl = HttpGetResponseLine();
|
||||
print ("Response Line: " .. rsl);
|
||||
|
||||
HttpGetRawRequestHeaders
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
rh = HttpGetRawRequestHeaders();
|
||||
print ("Raw Request Headers: " .. rh);
|
||||
|
||||
HttpGetRawResponseHeaders
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
rh = HttpGetRawResponseHeaders();
|
||||
print ("Raw Response Headers: " .. rh);
|
||||
|
||||
HttpGetRequestUriRaw
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
http_uri = HttpGetRequestUriRaw()
|
||||
if http_uri == nil then
|
||||
http_uri = "<unknown>"
|
||||
end
|
||||
|
||||
HttpGetRequestUriNormalized
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
http_uri = HttpGetRequestUriNormalized()
|
||||
if http_uri == nil then
|
||||
http_uri = "<unknown>"
|
||||
end
|
||||
|
||||
HttpGetRequestHeaders
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
a = HttpGetRequestHeaders();
|
||||
for n, v in pairs(a) do
|
||||
print(n,v)
|
||||
end
|
||||
|
||||
HttpGetResponseHeaders
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
a = HttpGetResponseHeaders();
|
||||
for n, v in pairs(a) do
|
||||
print(n,v)
|
||||
end
|
||||
|
||||
DNS
|
||||
---
|
||||
|
||||
DnsGetQueries
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
dns_query = DnsGetQueries();
|
||||
if dns_query ~= nil then
|
||||
for n, t in pairs(dns_query) do
|
||||
rrname = t["rrname"]
|
||||
rrtype = t["type"]
|
||||
|
||||
print ("QUERY: " .. ts .. " " .. rrname .. " [**] " .. rrtype .. " [**] " ..
|
||||
"TODO" .. " [**] " .. srcip .. ":" .. sp .. " -> " ..
|
||||
dstip .. ":" .. dp)
|
||||
end
|
||||
end
|
||||
|
||||
returns a table of tables
|
||||
|
||||
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
|
||||
|
||||
returns a table of tables
|
||||
|
||||
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
|
||||
|
||||
returns a table of tables
|
||||
|
||||
DnsGetRcode
|
||||
~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
rcode = DnsGetRcode();
|
||||
if rcode == nil then
|
||||
return 0
|
||||
end
|
||||
print (rcode)
|
||||
|
||||
returns a lua string with the error message, or nil
|
||||
|
||||
DnsGetRecursionDesired
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
if DnsGetRecursionDesired() == true then
|
||||
print ("RECURSION DESIRED")
|
||||
end
|
||||
|
||||
returns a bool
|
||||
|
||||
TLS
|
||||
---
|
||||
|
||||
Initialize with:
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["protocol"] = "tls"
|
||||
return needs
|
||||
end
|
||||
|
||||
TlsGetCertInfo
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Make certificate information available to the script through TlsGetCertInfo.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
version, subject, issuer, fingerprint = TlsGetCertInfo()
|
||||
if version == nil then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
TlsGetCertChain
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Make certificate chain available to the script through TlsGetCertChain.
|
||||
|
||||
The output is an array of certificate with each certificate being an hash
|
||||
with `data` and `length` keys.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
-- 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
|
||||
|
||||
|
||||
TlsGetCertNotAfter
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get the Unix timestamp of end of validity of certificate.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
notafter = TlsGetCertNotAfter()
|
||||
if notafter < os.time() then
|
||||
-- expired certificate
|
||||
end
|
||||
end
|
||||
|
||||
TlsGetCertNotBefore
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get the Unix timestamp of beginning of validity of certificate.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
notbefore = TlsGetCertNotBefore()
|
||||
if notbefore > os.time() then
|
||||
-- not yet valid certificate
|
||||
end
|
||||
end
|
||||
|
||||
TlsGetCertSerial
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Get TLS certificate serial number through TlsGetCertSerial.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
serial = TlsGetCertSerial()
|
||||
if serial then
|
||||
-- do something
|
||||
end
|
||||
end
|
||||
|
||||
TlsGetSNI
|
||||
~~~~~~~~~
|
||||
|
||||
Get the Server name Indication from a TLS connection.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
asked_domain = TlsGetSNI()
|
||||
if string.find(asked_domain, "badguys") then
|
||||
-- ok connection to bad guys let's do someting
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
JA3
|
||||
---
|
||||
|
||||
JA3 must be enabled in the Suricata config file (set 'app-layer.protocols.tls.ja3-fingerprints' to 'yes').
|
||||
|
||||
Initialize with:
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["protocol"] = "tls"
|
||||
return needs
|
||||
end
|
||||
|
||||
Ja3GetHash
|
||||
~~~~~~~~~~
|
||||
|
||||
Get the JA3 hash (md5sum of JA3 string) through Ja3GetHash.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
hash = Ja3GetHash()
|
||||
if hash == nil then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
Ja3GetString
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Get the JA3 string through Ja3GetString.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
str = Ja3GetString()
|
||||
if str == nil then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
SSH
|
||||
---
|
||||
|
||||
Initialize with:
|
||||
|
||||
::
|
||||
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["protocol"] = "ssh"
|
||||
return needs
|
||||
end
|
||||
|
||||
SshGetServerProtoVersion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get SSH protocol version used by the server through SshGetServerProtoVersion.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
version = SshGetServerProtoVersion()
|
||||
if version == nil then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
SshGetServerSoftwareVersion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get SSH software used by the server through SshGetServerSoftwareVersion.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
|
||||
function log (args)
|
||||
software = SshGetServerSoftwareVersion()
|
||||
if software == nil then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
SshGetClientProtoVersion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get SSH protocol version used by the client through SshGetClientProtoVersion.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
version = SshGetClientProtoVersion()
|
||||
if version == nil then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
SshGetClientSoftwareVersion
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Get SSH software used by the client through SshGetClientSoftwareVersion.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
function log (args)
|
||||
software = SshGetClientSoftwareVersion()
|
||||
if software == nil then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
Files
|
||||
-----
|
||||
|
||||
To use the file logging API, the script's init() function needs to look like:
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs['type'] = 'file'
|
||||
return needs
|
||||
end
|
||||
|
||||
SCFileInfo
|
||||
~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
|
||||
fileid, txid, name, size, magic, md5 = SCFileInfo()
|
||||
|
||||
returns fileid (number), txid (number), name (string), size (number),
|
||||
magic (string), md5 in hex (string)
|
||||
|
||||
SCFileState
|
||||
~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
state, stored = SCFileState()
|
||||
|
||||
returns state (string), stored (bool)
|
||||
|
||||
Alerts
|
||||
------
|
||||
|
||||
Alerts are a subset of the 'packet' logger:
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["type"] = "packet"
|
||||
needs["filter"] = "alerts"
|
||||
return needs
|
||||
end
|
||||
|
||||
SCRuleIds
|
||||
~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
sid, rev, gid = SCRuleIds()
|
||||
|
||||
SCRuleMsg
|
||||
~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
msg = SCRuleMsg()
|
||||
|
||||
SCRuleClass
|
||||
~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
|
||||
class, prio = SCRuleClass()
|
||||
|
||||
Streaming Data
|
||||
--------------
|
||||
|
||||
Streaming data can currently log out reassembled TCP data and
|
||||
normalized HTTP data. The script will be invoked for each consecutive
|
||||
data chunk.
|
||||
|
||||
In case of TCP reassembled data, all possible overlaps are removed
|
||||
according to the host OS settings.
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["type"] = "streaming"
|
||||
needs["filter"] = "tcp"
|
||||
return needs
|
||||
end
|
||||
|
||||
In case of HTTP body data, the bodies are unzipped and dechunked if applicable.
|
||||
|
||||
::
|
||||
|
||||
function init (args)
|
||||
local needs = {}
|
||||
needs["type"] = "streaming"
|
||||
needs["protocol"] = "http"
|
||||
return needs
|
||||
end
|
||||
|
||||
SCStreamingBuffer
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
function log(args)
|
||||
data = SCStreamingBuffer()
|
||||
hex_dump(data)
|
||||
end
|
||||
|
||||
Misc
|
||||
----
|
||||
|
||||
SCThreadInfo
|
||||
~~~~~~~~~~~~
|
||||
|
||||
::
|
||||
|
||||
tid, tname, tgroup = SCThreadInfo()
|
||||
|
||||
It gives: tid (integer), tname (string), tgroup (string)
|
||||
|
||||
SCLogError, SCLogWarning, SCLogNotice, SCLogInfo, SCLogDebug
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Print a message. It will go into the outputs defined in the
|
||||
yaml. Whether it will be printed depends on the log level.
|
||||
|
||||
Example:
|
||||
|
||||
::
|
||||
|
||||
SCLogError("some error message")
|
||||
|
||||
SCLogPath
|
||||
~~~~~~~~~
|
||||
|
||||
Expose the log path.
|
||||
|
||||
::
|
||||
|
||||
|
||||
name = "fast_lua.log"
|
||||
function setup (args)
|
||||
filename = SCLogPath() .. "/" .. name
|
||||
file = assert(io.open(filename, "a"))
|
||||
end
|
@ -0,0 +1,17 @@
|
||||
Lua usage in Suricata
|
||||
=====================
|
||||
|
||||
Lua can be used in two components of Suricata. First one is output an the second one in signature.
|
||||
|
||||
Both features are using a list of functions to access to data extracted by Suricata. You can get the
|
||||
list of functions in the :ref:`lua-functions` page.
|
||||
|
||||
Lua output
|
||||
----------
|
||||
|
||||
Lua can be used to write arbitrary output. See :ref:`lua-output` for more information.
|
||||
|
||||
Lua signature
|
||||
-------------
|
||||
|
||||
Lua script can be used as a filter in signatures. See :ref:`lua-scripting` for more information.
|
Loading…
Reference in New Issue