TutorialΒΆ
In this tutorial a plugin which detects executable files .exe
requested over HTTP is created. This tutorial is step-by-step:
- A basic plugin is created to print URLs containing
.exe
to the command line is created - Created plugin is extended to create alerts and set a flow status
To create this plugin, ntopng sources are used. However, this is not a requirement. Plugins can be created also for packaged versions of ntopng. The directory which will contain plugin files is created under the ntopng plugins directory as
# Launch this command from the root directory of the ntopng sources tree
mkdir -p ./scripts/plugins/exes_download/
As this plugin detectes executable files .exe
, it must be run agains every HTTP flow. To run the plugin against every HTTP flow, a flow user script (see Flow User Scripts) must be placed under user_scripts/flows
:
mkdir -p ./scripts/plugins/exes_download/user_scripts/flow/
touch ./scripts/plugins/exes_download/user_scripts/flow/exes_download.lua
The file exes_download.lua
can then be edited as:
local user_scripts = require("user_scripts")
local script = {
-- Script category
category = user_scripts.script_categories.security,
-- This module is disabled by default
default_enabled = true,
-- See below
hooks = {},
-- Allow user script configuration from the GUI
gui = {
i18n_title = "EXEs Download",
i18n_description = "Detects .exe downloaded via HTTP",
}
}
-- #################################################################
-- Defines an hook which is executed every time a protocol of a flow is detected
function script.hooks.protocolDetected(now)
local http_info = flow.getHTTPInfo()
-- if the flow is HTTP and it contains a last_url...
if http_info and http_info["protos.http.last_url"] then
-- if an .exe is found in the URL...
if http_info["protos.http.last_url"]:match("%.exe") then
-- Prepare a text line to be printed to the console
local line = string.format("last_url: %s [%s]\n", http_info["protos.http.last_url"], shortFlowLabel(flow.getInfo()))
-- Actually print the line to the consol
io.write(line)
end
end
end
-- #################################################################
return script
The first line
local user_scripts = require("user_scripts")
Is necessary to specify a flow category in the script
table which must be returned at the end of the script. Indeed, the first key of this table is category
and has a value of user_scripts.script_categories.security
. Other categories are available in user_scripts.script_categories
. Failing to set a category would cause ntopng to choose a default category. The script
table then contains a boolean default_enabled = true
to make the user script enabled by default. This means ntopng will execute it and it will appear under the enabled flow user scripts in the web GUI. A table hooks = {}
is specified as well and is populated with function script.hooks.protocolDetected
. Finally a table gui
indicates a title and a description which will be shown under the flow user scripts of the ntopng web GUI.
The function function script.hooks.protocolDetected
gets executed every time the Layer-7 application protocol of a flow is detected (see Flow User Script Hooks). This function accesses the API with flow.getHTTPInfo()
to get flow HTTP data. If the flow is not HTTP, this table will be nil
. If not nil
, the protos.http.last_url
of the flow is read and a :match
regexp is used to search for the string .exe
in the URL. If found, a simple line is prepared and printed to the console with io.write
. At this point, the plugin is functional. Restart ntopng and try to fetch a URL with a .exe
: this will cause ntopng to print flow details and URL to the console.
To extend this plugin to generate alerts and flow statuses, two additional directories need to be created, for Flow Definitions and Alert Definitions, respectively:
mkdir -p ./scripts/plugins/exes_download/alert_definitions/
mkdir -p ./scripts/plugins/exes_download/status_definitions/
Then, an alert definition and a flow status definition are created with two files:
touch scripts/plugins/exes_download/status_definitions/status_exe_download.lua
touch scripts/plugins/exes_download/alert_definitions/alert_exe_download.lua
Set the alert definition file alert_exe_download.lua
contents as:
local alert_keys = require "alert_keys"
-- #######################################################
-- @brief Prepare an alert table used to generate the alert
-- @param alert_severity A severity as defined in `alert_consts.alert_severities`
-- @param tls_info A Lua table with HTTP info generated calling `flow.getHTTPInfo()`
-- @return A table with the alert built
local function createExeDownload(alert_severity, http_info)
local built = {
alert_severity = alert_severity,
alert_type_params = http_info -- This info will go into the alert JSON
}
return built
end
-- #######################################################
return {
alert_key = alert_keys.user.alert_user_01,
-- equivalent
-- alert_key = {0, alert_keys.user.alert_user_01},
-- custom pens
-- alert_key = {312 -- PEN -- , 513 --alert id --]]},
i18n_title = "EXE download",
icon = "fas fa-exclamation",
creator = createExeDownload,
}
The file contains the alert title and an icon which will be used by ntopng to print the alerts. As this is a user-developed plugin, and no other user-developed plugin is using it, key alert_keys.user.alert_user_01
is chosen as alert_key
. A createExeDownload
is implemented as well to add the detected HTTP information straight into the alert JSON.
Set the status definition file status_exe_download.lua
as:
local alert_consts = require("alert_consts")
local status_keys = require "flow_keys"
return {
status_key = status_keys.user.status_user_01,
alert_severity = alert_consts.alert_severities.error,
alert_type = alert_consts.alert_types.alert_exe_download,
i18n_title = "EXE download",
i18n_description = "Flow has downloaded an executable file",
}
The file contains a status title and a description which will be used by ntopng when showing the flow status. It also contains alert_severity
and alert_type
which tell ntopng the status is going to cause an alert of type alert_exe_download
to be triggered. As this is a user-developed plugin, and no other user-developed plugin is using it, key status_keys.user.status_user_01
is chosen as status_key
.
The final thing which is required to set the flow status and trigger the alert is to add an extra require to the user script
local flow_consts = require("flow_consts")
And modify function script.hooks.protocolDetected(now)
as follow:
-- Defines an hook which is executed every time a procotol of a flow is detected
function script.hooks.protocolDetected(now)
local http_info = flow.getHTTPInfo()
-- if the flow is HTTP and it contains a last_url...
if http_info and http_info["protos.http.last_url"] then
-- if an .exe is found in the URL...
if http_info["protos.http.last_url"]:match("%.exe") then
flow.triggerStatus(
flow_consts.status_types.status_exe_download.create(
flow_consts.status_types.status_exe_download.alert_severity,
http_info
),
100 --[[ flow_score --]],
100 --[[ cli_score ]],
10 --[[ srv_score]])
end
end
end
Basically, a new function flow.triggerStatus
is added. This function wants the result of a call to create
as first parameter. Function create
takes a severity and an http_info
as first and second parameters, respectively. These two parameters are be passed to function createExeDownload
created in the alert definition file above. Then flow.triggerStatus
takes thress scores which are added to the flow, client and server scores, respectively.
Now the plugin is fully functional and ready to set flow statuses and trigger alerts when it detects and .exe
file. English strings can be localized as described in Localization.