ransomware/artifact.lua (3,044 lines of code) (raw):
--[[
Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
or more contributor license agreements. You may not install, use, modify,
or distribute this file unless you have a valid commercial license from
Elasticsearch B.V. or one of its affiliates. If you are interested in
obtaining Elastic's permission to use this file, please contact
elastic_license@elastic.co.
--]]
local utils = {}
-- ExtensionData creates an extension data table from the given arguments.
-- @param category int: Category of the extension.
-- @param lowEntropy boolean: True when the extension is known to have a low entropy.
-- @param magicBytes table: Represents the file magic bytes.
-- @return table: A table representing the extension data.
function utils.ExtensionData(category, lowEntropy, magicBytes)
local obj = {}
obj.category = category
obj.lowEntropy = lowEntropy
obj.magicBytes = magicBytes
return obj
end
-- Returns the hexadecimal representation of the binary data. Every byte of data
-- is converted into the corresponding 2-digit hex representation. The returned
-- bytes object is therefore twice as long as the length of data.
-- @param buff string: Represents the binary data.
-- @return string: hexlified string.
function utils.Hexlify(buff)
local t = {}
for i = 1, #buff do
table.insert(t, string.format('%02X', string.byte(string.sub(buff, i, i))))
end
return table.concat(t, '')
end
-- Split a string by a separator.
-- @param str string: The subject string.
-- @param sep string: The separator used to split the string.
-- @return table: A table representing the split string.
function utils.Split(str, sep)
local result = {}
local regex = ('([^%s]+)'):format(sep)
for each in str:gmatch(regex) do
table.insert(result, each)
end
return result
end
-- Returns a boolean representing if key exists in the provided table.
-- @param inputTable table: The subject table.
-- @param key string: The key to check for.
-- @return boolean: True if the key exists in the table.
function utils.TableHasKey(inputTable, key)
return inputTable[key] ~= nil
end
-- Returns a boolean representing if value exists in the provided table.
-- @param inputTable table: The subject table.
-- @param value string: The value to check for.
-- @return boolean: True if the value exists in the table.
function utils.TableHasValue(inputTable, value)
for _, v in ipairs(inputTable) do
if v == value then
return true
end
end
return false
end
-- Performs a deep copy of an object. This function supports: tables as keys,
-- recursive tables, and preserves meta-tables.
-- @param obj table: The subject table.
-- @param seen table: A table meant to be ignored by the "outside" caller and
-- only used for recursive calls. It avoids repeated deep copying of tables that
-- occur more than once in a single table.
-- @param Table table: The newly copied table.
function utils.Copy(obj, seen)
if type(obj) ~= 'table' then
return obj
end
if seen and seen[obj] then
return seen[obj]
end
local s = seen or {}
local res = setmetatable({}, getmetatable(obj))
s[obj] = res
for k, v in pairs(obj) do
res[utils.Copy(k, s)] = utils.Copy(v, s)
end
return res
end
-- Converts a byte string to an array of bytes.
-- @param str string: The subject string.
-- @return table: A table containing the resulted array of bytes.
function utils.StringToByteArray(str)
local result = {}
for i = 1, #str do
table.insert(result, string.byte(str, i));
end
return result
end
-- Removes alternate data stream (if exists) from the provided file path,
-- e.g. C:\test.txt:Zone.Identifier => C:\test.txt.
-- @param str string: The subject file path.
-- @return string: The curated string if ADS is found. Otherwise, a copy of the string.
function utils.RemoveAdsFromPath(str)
local volumeIndex = string.find(str, ':', nil, true)
-- If we confirm the second character was a colon (e.g. 'C:'), then
-- proceed with attempting to remove an ADS from the remaining path.
if volumeIndex ~= nil then
if volumeIndex == 2 then
return utils.RemoveAdsFromExtension(str, volumeIndex + 1)
end
end
return str
end
-- Removes alternate data stream (if exists) from the provided filename,
-- extension, or partial path. e.g. test.txt:Zone.Identifier => test.txt
-- @param str string: The subject file name.
-- @param startIndex integer: An index to start the search from.
-- @return string: The curated string if ADS is found. Otherwise, a copy of the string.
function utils.RemoveAdsFromExtension(str, startIndex)
startIndex = startIndex or 1
local adsIndex = string.find(str, ':', startIndex, true)
if adsIndex ~= nil then
return string.sub(str, startIndex, adsIndex - 1)
end
return str
end
-- NormalizePath attempts to find the parent of a directory but in terms of
-- responsibility. This is useful when we want to know processes that
-- attempt to modify directories outside of their scope. i.g:
-- c:\\program files\\google\\sdk\\list.py ==> c:\\program files\\google\\
-- Any process which modifies files outside its scope it considered suspicious.
-- @param str string: The subject file path.
-- @return string: The normalized file path in success, or nil otherwise.
function utils.NormalizePath(filePath)
-- Lower case the file path for easy pattern matching.
filePath = filePath:lower()
-- "Program Files" and "Program Files (x86) can use the shorter version,
-- that is "progra~1" and "progra~2" correspondingly.
filePath = filePath:gsub('progra~1', 'program files')
filePath = filePath:gsub('progra~2', 'program files (x86)')
-- If the file path does not start with a drive letter, abort.
local match = filePath:match('^%a:\\')
if match == nil then
return nil
end
local match = filePath:match('^%a:\\programdata\\.-\\')
if match ~= nil then
return match
end
match = filePath:match('^%a:\\program files\\.-\\')
if match ~= nil then
return match
end
match = filePath:match('^%a:\\program files %(x86%)\\.-\\')
if match ~= nil then
return match
end
match = filePath:match('^%a:\\.-\\')
if match ~= nil then
return match
end
match = filePath:match('(.*[/\\])')
if match ~= nil then
return match
end
return nil
end
-- Check if a file extension is typically used by Microsoft Office, the first
-- two characters of the filename will be checked to determine if this may be
-- an Office lock file and will not be subjected to a header magic byte check.
-- @param extension string: The subject extension name.
-- @param fileName string: The subject file name.
-- @return boolean: True when an MS Office lock file is found. False otherwise.
function utils.IsOfficeLockFile(extension, fileName)
-- A list of known Microsoft Office Extensions.
local officeExtensions = {
'doc',
'docb',
'docm',
'docx',
'dotm',
'dotx',
'dot',
'wbk',
'pot',
'potm',
'potx',
'ppam',
'pps',
'ppsm',
'ppsx',
'ppt',
'pptm',
'pptx',
'sldm',
'sldx',
'xla',
'xlam',
'xll',
'xlm',
'xls',
'xlsb',
'xlsm',
'xlsx',
'xlt',
'xltm',
'xltx',
'xlw'
}
if utils.TableHasValue(officeExtensions, extension) then
local index = string.find(fileName, '~$', nil, true)
if index ~= nil then
if index == 1 then
return true
end
end
end
return false
end
-- Prints out tables summarizing process event activity sorted by operations and
-- extensions.
-- @param processData table: A table containing process data.
-- @return void.
function utils.PrintExtensionTables(processData)
utils.DebugLog('=================')
utils.DebugLog('Create Extensions')
for k, v in pairs(processData.createExtensions) do
utils.DebugLog('*** ' .. k)
for _, v2 in pairs(v) do
utils.DebugLog(v2.operation .. ' | ' .. string.sub(v2.entropy, 1, 4) .. ' | ' .. v2.alertScore .. ' | ' ..
string.sub(v2.filePath, 1, 200))
end
end
utils.DebugLog('=================')
utils.DebugLog('Modify Extensions')
for k, v in pairs(processData.modifyExtensions) do
utils.DebugLog('*** ' .. k)
for _, v2 in pairs(v) do
utils.DebugLog(v2.operation .. ' | ' .. string.sub(v2.entropy, 1, 4) .. ' | ' .. v2.alertScore .. ' | ' ..
string.sub(v2.filePath, 1, 200))
end
end
utils.DebugLog('=================')
utils.DebugLog('Delete Extensions')
for k, v in pairs(processData.deleteExtensions) do
utils.DebugLog('*** ' .. k)
for _, v2 in pairs(v) do
utils.DebugLog(v2.operation .. ' | ' .. string.sub(v2.entropy, 1, 4) .. ' | ' .. v2.alertScore .. ' | ' ..
string.sub(v2.filePath, 1, 200))
end
end
utils.DebugLog('=================')
utils.DebugLog('Rename Extensions')
for k, v in pairs(processData.renameExtensions) do
utils.DebugLog('*** ' .. k)
for _, v2 in pairs(v) do
utils.DebugLog(v2.operation .. ' | ' .. string.sub(v2.entropy, 1, 4) .. ' | ' .. v2.alertScore .. ' | ' ..
string.sub(v2.filePath, 1, 200))
end
end
utils.DebugLog('=================')
utils.DebugLog('Overwrite Extensions')
for k, v in pairs(processData.overwriteExtensions) do
utils.DebugLog('*** ' .. k)
for _, v2 in pairs(v) do
utils.DebugLog(v2.operation .. ' | ' .. string.sub(v2.entropy, 1, 4) .. ' | ' .. v2.alertScore .. ' | ' ..
string.sub(v2.filePath, 1, 200))
end
end
utils.DebugLog('=================')
utils.DebugLog('headerMismatchExtensions')
for k, v in pairs(processData.headerMismatchExtensions) do
utils.DebugLog('*** ' .. k)
end
utils.DebugLog('=================')
utils.DebugLog('entropyMismatchExtensions')
for k, v in pairs(processData.entropyMismatchExtensions) do
utils.DebugLog('*** ' .. k .. ' : ' .. v)
end
end
-- Provides a quick string summary of a process activity by tallying the different
-- types of file operations.
-- @param processData table: A table containing process data.
-- @return void.
function utils.PrintOperationTables(processData)
local creates = 0
local modifies = 0
local deletes = 0
local renames = 0
local overwrites = 0
for _, v in pairs(processData.createExtensions) do
creates = creates + #v
end
for _, v in pairs(processData.modifyExtensions) do
for _, _ in pairs(v) do
modifies = modifies + 1
end
end
for _, v in pairs(processData.deleteExtensions) do
deletes = deletes + #v
end
for _, v in pairs(processData.renameExtensions) do
renames = renames + #v
end
for _, v in pairs(processData.overwriteExtensions) do
overwrites = overwrites + #v
end
local operationString =
'PID: ' .. processData.processId .. ' Creates: ' .. creates .. ' | Modifies: ' .. modifies .. ' | Deletes: ' ..
deletes .. ' | Renames: ' .. renames .. ' | Overwrites: ' .. overwrites
return operationString
end
-- Prints a lua table. This function supports printing nested tables.
-- @param t Table: The table subject for printing.
-- @return: void.
function utils.PrintTable(t)
local printTable_cache = {}
local function sub_printTable(t, indent)
if (printTable_cache[tostring(t)]) then
utils.DebugLog(indent .. '*' .. tostring(t))
else
printTable_cache[tostring(t)] = true
if (type(t) == 'table') then
for pos, val in pairs(t) do
if (type(val) == 'table') then
utils.DebugLog(indent .. '[' .. pos .. '] => ' .. tostring(t) .. ' {')
sub_printTable(val, indent .. string.rep(' ', string.len(pos) + 8))
utils.DebugLog(indent .. string.rep(' ', string.len(pos) + 6) .. '}')
elseif (type(val) == 'string') then
utils.DebugLog(indent .. '[' .. pos .. '] => "' .. val .. '"')
else
utils.DebugLog(indent .. '[' .. pos .. '] => ' .. tostring(val))
end
end
else
utils.DebugLog(indent .. tostring(t))
end
end
end
if (type(t) == 'table') then
utils.DebugLog(tostring(t) .. ' {')
sub_printTable(t, ' ')
utils.DebugLog('}')
else
sub_printTable(t, ' ')
end
end
------------------------------------------------------------------------------
-- The functions below are wrappers over functions that are called directly by
-- the sensor, when the lua module is used outside of the endpoint, the `mock`
-- module implements the required functions to mock.
------------------------------------------------------------------
-- Wrapper function around llog.
-- @param str string: The subject file path.
-- @return void.
function utils.DebugLog(str)
-- TODO decouple logging.
if globals.logging then
-- llog(globals.namespace.nameString .. ': ' .. str)
llog(str)
end
end
-- Get the list of all user profiles.
-- @return table: A table representing the list of user profiles.
function utils.GetAllUserProfiles()
local results = {}
-- FOLDERID_UserProfiles.
local usersDir = GetKnownFolderPath('{0762D272-C50A-4BB0-A382-697DCD729B80}')
local users = ListDir(usersDir)
for _, f in ipairs(users) do
if f.Type == 'DIR' then
table.insert(results, f.Path)
end
end
return results
end
-- Determines what product is in use.
-- @return string: a string representing the current product: elastic or endgame.
function utils.GetProduct()
-- check if we can resolve lproduct() func.
if lproduct ~= nil then
-- Check which product is in use.
return lproduct()
else
-- lproduct will be nil *only* if we are running an endgame sensor.
return 'endgame'
end
end
-- Check if the actual sensor version is less than the provided version string.
-- @param targetVersion string: The targeted version.
-- @return boolean: True or false for properly formatted strings (ex. major.minor.release),
-- or false on formatting errors,
-- or true if lversion is nil.
function utils.CurrentVersionLessThan(targetVersion)
if not utils.IsVersionAvailable() then
return true
end
local currentVersion = lversion('sensor')
-- Validate parameters.
if (not currentVersion) or (not targetVersion) then
return false
end
-- Grab the pieces.
local currentMajor, currentMinor, currentRelease
local targetMajor, targetMinor, targetRelease
_, _, currentMajor, currentMinor, currentRelease = string.find(currentVersion, '(%d+)%.(%d+)%.(%d+)')
_, _, targetMajor, targetMinor, targetRelease = string.find(targetVersion, '(%d+)%.(%d+)%.(%d+)')
-- Validate major version parsing.
if (not currentMajor) or (not targetMajor) then
return false
end
-- Compare major versions.
if (currentMajor < targetMajor) then
return true
end
if (currentMajor == targetMajor) then
-- Validate minor version parsing.
if (not currentMinor) or (not targetMinor) then
return false
end
-- Compare minor versions.
if (currentMinor < targetMinor) then
return true
end
if (currentMinor == targetMinor) then
-- Validate release version parsing.
if (not currentRelease) or (not targetRelease) then
return false
end
-- Compare release versions.
if (currentRelease < targetRelease) then
return true
end
end
end
return false
end
-- Determines if lversion function is available for use in lua. lversion will
-- be nil only if we are running sensor version 3.53 or lower.
-- @return boolean: True if lversion is nil. False otherwise.
function utils.IsVersionAvailable()
if lversion ~= nil then
return true
else
return false
end
end
local alert = {
-- Limits the number of diagnostic alerts generated.
DIAGNOSTIC_ALERT_CAP = 10,
-- Mapping between file operations and their string representation.
FILE_OP_STR_MAP = {'creation', 'modification', 'deletion', 'rename', 'overwrite', 'open'}
}
-- Inserts the input alert metric into the list of event data alert metrics.
-- @param eventData table: A table containing event data.
-- @param alertMetric string: The subject alert name.
-- @return table: A table representing the new event data alert metrics.
function alert.RaiseFileAlertMetric(eventData, alertMetric)
if not utils.TableHasKey(eventData.alertMetrics, alertMetric) then
table.insert(eventData.alertMetrics, alertMetric)
end
return eventData.alertMetrics
end
-- Handle alert generation by passing a table to the sensor callback via `lemit`.
-- @param alertProcessData table: A table containing alert process data.
-- @param isDiagnostic boolean: A boolean that indicates whether or not this is
-- a designated diagnostic alert.
-- @return boolean: True in every case. TODO: fix possible return values.
function alert.GenerateAlert(alertProcessData, isDiagnostic)
local processTable = {}
local product = utils.GetProduct()
if product == nil or product == '' then
-- GetProduct() will return "endgame" if import isn't found and
-- lproduct() will always return a value, so if this is the case we're
-- in undefined behavior territory and should bail.
utils.DebugLog('Error collecting product information via GetProduct()')
return true
end
if isDiagnostic and globals.namespace.totalAlerts >= alert.DIAGNOSTIC_ALERT_CAP then
-- globals.alertGenerated = true
utils.DebugLog('alert.DIAGNOSTIC_ALERT_CAP REACHED! alert will not be generated for PID: ' ..
alertProcessData.processId)
return true
end
if isDiagnostic and alertProcessData.diagnosticAlertQueued then
utils.DebugLog('FINALLY generate our DIAGNOSTIC alert!')
-- set boolean to false to avoid duplicate diagnostic alerts
alertProcessData.diagnosticAlertQueued = false
elseif isDiagnostic and alertProcessData.diagnosticAlerted then
utils.DebugLog('PREVIOUSLY DIAGNOSTIC ALERTED ON THIS PROCESS!')
return true
elseif false == alertProcessData.activeAnalysis then
utils.DebugLog('Process no longer subject to active analysis')
return true
elseif true == alertProcessData.alerted then
utils.DebugLog('Previously alerted on this process in this namespace')
return true
end
if nil ~= alertProcessData.createExtensions then
utils.PrintExtensionTables(alertProcessData)
utils.PrintOperationTables(alertProcessData)
end
-- Set fields shared between endgame and elastic.
processTable.pid = alertProcessData.processId
processTable.is_alert = true
processTable.score = alertProcessData.totalScore
processTable.alert_files = {}
if isDiagnostic then
utils.DebugLog('DIAGNOSTIC ALERT: ' .. alertProcessData.processId)
alertProcessData.diagnosticAlerted = true
-- Endpoint/sensor still use 'beta_alert' key.
processTable.beta_alert = true
else
alertProcessData.activeAnalysis = false
alertProcessData.alerted = true
-- Endpoint/sensor still use 'beta_alert' key.
processTable.beta_alert = false
end
-- Emit alert in specific schema for corresponding product in use.
if product == 'endgame' then
alert.GenerateEndgameAlert(processTable, alertProcessData)
elseif product == 'elastic' then
processTable.canary_alert = alertProcessData.canary_alert
-- Add in RansomwareChildProcesses if present.
if nil ~= alertProcessData.child_processes then
processTable.child_processes = alertProcessData.child_processes
end
alert.GenerateElasticAlert(processTable, alertProcessData)
end
lemit(processTable)
globals.alertGenerated = true
globals.namespace.totalAlerts = globals.namespace.totalAlerts + 1
utils.DebugLog('namespace.totalAlerts: ' .. globals.namespace.totalAlerts)
return true
end
-- Generate an alert in the old Endgame schema.
-- @param processData table: A table containing process data.
-- @param alertProcessData table: A table containing alert process data.
-- @return void.
function alert.GenerateEndgameAlert(processTable, alertProcessData)
local tempMessage = {}
local incompatible = false
-- Set Endgame specific fields.
processTable.file_list = {}
processTable.process_alerts = {'PROCESS_LUA_ALERT'}
-- Check compatibility.
incompatible = utils.CurrentVersionLessThan('3.54.0')
for _, v in pairs(alertProcessData.events) do
tempMessage = {}
tempMessage.file_path = v.filePath
if not incompatible then
-- 3.54 and greater sensors are compatible with new changes;
-- schema changes and proper use of alert_files messages for extended
-- alert data needed for triage and event trace replay.
table.insert(processTable.file_list, tempMessage)
tempMessage = {}
tempMessage.file_path = v.filePath
tempMessage.score = v.alertScore
tempMessage.entropy = v.entropy
tempMessage.file_extension = v.fileExtension
tempMessage.bk_file_operation = v.operation
tempMessage.file_alerts = {}
tempMessage.header_string = v.headerString
for _, v2 in pairs(v.alertMetrics) do
table.insert(tempMessage.file_alerts, v2)
end
if utils.FILE_RENAME == v.operation then
tempMessage.file_previous_path = v.filePreviousPath
tempMessage.file_previous_extension = v.filePreviousExtension
end
table.insert(processTable.alert_files, tempMessage)
elseif incompatible then
-- Maintain clean filepath entries for 3.53.
table.insert(processTable.file_list, tempMessage)
end
end
-- Hacky version left in to support sending extended triage data for
-- 3.53 (since we didn't parse alert_files entries in the sensor).
if incompatible then
for _, v in pairs(alertProcessData.events) do
tempMessage = {}
tempMessage.file_path = v.fileName .. ' | ' .. v.alertScore .. ' | ' .. v.entropy .. ' | ' .. v.headerString
table.insert(processTable.file_list, tempMessage)
end
for _, v in pairs(alertProcessData.events) do
tempMessage = {}
tempMessage.file_path = v.fileName
for _, v2 in pairs(v.alertMetrics) do
tempMessage.file_path = tempMessage.file_path .. '|' .. v2
end
tempMessage.file_path = tempMessage.file_path .. ' | ' .. v.operation + .0
if utils.FILE_RENAME == v.operation then
tempMessage.file_path = tempMessage.file_path .. ' | ' .. v.filePreviousPath
end
table.insert(processTable.file_list, tempMessage)
end
end
end
-- Generates an alert in elastic ECS schema.
-- @param processData table: A table containing process data.
-- @param alertProcessData table: A table containing alert process data.
-- @return void.
function alert.GenerateElasticAlert(processTable, alertProcessData)
local tempMessage = {}
for _, v in pairs(alertProcessData.events) do
-- Output data in ECS Schema format
-- files :
-- fields :
-- operation :
-- entropy :
-- metrics :
-- extension :
-- original.path :
-- original.extension :
-- path :
-- data :
-- score :
tempMessage = {}
tempMessage.path = v.filePath
tempMessage.score = v.alertScore
tempMessage.entropy = v.entropy
tempMessage.extension = v.fileExtension
tempMessage.data = v.headerString
-- Lua arrays start from 1 so add 1 to correctly index the specified
-- file operation to string.
if nil ~= alert.FILE_OP_STR_MAP[v.operation + 1] then
tempMessage.operation = alert.FILE_OP_STR_MAP[v.operation + 1]
end
local metricsCount = 0
for _, v2 in pairs(v.alertMetrics) do
if 0 == metricsCount then
tempMessage.metrics = {}
end
table.insert(tempMessage.metrics, v2)
metricsCount = metricsCount + 1
end
if utils.FILE_RENAME == v.operation then
tempMessage.original = {}
tempMessage.original['path'] = v.filePreviousPath
tempMessage.original['extension'] = v.filePreviousExtension
end
-- As the endpoint doesn't parse alert_files (but rather pulls the
-- array out by the key) we can leave this the same and re-append
-- as new ECS `files` field in the endpoint.
table.insert(processTable.alert_files, tempMessage)
end
end
_G.globals = {}
globals.logging = false
globals.alertGenerated = false
globals.namespaces = {}
globals.config = {}
-- should always start as false
globals.diagnosticCanariesDropped = false
globals.productionCanariesDropped = false
globals.diagnosticStartupInvoked = false
globals.productionStartupInvoked = false
-- is this running on elastic 8.6.0 or newer?
globals.canaryCompatible = false
-- limit canary cleanup failure alerts
globals.bCanaryDiagnosticsEmitted = false
-- namespace is used to reference the current namespace while we are in the globals scope.
globals.namespace = nil
-- default metric values.
globals.config["ABNORMAL_EXTENSION_CHARACTERS"] = {}
globals.config["ABNORMAL_EXTENSION_CHARACTERS"]["score"] = 0.1
globals.config["CREATE_EXTENSION_KNOWN_HEADER_MISMATCH_WITH_PREVIOUSLY_DELETED_SUBSTRING"] = {}
globals.config["CREATE_EXTENSION_KNOWN_HEADER_MISMATCH_WITH_PREVIOUSLY_DELETED_SUBSTRING"]["score"] = 1.0
globals.config["CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN"] = {}
globals.config["CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN"]["score"] = 0.002
globals.config["CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED"] = {}
globals.config["CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED"]["score"] = 0.02
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN"] = {}
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN"]["score"] = 0.005
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED"] = {}
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED"]["score"] = 0.03
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_AVERAGE"] = {}
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_AVERAGE"]["score"] = 0.1
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGH"] = {}
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGH"]["score"] = 0.15
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHER"] = {}
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHER"]["score"] = 0.2
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHEST"] = {}
globals.config["CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHEST"]["score"] = 0.5
globals.config["CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGH"] = {}
globals.config["CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGH"]["score"] = 0.05
globals.config["CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHER"] = {}
globals.config["CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHER"]["score"] = 0.15
globals.config["CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHEST"] = {}
globals.config["CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHEST"]["score"] = 0.25
globals.config["DELETE_EXTENSION_BLOCKLIST_PREVIOUSLY_CREATED_FILEPATH"] = {}
globals.config["DELETE_EXTENSION_BLOCKLIST_PREVIOUSLY_CREATED_FILEPATH"]["score"] = 0.75
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGH"] = {}
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGH"]["score"] = 0.4
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHER"] = {}
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHER"]["score"] = 0.5
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHEST"] = {}
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHEST"]["score"] = 0.6
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH"] = {}
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH"]["score"] = 0.3
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER"] = {}
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER"]["score"] = 0.4
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST"] = {}
globals.config["DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST"]["score"] = 0.5
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH"] = {}
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH"]["score"] = 0.3
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER"] = {}
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER"]["score"] = 0.4
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST"] = {}
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST"]["score"] = 0.5
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING"] = {}
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING"]["score"] = 0.1
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGH"] = {}
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGH"]["score"] = 0.2
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHER"] = {}
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHER"]["score"] = 0.3
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHEST"] = {}
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHEST"]["score"] = 0.4
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN"] = {}
globals.config["DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN"]["score"] = 0.1
globals.config["ENTROPY_HIGHER"] = {}
globals.config["ENTROPY_HIGHER"]["score"] = 0.05
globals.config["ENTROPY_HIGHER_EXTENSION_UNKNOWN"] = {}
globals.config["ENTROPY_HIGHER_EXTENSION_UNKNOWN"]["score"] = 0.05
globals.config["ENTROPY_MISMATCH_HIGHER"] = {}
globals.config["ENTROPY_MISMATCH_HIGHER"]["score"] = 0.5
globals.config["ENTROPY_MISMATCH_HIGHER_WITH_HEADER_MISMATCH"] = {}
globals.config["ENTROPY_MISMATCH_HIGHER_WITH_HEADER_MISMATCH"]["score"] = 0.5
globals.config["ENTROPY_MISMATCH_HIGHEST"] = {}
globals.config["ENTROPY_MISMATCH_HIGHEST"]["score"] = 0.75
globals.config["ENTROPY_MISMATCH_HIGHEST_WITH_HEADER_MISMATCH"] = {}
globals.config["ENTROPY_MISMATCH_HIGHEST_WITH_HEADER_MISMATCH"]["score"] = 1.0
globals.config["EXTENSION_BLOCKLIST"] = {}
globals.config["EXTENSION_BLOCKLIST"]["score"] = 0.4
globals.config["HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET"] = {}
globals.config["HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET"]["score"] = 0.3
globals.config["PREVIOUS_HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET"] = {}
globals.config["PREVIOUS_HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET"]["score"] = 0.2
globals.config["RENAME_ENTROPY_MISMATCH_HIGHER"] = {}
globals.config["RENAME_ENTROPY_MISMATCH_HIGHER"]["score"] = 0.3
globals.config["RENAME_ENTROPY_MISMATCH_HIGHEST"] = {}
globals.config["RENAME_ENTROPY_MISMATCH_HIGHEST"]["score"] = 0.4
globals.config["RENAME_EXTENSION_KNOWN_TO_BLANK"] = {}
globals.config["RENAME_EXTENSION_KNOWN_TO_BLANK"]["score"] = 0.002
globals.config["RENAME_EXTENSION_KNOWN_TO_BLOCKLIST"] = {}
globals.config["RENAME_EXTENSION_KNOWN_TO_BLOCKLIST"]["score"] = 0.4
globals.config["RENAME_EXTENSION_KNOWN_TO_UNKNOWN"] = {}
globals.config["RENAME_EXTENSION_KNOWN_TO_UNKNOWN"]["score"] = 0.0025
globals.config["RENAME_EXTENSION_KNOWN_TO_UNKNOWN_MULTIPLE"] = {}
globals.config["RENAME_EXTENSION_KNOWN_TO_UNKNOWN_MULTIPLE"]["score"] = 0.005
globals.config["RENAME_EXTENSION_UNKNOWN_TO_BLOCKLIST"] = {}
globals.config["RENAME_EXTENSION_UNKNOWN_TO_BLOCKLIST"]["score"] = 0.3
globals.config["RENAME_EXTENSION_UNKNOWN_TO_UNKNOWN"] = {}
globals.config["RENAME_EXTENSION_UNKNOWN_TO_UNKNOWN"]["score"] = 0.01
globals.config["SUBEXTENSION_KNOWN"] = {}
globals.config["SUBEXTENSION_KNOWN"]["score"] = 0.003
globals.config["SUBEXTENSION_KNOWN_EXTENSION_UNKNOWN"] = {}
globals.config["SUBEXTENSION_KNOWN_EXTENSION_UNKNOWN"]["score"] = 0.0015
globals.config["SUBEXTENSION_UNKNOWN_AND_PREVIOUSLY_DELETED"] = {}
globals.config["SUBEXTENSION_UNKNOWN_AND_PREVIOUSLY_DELETED"]["score"] = 0.005
globals.config["TREND_SCORE_DELETE_CREATE_RATIO"] = {}
globals.config["TREND_SCORE_DELETE_CREATE_RATIO"]["score"] = 0.01
globals.config["TREND_SCORE_MORE_CREATES_THAN_DELETES"] = {}
globals.config["TREND_SCORE_MORE_CREATES_THAN_DELETES"]["score"] = 2.0
globals.config["TREND_SCORE_RENAME_EXTENSION_RATIO"] = {}
globals.config["TREND_SCORE_RENAME_EXTENSION_RATIO"]["score"] = 0.01
globals.config["TREND_SCORE_NUM_RENAMES"] = {}
globals.config["TREND_SCORE_NUM_RENAMES"]["score"] = 0.01
globals.config["TREND_SCORE_SINGLE_PREV_RENAME_EXTENSION"] = {}
globals.config["TREND_SCORE_SINGLE_PREV_RENAME_EXTENSION"]["score"] = 0.01
globals.INVALID_PROCESS_ID = 1
globals.PROCESS_EVENT_THRESHOLD = 200
globals.PROCESS_EXTENDED_EVENT_THRESHOLD = 400
globals.PROCESS_FINAL_EXTENDED_EVENT_THRESHOLD = 650
globals.PROCESS_TREND_FLOOR = 50
globals.PROCESS_ALERT_SCORE_THRESHOLD = 30.0
globals.PROCESS_PARENT_CHILD_ALERT_SCORE_THRESHOLD = 100.0
-- limits the number of diagnostic alerts generated
globals.DIAGNOSTIC_ALERT_CAP = 10
globals.CANARY_CREATE_FILE_ALERT_CAP = 5
globals.MAX_CHILD_PROCESSES = 5
-- TODO: refactor these RENAME globals to be paired with equivalent string mapping
globals.DEFAULT_RENAME = 0
globals.KNOWN_TO_SUSPICIOUS = 1
globals.KNOWN_TO_UNKNOWN = 2
globals.KNOWN_TO_BLANK = 3
globals.UNKNOWN_TO_SUSPICIOUS = 4
globals.UNKNOWN_TO_UNKNOWN = 5
globals.ENTROPY_REALLY_HIGH = 7.9
globals.ENTROPY_VERY_HIGH = 7.5
globals.ENTROPY_HIGH = 7.0
globals.FILE_CREATE_NEW = 0
globals.FILE_MODIFY = 1
globals.FILE_DELETE = 2
globals.FILE_RENAME = 3
globals.FILE_OVERWRITE = 4
globals.FILE_OPEN = 5
globals.fileOperationStringMappings = {'creation', 'modification', 'deletion', 'rename', 'overwrite', 'open'}
-- TODO: refactor the ENTROPY_STATUS_* globals along with string mapping like seen below
-- TODO x 2: refactor the parallel variables between these entries and
-- ENTROPY_REALLY_HIGH, ENTROPY_VERY_HIGH, ENTROPY_HIGH to avoid code
-- duplication and simplify code maintenance
-- globals.ENTROPY_STATUS_DEFAULT = 0
-- globals.ENTROPY_STATUS_HIGH = 1
-- globals.ENTROPY_STATUS_VERY_HIGH = 2
-- globals.ENTROPY_STATUS_REALLY_HIGH = 3
-- globals.ENTROPY_STATUS_MISMATCH_VERY_HIGH = 4
-- globals.ENTROPY_STATUS_MISMATCH_REALLY_HIGH = 5
--
-- globals.ENTROPY_STATUS_TO_STRING = {
-- [globals.ENTROPY_STATUS_DEFAULT]='ENTROPY_DEFAULT',
-- [globals.ENTROPY_STATUS_HIGH]='ENTROPY_HIGH',
-- [globals.ENTROPY_STATUS_VERY_HIGH]='ENTROPY_VERY_HIGH',
-- [globals.ENTROPY_STATUS_REALLY_HIGH]='ENTROPY_REALLY_HIGH',
-- [globals.ENTROPY_STATUS_MISMATCH_VERY_HIGH]='ENTROPY_MISMATCH_VERY_HIGH',
-- [globals.ENTROPY_STATUS_MISMATCH_REALLY_HIGH]='ENTROPY_MISMATCH_REALLY_HIGH',
-- }
globals.HEADER_MISMATCH_THRESHOLD = 5
globals.ENTROPY_MISMATCH_THRESHOLD = 5
globals.ENTROPY_STATUS_DEFAULT = 0
globals.ENTROPY_STATUS_HIGH = 1
globals.ENTROPY_STATUS_VERY_HIGH = 2
globals.ENTROPY_STATUS_REALLY_HIGH = 3
globals.ENTROPY_STATUS_MISMATCH_VERY_HIGH = 4
globals.ENTROPY_STATUS_MISMATCH_REALLY_HIGH = 5
-- table of file paths to ignore when processing file events.
globals.regexIgnorePaths = {
'^[a-z]:\\\\users\\\\.*\\\\appdata\\\\',
'^[a-z]:\\\\users\\\\.*\\\\downloads\\\\',
'^[a-z]:\\\\windows\\\\logs\\\\',
'^[a-z]:\\\\windows\\\\ccm\\\\',
'^[a-z]:\\\\windows\\\\csc\\\\',
'^[a-z]:\\\\windows\\\\ccmcache\\\\',
'^[a-z]:\\\\windows\\\\temp\\\\',
'^[a-z]:\\\\windows\\\\softwaredistribution\\\\',
'^[a-z]:\\\\windows\\\\prefetch\\\\',
'^[a-z]:\\\\windows\\\\installer\\\\',
'^[a-z]:\\\\windows\\\\rescache\\\\',
'^[a-z]:\\\\windows\\\\winsxs\\\\',
'^[a-z]:\\\\windows\\\\appcompat\\\\',
'^[a-z]:\\\\windows\\\\system32\\\\logfiles\\\\',
'^[a-z]:\\\\windows\\\\system32\\\\spp\\\\',
'^[a-z]:\\\\windows\\\\system32\\\\wdi\\\\',
'^[a-z]:\\\\windows\\\\system32\\\\winevt\\\\',
'^[a-z]:\\\\windows\\\\sys.*\\\\config\\\\systemprofile\\\\appdata\\\\',
'^[a-z]:\\\\programdata\\\\',
'^[a-z]:\\\\msocache\\\\',
'^[a-z]:\\\\ccmcache\\\\',
'^[a-z]:\\\\[$]windows[.]~bt\\\\',
'^[a-z]:\\\\[$]upgrade[.]~os\\\\',
'^[a-z]:\\\\sccmcontentlib\\\\',
'^[a-z]:\\\\sms_dp[$]\\\\',
'^[a-z]:\\\\program files\\\\steam\\\\',
'^[a-z]:\\\\program files \\(x86\\)\\\\steam\\\\',
'^[a-z]:\\\\program files\\\\microsoft configuration manager\\\\',
'^[a-z]:\\\\system volume information\\\\',
'^[a-z]:\\\\system recovery\\\\',
'^[a-z]:\\\\program files\\\\microsoft office servers\\\\.*\\\\data\\\\office ',
'^[a-z]:\\\\program files\\\\microsoft\\\\exchange ',
'^[a-z]:\\\\windows\\\\servic',
'^[a-z]:\\\\program files\\(x86\\)\\\\skf\\\\surveryor\\\\',
'^[a-z]:\\\\windows\\\\system32\\\\spool\\\\drivers\\\\',
'^[a-z]:\\\\dfsroots\\\\',
'^[a-z]:\\\\lscc\\\\',
'^[a-z]:\\\\_smstasksequence\\\\',
'^[a-z]:\\\\mimecast\\\\mse\\\\',
'.*\\\\!tdr.bin\\\\',
'.*\\\\.dropbox.cache\\\\',
'.*\\\\iis temporary compressed files\\\\',
'.*\\\\appdata\\\\local\\\\google\\\\chrome\\\\user data\\\\',
'.*\\\\microsoft sql server\\\\.*\\\\setup bootstrap\\\\update cache\\\\',
'.*\\\\microsoft\\\\windows\\\\inetcache\\\\content.mso\\\\',
'.*server\\\\applications\\\\gthrsvc\\\\',
'.*server\\\\.*\\\\clientaccess\\\\oab\\\\temp\\\\',
}
-- table of known Microsoft Office Extensions
globals.officeExtensions = {
'doc',
'docb',
'docm',
'docx',
'dotm',
'dotx',
'dot',
'wbk',
'pot',
'potm',
'potx',
'ppam',
'pps',
'ppsm',
'ppsx',
'ppt',
'pptm',
'pptx',
'sldm',
'sldx',
'xla',
'xlam',
'xll',
'xlm',
'xls',
'xlsb',
'xlsm',
'xlsx',
'xlt',
'xltm',
'xltx',
'xlw'
}
globals.t_xml_1 = {0x3C, 0x3F, 0x78, 0x6D, 0x6C}
globals.t_null_1 = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}
globals.extensionMap = {}
globals.extensionMap["ax"] = utils.ExtensionData(0, false, {})
globals.extensionMap["com"] = utils.ExtensionData(0, false, {{77,90,},})
globals.extensionMap["cpl"] = utils.ExtensionData(0, false, {})
globals.extensionMap["dll"] = utils.ExtensionData(0, false, {{77,90,},})
globals.extensionMap["drv"] = utils.ExtensionData(0, false, {{77,90,},})
globals.extensionMap["efi"] = utils.ExtensionData(0, false, {})
globals.extensionMap["exe"] = utils.ExtensionData(0, false, {{77,90,},})
globals.extensionMap["filepart"] = utils.ExtensionData(0, false, {})
globals.extensionMap["msi"] = utils.ExtensionData(0, false, {{208,207,17,224,161,177,26,225,},})
globals.extensionMap["ocx"] = utils.ExtensionData(0, false, {})
globals.extensionMap["opexdiag"] = utils.ExtensionData(0, false, {})
globals.extensionMap["scr"] = utils.ExtensionData(0, false, {})
globals.extensionMap["sys"] = utils.ExtensionData(0, false, {{77,90,},})
globals.extensionMap["001"] = utils.ExtensionData(0, true, {})
globals.extensionMap["002"] = utils.ExtensionData(0, true, {})
globals.extensionMap["003"] = utils.ExtensionData(0, true, {})
globals.extensionMap["automaticdestinations-ms"] = utils.ExtensionData(0, true, {})
globals.extensionMap["bcf"] = utils.ExtensionData(0, true, {})
globals.extensionMap["blf"] = utils.ExtensionData(0, true, {})
globals.extensionMap["cache"] = utils.ExtensionData(0, true, {})
globals.extensionMap["check_cache"] = utils.ExtensionData(0, true, {})
globals.extensionMap["chk"] = utils.ExtensionData(0, true, {})
globals.extensionMap["ci"] = utils.ExtensionData(0, true, {})
globals.extensionMap["cmake"] = utils.ExtensionData(0, true, {})
globals.extensionMap["cmdline"] = utils.ExtensionData(0, true, {})
globals.extensionMap["crwl"] = utils.ExtensionData(0, true, {})
globals.extensionMap["customdestinations-ms"] = utils.ExtensionData(0, true, {})
globals.extensionMap["cvr"] = utils.ExtensionData(0, true, {})
globals.extensionMap["dblog"] = utils.ExtensionData(0, true, {})
globals.extensionMap["dbtmp"] = utils.ExtensionData(0, true, {})
globals.extensionMap["depend"] = utils.ExtensionData(0, true, {})
globals.extensionMap["diagpkg"] = utils.ExtensionData(0, true, {})
globals.extensionMap["diagsession"] = utils.ExtensionData(0, true, {})
globals.extensionMap["dir"] = utils.ExtensionData(0, true, {})
globals.extensionMap["etl"] = utils.ExtensionData(0, true, {})
globals.extensionMap["evtx"] = utils.ExtensionData(0, true, {})
globals.extensionMap["exp"] = utils.ExtensionData(0, true, {})
globals.extensionMap["filters"] = utils.ExtensionData(0, true, {})
globals.extensionMap["gthr"] = utils.ExtensionData(0, true, {})
globals.extensionMap["hit"] = utils.ExtensionData(0, true, {})
globals.extensionMap["ico"] = utils.ExtensionData(0, true, {})
globals.extensionMap["ilk"] = utils.ExtensionData(0, true, {})
globals.extensionMap["lastbuildstate"] = utils.ExtensionData(0, true, {})
globals.extensionMap["library-ms"] = utils.ExtensionData(0, true, {})
globals.extensionMap["list"] = utils.ExtensionData(0, true, {})
globals.extensionMap["little"] = utils.ExtensionData(0, true, {})
globals.extensionMap["log1"] = utils.ExtensionData(0, true, {})
globals.extensionMap["mui"] = utils.ExtensionData(0, true, {})
globals.extensionMap["nls"] = utils.ExtensionData(0, true, {})
globals.extensionMap["obj"] = utils.ExtensionData(0, true, {})
globals.extensionMap["perlcriticrc"] = utils.ExtensionData(0, true, {})
globals.extensionMap["pset"] = utils.ExtensionData(0, true, {})
globals.extensionMap["regtrans-ms"] = utils.ExtensionData(0, true, {})
globals.extensionMap["rsp"] = utils.ExtensionData(0, true, {})
globals.extensionMap["sbstore"] = utils.ExtensionData(0, true, {})
globals.extensionMap["sqlite"] = utils.ExtensionData(0, true, {})
globals.extensionMap["sqlite-shm"] = utils.ExtensionData(0, true, {})
globals.extensionMap["sqlite-wal"] = utils.ExtensionData(0, true, {})
globals.extensionMap["stamp"] = utils.ExtensionData(0, true, {})
globals.extensionMap["suodat"] = utils.ExtensionData(0, true, {})
globals.extensionMap["swp"] = utils.ExtensionData(0, true, {})
globals.extensionMap["temp"] = utils.ExtensionData(0, true, {})
globals.extensionMap["tlog"] = utils.ExtensionData(0, true, {})
globals.extensionMap["trn"] = utils.ExtensionData(0, true, {})
globals.extensionMap["unsuccessfulbuild"] = utils.ExtensionData(0, true, {})
globals.extensionMap["vcxproj"] = utils.ExtensionData(0, true, {})
globals.extensionMap["viminfo"] = utils.ExtensionData(0, true, {})
globals.extensionMap["wid"] = utils.ExtensionData(0, true, {})
globals.extensionMap["winprf"] = utils.ExtensionData(0, true, {})
globals.extensionMap["xlb"] = utils.ExtensionData(0, true, {})
globals.extensionMap["7z"] = utils.ExtensionData(1, false, {{55,122,188,175,39,28,},})
globals.extensionMap["accdb"] = utils.ExtensionData(1, false, {{},})
globals.extensionMap["ace"] = utils.ExtensionData(1, false, {})
globals.extensionMap["aiff"] = utils.ExtensionData(1, false, {{},})
globals.extensionMap["apk"] = utils.ExtensionData(1, false, {{80,75,},})
globals.extensionMap["asf"] = utils.ExtensionData(1, false, {{},})
globals.extensionMap["avi"] = utils.ExtensionData(1, false, {{},})
globals.extensionMap["bak"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bin"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bmp"] = utils.ExtensionData(1, false, {{66,77,},{71,73,70,56,},{255,216,255,},{239,191,189,239,},{137,80,78,71,13,10,},{137,80,78,71,13,10,26,10,},{71,73,70,56,57,97,},})
globals.extensionMap["bz2"] = utils.ExtensionData(1, false, {{66,90,104,},})
globals.extensionMap["cab"] = utils.ExtensionData(1, false, {{77,83,67,70,},{73,83,99,40,},})
globals.extensionMap["car"] = utils.ExtensionData(1, false, {})
globals.extensionMap["cfg"] = utils.ExtensionData(1, false, {})
globals.extensionMap["chm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["class"] = utils.ExtensionData(1, false, {{202,254,186,190,},})
globals.extensionMap["cr2"] = utils.ExtensionData(1, false, {{73,73,42,0,16,0,0,0,67,82,},})
globals.extensionMap["crt"] = utils.ExtensionData(1, false, {})
globals.extensionMap["crx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dar"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dat"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dazip"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dmg"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dmp"] = utils.ExtensionData(1, false, {})
globals.extensionMap["docm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["docx"] = utils.ExtensionData(1, false, {{80,75,},{7,},{10,},})
globals.extensionMap["dotm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dotx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["flac"] = utils.ExtensionData(1, false, {{102,76,97,67,},})
globals.extensionMap["flv"] = utils.ExtensionData(1, false, {})
globals.extensionMap["gif"] = utils.ExtensionData(1, false, {{66,77,},{71,73,70,56,},{255,216,255,},{239,191,189,239,},{137,80,78,71,13,10,},{137,80,78,71,13,10,26,10,},{71,73,70,56,57,97,},})
globals.extensionMap["gz"] = utils.ExtensionData(1, false, {{31,139,},})
globals.extensionMap["info"] = utils.ExtensionData(1, false, {})
globals.extensionMap["iso"] = utils.ExtensionData(1, false, {{67,68,48,48,49,},})
globals.extensionMap["jar"] = utils.ExtensionData(1, false, {{80,75,},})
globals.extensionMap["jpe"] = utils.ExtensionData(1, false, {})
globals.extensionMap["jpeg"] = utils.ExtensionData(1, false, {{66,77,},{71,73,70,56,},{255,216,255,},{239,191,189,239,},{137,80,78,71,13,10,},{137,80,78,71,13,10,26,10,},{71,73,70,56,57,97,},})
globals.extensionMap["jpg"] = utils.ExtensionData(1, false, {{66,77,},{71,73,70,56,},{255,216,255,},{239,191,189,239,},{137,80,78,71,13,10,},{137,80,78,71,13,10,26,10,},{71,73,70,56,57,97,},})
globals.extensionMap["jse"] = utils.ExtensionData(1, false, {})
globals.extensionMap["lz"] = utils.ExtensionData(1, false, {})
globals.extensionMap["lzma"] = utils.ExtensionData(1, false, {})
globals.extensionMap["lzo"] = utils.ExtensionData(1, false, {})
globals.extensionMap["m4a"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mar"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mka"] = utils.ExtensionData(1, false, {{26,69,223,163,},})
globals.extensionMap["mks"] = utils.ExtensionData(1, false, {{26,69,223,163,},})
globals.extensionMap["mkv"] = utils.ExtensionData(1, false, {{26,69,223,163,},})
globals.extensionMap["mov"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mp3"] = utils.ExtensionData(1, false, {{73,68,51,},{255,251,},})
globals.extensionMap["mp4"] = utils.ExtensionData(1, false, {})
globals.extensionMap["msp"] = utils.ExtensionData(1, false, {})
globals.extensionMap["odp"] = utils.ExtensionData(1, false, {{80,75,},})
globals.extensionMap["ods"] = utils.ExtensionData(1, false, {{80,75,},})
globals.extensionMap["odt"] = utils.ExtensionData(1, false, {{80,75,},})
globals.extensionMap["oga"] = utils.ExtensionData(1, false, {{79,103,103,83,},})
globals.extensionMap["ogg"] = utils.ExtensionData(1, false, {{79,103,103,83,},})
globals.extensionMap["ogv"] = utils.ExtensionData(1, false, {{79,103,103,83,},})
globals.extensionMap["part"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pdb"] = utils.ExtensionData(1, false, {{77,105,99,114,111,115,111,102,116,32,67,47,67,43,43,32,},{66,83,74,66,},})
globals.extensionMap["pdf"] = utils.ExtensionData(1, false, {{37,80,68,70,},})
globals.extensionMap["pkpass"] = utils.ExtensionData(1, false, {})
globals.extensionMap["png"] = utils.ExtensionData(1, false, {{66,77,},{71,73,70,56,},{255,216,255,},{239,191,189,239,},{137,80,78,71,13,10,},{137,80,78,71,13,10,26,10,},{71,73,70,56,57,97,},})
globals.extensionMap["pot"] = utils.ExtensionData(1, false, {})
globals.extensionMap["potm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["potx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ppa"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ppam"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pps"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ppsm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ppsx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pptm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pptx"] = utils.ExtensionData(1, false, {{80,75,},{7,},{10,},})
globals.extensionMap["ps"] = utils.ExtensionData(1, false, {})
globals.extensionMap["psd"] = utils.ExtensionData(1, false, {{56,66,80,83,},})
globals.extensionMap["pst"] = utils.ExtensionData(1, false, {{33,66,68,78,66,},})
globals.extensionMap["pyd"] = utils.ExtensionData(1, false, {{77,90,},})
globals.extensionMap["rar"] = utils.ExtensionData(1, false, {{82,97,114,33,26,7,0,},{82,97,114,33,26,7,1,0,},})
globals.extensionMap["rll"] = utils.ExtensionData(1, true, {{77,90,},})
globals.extensionMap["rz"] = utils.ExtensionData(1, false, {})
globals.extensionMap["swf"] = utils.ExtensionData(1, false, {{70,87,83,},{67,87,83,},})
globals.extensionMap["tar"] = utils.ExtensionData(1, false, {})
globals.extensionMap["tbl"] = utils.ExtensionData(1, false, {})
globals.extensionMap["tbz2"] = utils.ExtensionData(1, false, {})
globals.extensionMap["tif"] = utils.ExtensionData(1, false, {{73,73,42,0,},{77,77,0,42,},})
globals.extensionMap["tiff"] = utils.ExtensionData(1, false, {{73,73,42,0,},{77,77,0,42,},})
globals.extensionMap["tlz"] = utils.ExtensionData(1, false, {})
globals.extensionMap["vfs0"] = utils.ExtensionData(1, false, {})
globals.extensionMap["vmdk"] = utils.ExtensionData(1, false, {{75,68,77,},})
globals.extensionMap["wallet"] = utils.ExtensionData(1, false, {{10,22,111,114,103,46,98,105,116,99,111,105,110,46,112,114,},})
globals.extensionMap["war"] = utils.ExtensionData(1, false, {})
globals.extensionMap["wav"] = utils.ExtensionData(1, false, {{82,},{87,},})
globals.extensionMap["webm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["wim"] = utils.ExtensionData(1, false, {})
globals.extensionMap["wma"] = utils.ExtensionData(1, false, {{48,38,178,117,142,102,207,17,166,217,0,170,0,98,206,108,},})
globals.extensionMap["wmo"] = utils.ExtensionData(1, false, {})
globals.extensionMap["wmv"] = utils.ExtensionData(1, false, {{48,38,178,117,142,102,207,17,166,217,0,170,0,98,206,108,},})
globals.extensionMap["woff"] = utils.ExtensionData(1, false, {})
globals.extensionMap["xar"] = utils.ExtensionData(1, false, {{120,97,114,33,},})
globals.extensionMap["xla"] = utils.ExtensionData(1, false, {})
globals.extensionMap["xlam"] = utils.ExtensionData(1, false, {})
globals.extensionMap["xlsb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["xlsm"] = utils.ExtensionData(1, false, {{80,75,},})
globals.extensionMap["xlsx"] = utils.ExtensionData(1, false, {{80,75,},{7,},{10,},})
globals.extensionMap["xlt"] = utils.ExtensionData(1, false, {})
globals.extensionMap["xltx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["xps"] = utils.ExtensionData(1, false, {})
globals.extensionMap["z"] = utils.ExtensionData(1, false, {})
globals.extensionMap["zip"] = utils.ExtensionData(1, false, {{80,75,},{31,139,},{120,},})
globals.extensionMap["zipx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["zz"] = utils.ExtensionData(1, false, {})
globals.extensionMap["acm"] = utils.ExtensionData(1, true, {})
globals.extensionMap["aml"] = utils.ExtensionData(1, true, {})
globals.extensionMap["ascii"] = utils.ExtensionData(1, true, {})
globals.extensionMap["asp"] = utils.ExtensionData(1, true, {})
globals.extensionMap["aspx"] = utils.ExtensionData(1, true, {})
globals.extensionMap["assoc"] = utils.ExtensionData(1, true, {})
globals.extensionMap["asx"] = utils.ExtensionData(1, true, {})
globals.extensionMap["bash_history"] = utils.ExtensionData(1, true, {})
globals.extensionMap["bash_profile"] = utils.ExtensionData(1, true, {})
globals.extensionMap["bashrc"] = utils.ExtensionData(1, true, {})
globals.extensionMap["bat"] = utils.ExtensionData(1, true, {})
globals.extensionMap["c"] = utils.ExtensionData(1, true, {})
globals.extensionMap["cgi"] = utils.ExtensionData(1, true, {})
globals.extensionMap["charset"] = utils.ExtensionData(1, true, {})
globals.extensionMap["chs"] = utils.ExtensionData(1, true, {})
globals.extensionMap["cht"] = utils.ExtensionData(1, true, {})
globals.extensionMap["cmd"] = utils.ExtensionData(1, true, {})
globals.extensionMap["cpp"] = utils.ExtensionData(1, true, {})
globals.extensionMap["cs"] = utils.ExtensionData(1, true, {})
globals.extensionMap["csh"] = utils.ExtensionData(1, true, {})
globals.extensionMap["css"] = utils.ExtensionData(1, true, {})
globals.extensionMap["csv"] = utils.ExtensionData(1, true, {})
globals.extensionMap["def"] = utils.ExtensionData(1, true, {})
globals.extensionMap["dif"] = utils.ExtensionData(1, true, {})
globals.extensionMap["doc"] = utils.ExtensionData(1, true, {{208,207,17,224,161,177,26,225,},{7,},{37,},{63,215,108,},{10,},})
globals.extensionMap["dtd"] = utils.ExtensionData(1, true, {})
globals.extensionMap["ebd"] = utils.ExtensionData(1, true, {})
globals.extensionMap["fllnks"] = utils.ExtensionData(1, true, {})
globals.extensionMap["git"] = utils.ExtensionData(1, true, {})
globals.extensionMap["gitconfig"] = utils.ExtensionData(1, true, {})
globals.extensionMap["gitignore"] = utils.ExtensionData(1, true, {})
globals.extensionMap["h"] = utils.ExtensionData(1, true, {})
globals.extensionMap["hlp"] = utils.ExtensionData(1, true, {})
globals.extensionMap["htm"] = utils.ExtensionData(1, true, {})
globals.extensionMap["html"] = utils.ExtensionData(1, true, {})
globals.extensionMap["iec"] = utils.ExtensionData(1, true, {})
globals.extensionMap["ime"] = utils.ExtensionData(1, true, {})
globals.extensionMap["inf"] = utils.ExtensionData(1, true, {})
globals.extensionMap["ini"] = utils.ExtensionData(1, true, {})
globals.extensionMap["java"] = utils.ExtensionData(1, true, {})
globals.extensionMap["jnt"] = utils.ExtensionData(1, true, {})
globals.extensionMap["job"] = utils.ExtensionData(1, true, {})
globals.extensionMap["jpn"] = utils.ExtensionData(1, true, {})
globals.extensionMap["js"] = utils.ExtensionData(1, true, {})
globals.extensionMap["json"] = utils.ExtensionData(1, true, {})
globals.extensionMap["jsp"] = utils.ExtensionData(1, true, {})
globals.extensionMap["kor"] = utils.ExtensionData(1, true, {})
globals.extensionMap["lex"] = utils.ExtensionData(1, true, {})
globals.extensionMap["lib"] = utils.ExtensionData(1, true, {})
globals.extensionMap["litesql"] = utils.ExtensionData(1, true, {})
globals.extensionMap["lnk"] = utils.ExtensionData(1, true, {})
globals.extensionMap["log"] = utils.ExtensionData(1, true, {})
globals.extensionMap["log2"] = utils.ExtensionData(1, true, {})
globals.extensionMap["m3u"] = utils.ExtensionData(1, true, {})
globals.extensionMap["man"] = utils.ExtensionData(1, true, {})
globals.extensionMap["manifest"] = utils.ExtensionData(1, true, {})
globals.extensionMap["md"] = utils.ExtensionData(1, true, {})
globals.extensionMap["mht"] = utils.ExtensionData(1, true, {})
globals.extensionMap["mhtml"] = utils.ExtensionData(1, true, {})
globals.extensionMap["mof"] = utils.ExtensionData(1, true, {})
globals.extensionMap["msc"] = utils.ExtensionData(1, true, {})
globals.extensionMap["msg"] = utils.ExtensionData(1, true, {})
globals.extensionMap["nfo"] = utils.ExtensionData(1, true, {})
globals.extensionMap["odb"] = utils.ExtensionData(1, true, {})
globals.extensionMap["odc"] = utils.ExtensionData(1, true, {})
globals.extensionMap["odm"] = utils.ExtensionData(1, true, {})
globals.extensionMap["php"] = utils.ExtensionData(1, true, {})
globals.extensionMap["pl"] = utils.ExtensionData(1, true, {})
globals.extensionMap["pml"] = utils.ExtensionData(1, true, {})
globals.extensionMap["pol"] = utils.ExtensionData(1, true, {})
globals.extensionMap["ppt"] = utils.ExtensionData(1, true, {{208,207,17,224,161,177,26,225,},{7,},{10,},})
globals.extensionMap["prn"] = utils.ExtensionData(1, true, {})
globals.extensionMap["prx"] = utils.ExtensionData(1, true, {})
globals.extensionMap["ps1"] = utils.ExtensionData(1, true, {})
globals.extensionMap["py"] = utils.ExtensionData(1, true, {})
globals.extensionMap["pyc"] = utils.ExtensionData(1, true, {})
globals.extensionMap["qts"] = utils.ExtensionData(1, true, {{77,90,},})
globals.extensionMap["qtx"] = utils.ExtensionData(1, true, {{77,90,},})
globals.extensionMap["rat"] = utils.ExtensionData(1, true, {})
globals.extensionMap["rdp"] = utils.ExtensionData(1, true, {})
globals.extensionMap["rea"] = utils.ExtensionData(1, true, {})
globals.extensionMap["readme"] = utils.ExtensionData(1, true, {})
globals.extensionMap["reg"] = utils.ExtensionData(1, true, {})
globals.extensionMap["resp"] = utils.ExtensionData(1, true, {})
globals.extensionMap["rs"] = utils.ExtensionData(1, true, {})
globals.extensionMap["rss"] = utils.ExtensionData(1, true, {})
globals.extensionMap["rtf"] = utils.ExtensionData(1, true, {{123,92,114,116,102,49,},})
globals.extensionMap["scf"] = utils.ExtensionData(1, true, {})
globals.extensionMap["sdf"] = utils.ExtensionData(1, true, {})
globals.extensionMap["sdi"] = utils.ExtensionData(1, true, {})
globals.extensionMap["sep"] = utils.ExtensionData(1, true, {})
globals.extensionMap["sh"] = utils.ExtensionData(1, true, {})
globals.extensionMap["slk"] = utils.ExtensionData(1, true, {})
globals.extensionMap["sln"] = utils.ExtensionData(1, true, {})
globals.extensionMap["sql"] = utils.ExtensionData(1, true, {})
globals.extensionMap["svg"] = utils.ExtensionData(1, true, {})
globals.extensionMap["text"] = utils.ExtensionData(1, true, {})
globals.extensionMap["tha"] = utils.ExtensionData(1, true, {})
globals.extensionMap["tlb"] = utils.ExtensionData(1, true, {})
globals.extensionMap["tsp"] = utils.ExtensionData(1, true, {})
globals.extensionMap["tt"] = utils.ExtensionData(1, true, {})
globals.extensionMap["txt"] = utils.ExtensionData(1, true, {})
globals.extensionMap["uce"] = utils.ExtensionData(1, true, {})
globals.extensionMap["vb"] = utils.ExtensionData(1, true, {})
globals.extensionMap["vbs"] = utils.ExtensionData(1, true, {})
globals.extensionMap["vbscript"] = utils.ExtensionData(1, true, {})
globals.extensionMap["wmf"] = utils.ExtensionData(1, true, {{215,205,198,154,},})
globals.extensionMap["ws"] = utils.ExtensionData(1, true, {})
globals.extensionMap["wsf"] = utils.ExtensionData(1, true, {})
globals.extensionMap["wsh"] = utils.ExtensionData(1, true, {})
globals.extensionMap["xhtm"] = utils.ExtensionData(1, true, {})
globals.extensionMap["xhtml"] = utils.ExtensionData(1, true, {})
globals.extensionMap["xls"] = utils.ExtensionData(1, true, {{208,207,17,224,161,177,26,225,},{7,},{10,},})
globals.extensionMap["xml"] = utils.ExtensionData(1, true, {})
globals.extensionMap["xsl"] = utils.ExtensionData(1, true, {})
globals.extensionMap["yml"] = utils.ExtensionData(1, true, {})
globals.extensionMap[""] = utils.ExtensionData(1, false, {})
globals.extensionMap["3fr"] = utils.ExtensionData(1, false, {})
globals.extensionMap["arch00"] = utils.ExtensionData(1, false, {})
globals.extensionMap["arw"] = utils.ExtensionData(1, false, {})
globals.extensionMap["asset"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bar"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bay"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bc6"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bc7"] = utils.ExtensionData(1, false, {})
globals.extensionMap["big"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bik"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bkf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bkp"] = utils.ExtensionData(1, false, {})
globals.extensionMap["blob"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bpd"] = utils.ExtensionData(1, false, {})
globals.extensionMap["bsa"] = utils.ExtensionData(1, false, {})
globals.extensionMap["cas"] = utils.ExtensionData(1, false, {})
globals.extensionMap["cdr"] = utils.ExtensionData(1, false, {})
globals.extensionMap["cer"] = utils.ExtensionData(1, false, {})
globals.extensionMap["cfr"] = utils.ExtensionData(1, false, {})
globals.extensionMap["crw"] = utils.ExtensionData(1, false, {})
globals.extensionMap["d3dbsp"] = utils.ExtensionData(1, false, {})
globals.extensionMap["das"] = utils.ExtensionData(1, false, {})
globals.extensionMap["db"] = utils.ExtensionData(1, false, {})
globals.extensionMap["db0"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dba"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dbf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dcr"] = utils.ExtensionData(1, false, {})
globals.extensionMap["der"] = utils.ExtensionData(1, false, {})
globals.extensionMap["desc"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dng"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dwg"] = utils.ExtensionData(1, false, {})
globals.extensionMap["dxg"] = utils.ExtensionData(1, false, {})
globals.extensionMap["eml"] = utils.ExtensionData(1, false, {})
globals.extensionMap["epk"] = utils.ExtensionData(1, false, {})
globals.extensionMap["eps"] = utils.ExtensionData(1, false, {})
globals.extensionMap["erf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["esm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ff"] = utils.ExtensionData(1, false, {})
globals.extensionMap["forge"] = utils.ExtensionData(1, false, {})
globals.extensionMap["fos"] = utils.ExtensionData(1, false, {})
globals.extensionMap["fpk"] = utils.ExtensionData(1, false, {})
globals.extensionMap["fsh"] = utils.ExtensionData(1, false, {})
globals.extensionMap["gdb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["gdl"] = utils.ExtensionData(1, false, {})
globals.extensionMap["gho"] = utils.ExtensionData(1, false, {})
globals.extensionMap["gid"] = utils.ExtensionData(1, false, {})
globals.extensionMap["hdmp"] = utils.ExtensionData(1, false, {})
globals.extensionMap["hkdb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["hkx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["hplg"] = utils.ExtensionData(1, false, {})
globals.extensionMap["hvpl"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ibank"] = utils.ExtensionData(1, false, {})
globals.extensionMap["icxs"] = utils.ExtensionData(1, false, {})
globals.extensionMap["indd"] = utils.ExtensionData(1, false, {})
globals.extensionMap["itdb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["itl"] = utils.ExtensionData(1, false, {})
globals.extensionMap["itm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["iwd"] = utils.ExtensionData(1, false, {})
globals.extensionMap["iwi"] = utils.ExtensionData(1, false, {})
globals.extensionMap["kdb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["kdc"] = utils.ExtensionData(1, false, {})
globals.extensionMap["kf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["layout"] = utils.ExtensionData(1, false, {})
globals.extensionMap["lbf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["litemod"] = utils.ExtensionData(1, false, {})
globals.extensionMap["lock"] = utils.ExtensionData(1, false, {})
globals.extensionMap["lrf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ltx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["lvl"] = utils.ExtensionData(1, false, {})
globals.extensionMap["m2"] = utils.ExtensionData(1, false, {})
globals.extensionMap["map"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mcmeta"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mdb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mdbackup"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mddata"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mdf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mef"] = utils.ExtensionData(1, false, {})
globals.extensionMap["menu"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mk3d"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mlx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mpqge"] = utils.ExtensionData(1, false, {})
globals.extensionMap["mrwref"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ncf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["nrw"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ntf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ntl"] = utils.ExtensionData(1, false, {})
globals.extensionMap["orf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["p12"] = utils.ExtensionData(1, false, {})
globals.extensionMap["p7b"] = utils.ExtensionData(1, false, {})
globals.extensionMap["p7c"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pak"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pdd"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pef"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pem"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pfx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pif"] = utils.ExtensionData(1, false, {})
globals.extensionMap["pma"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ppd"] = utils.ExtensionData(1, false, {})
globals.extensionMap["psk"] = utils.ExtensionData(1, false, {})
globals.extensionMap["ptx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["qdf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["qic"] = utils.ExtensionData(1, false, {})
globals.extensionMap["r3d"] = utils.ExtensionData(1, false, {})
globals.extensionMap["raf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["raw"] = utils.ExtensionData(1, false, {})
globals.extensionMap["rb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["re4"] = utils.ExtensionData(1, false, {})
globals.extensionMap["rgss3a"] = utils.ExtensionData(1, false, {})
globals.extensionMap["rim"] = utils.ExtensionData(1, false, {})
globals.extensionMap["rofl"] = utils.ExtensionData(1, false, {})
globals.extensionMap["rw2"] = utils.ExtensionData(1, false, {})
globals.extensionMap["rwl"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sav"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sid"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sidd"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sidn"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sie"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sis"] = utils.ExtensionData(1, false, {})
globals.extensionMap["size"] = utils.ExtensionData(1, false, {})
globals.extensionMap["slm"] = utils.ExtensionData(1, false, {})
globals.extensionMap["snx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sr2"] = utils.ExtensionData(1, false, {})
globals.extensionMap["srf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["srw"] = utils.ExtensionData(1, false, {})
globals.extensionMap["sum"] = utils.ExtensionData(1, false, {})
globals.extensionMap["syncdb"] = utils.ExtensionData(1, false, {})
globals.extensionMap["t12"] = utils.ExtensionData(1, false, {})
globals.extensionMap["t13"] = utils.ExtensionData(1, false, {})
globals.extensionMap["tax"] = utils.ExtensionData(1, false, {})
globals.extensionMap["tmp"] = utils.ExtensionData(1, false, {})
globals.extensionMap["tor"] = utils.ExtensionData(1, false, {})
globals.extensionMap["upk"] = utils.ExtensionData(1, false, {})
globals.extensionMap["vcf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["vdf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["vpk"] = utils.ExtensionData(1, false, {})
globals.extensionMap["vpp_pc"] = utils.ExtensionData(1, false, {})
globals.extensionMap["vsdx"] = utils.ExtensionData(1, false, {})
globals.extensionMap["vtf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["w3x"] = utils.ExtensionData(1, false, {})
globals.extensionMap["wb2"] = utils.ExtensionData(1, false, {})
globals.extensionMap["wpd"] = utils.ExtensionData(1, false, {})
globals.extensionMap["wps"] = utils.ExtensionData(1, false, {})
globals.extensionMap["x3f"] = utils.ExtensionData(1, false, {})
globals.extensionMap["xf"] = utils.ExtensionData(1, false, {})
globals.extensionMap["xlk"] = utils.ExtensionData(1, false, {})
----
globals.EXTENSION_IGNORED = 0
globals.EXTENSION_MONITORED = 1
globals.EXTENSION_SUSPICIOUS = 2
globals.EXTENSION_UNKNOWN = 3
globals.unknownExtensionData = utils.ExtensionData(globals.EXTENSION_UNKNOWN, false, {})
-- start ransomware extensions
globals.extensionMap['0x0'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['1999'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['73i87a'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['777'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['7h9r'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['8lock8'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['____xratteamlucked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['___xratteamlucked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['_airacropencrypted'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['_crypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['_nullbyte'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['_ryp'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['acuna'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['adam'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['adolfhitler'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['aes256'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['aga'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['alcatraz'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['amba'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['angelamerkel'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['angleware'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['antihacker2017'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['arena'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['babyk'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['bagli'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['barrax'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['bart'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['better_call_saul'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['bitpy'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['bitstak'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['blackruby'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['bleepyourfiles'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['bloc'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['blocatto'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['braincrypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['breaking_bad'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['bript'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['btc'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['btc - help - you'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['btcbtcbtc'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['btcware'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['bullet'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cccrrrppp'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cerber'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cerber2'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cerber3'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cesar'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['chernolocker)'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['chifrator@qq_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['chip'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['ciop'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['clop'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['coded'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['com]'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['com___'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['comrade'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['coverton'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crab'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crashed'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crime'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crinf'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['criptiko'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['criptoko'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['criptokod'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cripttt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crjoker'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crptrgr'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crrrt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cry_'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cryp1'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crypt38'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crypted'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crypted_file'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crypto'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cryptoshiel'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cryptoshield'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cryptotorlocker2015!'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cryptz'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crypz'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['crysis'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['cuba'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['czvxce'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['d4nk'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['da_vinci_code'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['dale'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['damage'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['darkness'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['dcrtr'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['dcry'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['dcrypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['ddsg'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['deep'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['deria'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['deuscrypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['dharma'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['disappeared'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['donut'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['doomed'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['dxxd'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['dyatel@qq_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['edgel'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['ehiz'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['embrace'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['encedrsa'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['enciphered'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['encoderpass'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['encr'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['encryptedaes'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['encryptedrsa'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['enigma'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['es_helps'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['evil'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['evillock'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['exotic'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['ezz'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['fantom'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['fear'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['file0locked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['fileiscryptedhard'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['filock'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['firecrypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['frtrss'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['fs0ciety'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['fuck'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['fucked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['fuckyourdata'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['gdcb'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['gruzin@qq_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['gujd'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['h3ll'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['ha3'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['hannah'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['hanta'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['happy new year'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['hb15'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['helpdecrypt@ukr.net'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['herbst'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['hive'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['honor'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['id-_locked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['id-_locked_by_krec'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['id-_locked_by_perfect'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['id-_r9oj'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['id-_x3m'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['igvm'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['iiohy'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['infected'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['insane'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['iqll'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['jest'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['jey'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['josep'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['justbtcwillhelpyou'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['karma'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['kencf'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['keybtc@inbox_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['keyh0les'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['keyz'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['killedxxx'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['kimcilware'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['king_ouroboros'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['kirked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['kkk'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['korrektor'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['kostya'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['kr3'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['krab'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['kraken'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['kratos'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['l0cked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['lechiffre'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['leex'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['legion'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['leon'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['lesli'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['licked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['lock93'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['lockbit'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['locked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['locked-[xxx]'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['locked_file'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['locklock'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['locky'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['lol!'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['lovewindows'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['lssr'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['madebyadam'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['megac0rtx'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['miis'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['mpqq'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['nalog@qq_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['neer'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['nefilim'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['no_more_ransom'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['nochance'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['nuclear55'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['nusm'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['nwji'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['odcodc'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['ohno!'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['omg!'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['oops'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['oor'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['oplata@qq_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['oshit'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['p5tkjw'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['paas'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['padcrypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['pahad'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['pahd'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['pain'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['payrmts'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['pcqq'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['piiq'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['pizda@qq_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['poar2w'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['pooe'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['porno'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['potato'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['prolock'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['pysa'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['qscx'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['qwerty'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['r4a'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['r5a'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['radamant'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['ragnarok'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['ransomaes'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['razy'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['rdmk'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['rejg'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['rekt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['relock@qq_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['remind'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['revenge'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['rnsmwr'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['rokku'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['rrk'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['rsnslocked'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['rsplited'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sage'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sanction'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['satyr'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['securecrypted'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sepsis'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['serpent'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sexy'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['shinigami'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['shino'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sick'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sifreli'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sigrun'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['silent'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sport'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['sspq'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['surprise'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['thetrumplockerf'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['thetrumplockerp'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['toxcrypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['triple_m'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['tron'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['troyancoder@qq_com'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['trun'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['tzu'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['unavailable'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['vault'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['vbransom'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['velikasrbija'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['venusf'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['venusp'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['versiegelt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['vindows'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['volcano'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['vscrypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['vxlock'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['wcry'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['wflx'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['whiterose'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['windows10'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['wnx'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['wwka'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['wyvern'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['x3m'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['xcri'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['xmdxtazx'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['xort'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['xrtn'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['xtbl'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['yourransom'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['z81928819'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['zcrypt'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['zino'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['zorro'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['zqqw'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['zyklon'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['zzla'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
globals.extensionMap['{crypttendblackdc)'] = utils.ExtensionData(globals.EXTENSION_SUSPICIOUS, false, {})
-- Canary represents a canary file object.
-- @param fullpath string: canary full destination path (required)
-- @param content string: content of the canary file (required).
-- @param hidden bool: true sets the file attributes to hidden.
-- @param force bool: true forces the creation of the file.
-- @param system bool: true sets the file attributes to system .
-- @return table representing the the canary file or nil.
function globals.Canary(fullpath, content, force, hidden, system)
-- sanity check for required fields.
if fullpath == nil or content == nil then
return nil
end
-- by default, force create files.
if force == nil then
force = true
end
-- by default, do not hide files.
if hidden == nil then
hidden = false
end
-- by default, create a regular file.
if system == nil then
system = false
end
local namesList = utils.Split(fullpath, '\\')
if #namesList <= 2 then
return nil
end
local filename = namesList[#namesList]:lower()
local dirname = namesList[#namesList - 1]:lower()
local self = {}
self.force = force
self.hidden = hidden
self.system = system
self.fullpath = fullpath
self.filename = filename
self.dirname = dirname
self.content = content
return self
end
-- Create a canary file content. This function should be improved later to take
-- a mime type and generate a valid canary file type.
function globals.CreateCanaryContent()
local header = 'This is a canary file to detect ransomware. Please do not'
header = header .. ' modify or delete it.\n\n\n'
-- math.randomseed(os.time())
-- local r = math.random(1, 20)
local lorem = [[ Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip
ex ea commodo consequat.Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
]]
local canaryContent = header
for _ = 1, 20 do
canaryContent = canaryContent .. lorem
end
return canaryContent
end
-- This function checks for file creation events in any canary directory OR
-- any file events (except DELETE and OPEN) related to canary files.
function globals.Lua_CanaryCheck(eventData)
local subDir = nil
local filePath = eventData.filePath:lower()
for _, dirName in ipairs(globals.namespace.canaryDirNames) do
subDir = string.find(filePath, dirName, nil, true)
if nil ~= subDir then
-- event involves a canary directory
break
end
end
if nil == subDir then
return false
end
local fileName = eventData.fileName:lower()
for _, canaryFileName in ipairs(globals.namespace.canaryFileNames) do
local subFile = string.find(fileName, canaryFileName, nil, true)
if globals.FILE_CREATE_NEW == eventData.operation then
utils.DebugLog('NEW FILE IN CANARY DIRECTORY!!!!!')
if globals.namespace.totalCanaryCreateFileAlerts >= globals.CANARY_CREATE_FILE_ALERT_CAP then
utils.DebugLog('CANARY CREATE FILE THRESHOLD REACHED')
else
globals.namespace.totalCanaryCreateFileAlerts = globals.namespace.totalCanaryCreateFileAlerts + 1
return true
end
elseif (nil ~= subFile) and (globals.FILE_DELETE ~= eventData.operation) and
(globals.FILE_OPEN ~= eventData.operation) then
utils.DebugLog('CANARY FILE EVENT!!!!!!')
return true
end
end
return false
end
-- set a custom config metrics
function globals.SetConfig(newConfig)
globals.config = newConfig
end
-- set a custom extension map
function globals.SetExtensionMap(newExtensionMap)
globals.extensionMap = newExtensionMap
end
-- set custom ignore paths
function globals.SetIgnorePaths(newIgnorePaths)
globals.regexIgnorePaths = newIgnorePaths
end
--------------------------------------------------------------
-- GLOBAL FUNCTIONS
-- Common functions that will be called by separate namespaces
--------------------------------------------------------------
-- Used to track the namespace currently being used so any associated debug
-- logging can be tagged with the appropriate namespace. In the future,
-- we could write namespace-specific logic in shared global functions to reduce
-- code duplication.
function globals.SwitchNamespace(newNamespace)
globals.namespace = newNamespace
end
-- In order to work around 3.53 limitation that limits processes to one alert,
-- we will queue diagnostic alerts (in order to give standard alerts priority)
-- until the process:
-- 1) Generates a standard alert.
-- 2) Meets the file event threshold.
-- 3) Is terminated.
-- Once one of these conditions is met, we consult the relevant ProcessData
-- object in the diagnostic namespace to determine if a diagnostic alert was
-- previously queued; if one has, then we invoke the GenerateAlert function.
-- @param processId integer: The subject process identifier.
function globals.CheckForQueuedDiagnosticAlert(processId)
local currentNamespace = globals.namespace
if (#globals.namespaces == 1) and (currentNamespace.isDiagnostic == false) then
return false
end
for _, namespace in pairs(globals.namespaces) do
if namespace.isDiagnostic then
globals.SwitchNamespace(namespace)
break
end
end
if utils.TableHasKey(globals.namespace.processDataTable, processId) then
local diagnosticProcessData = globals.namespace.processDataTable[processId]
if diagnosticProcessData.diagnosticAlertQueued then
utils.DebugLog('diagnosticAlertQueued: ' .. processId)
alert.GenerateAlert(diagnosticProcessData, true)
end
end
globals.SwitchNamespace(currentNamespace)
end
-- Sanitizes the paths, names, and extensions for the provided event.
-- @param eventData table: A table containing event data.
-- @return void.
function globals.CleanEventData(eventData)
if utils.TableHasKey(eventData, 'filePath') then
eventData.filePath = utils.RemoveAdsFromPath(eventData.filePath)
eventData.fileName = eventData.filePath:match('[^\\]+$')
end
if utils.TableHasKey(eventData, 'fileExtension') then
eventData.fileExtension = utils.RemoveAdsFromPath(eventData.fileExtension)
end
if utils.TableHasKey(eventData, 'filePreviousPath') then
eventData.filePreviousPath = utils.RemoveAdsFromPath(eventData.filePreviousPath)
eventData.filePreviousName = eventData.filePreviousPath:match('[^\\]+$')
end
if utils.TableHasKey(eventData, 'filePreviousExtension') then
eventData.filePreviousExtension = utils.RemoveAdsFromPath(eventData.filePreviousExtension)
end
if utils.IsOfficeLockFile(eventData.fileExtension, eventData.fileName) then
utils.DebugLog('OFFICE LOCK FILE: ' .. eventData.filePath)
eventData.officeLockFile = true
else
eventData.officeLockFile = false
end
end
-- Updates the extension table. This function inserts the extension name to the
-- appropriate process data extension table according to the file operation.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return void.
function globals.UpdateExtensionTables(eventData, processData)
if globals.FILE_CREATE_NEW == eventData.operation then
if not utils.TableHasKey(processData.createExtensions, eventData.fileExtension) then
processData.createExtensions[eventData.fileExtension] = {}
end
table.insert(processData.createExtensions[eventData.fileExtension], eventData)
elseif globals.FILE_MODIFY == eventData.operation then
if not utils.TableHasKey(processData.modifyExtensions, eventData.fileExtension) then
processData.modifyExtensions[eventData.fileExtension] = {}
end
table.insert(processData.modifyExtensions[eventData.fileExtension], eventData)
elseif globals.FILE_DELETE == eventData.operation then
if not utils.TableHasKey(processData.deleteExtensions, eventData.fileExtension) then
processData.deleteExtensions[eventData.fileExtension] = {}
end
table.insert(processData.deleteExtensions[eventData.fileExtension], eventData)
elseif globals.FILE_RENAME == eventData.operation then
if not utils.TableHasKey(processData.renameExtensions, eventData.fileExtension) then
processData.renameExtensions[eventData.fileExtension] = {}
end
table.insert(processData.renameExtensions[eventData.fileExtension], eventData)
if not utils.TableHasKey(processData.renamePreviousExtensions, eventData.filePreviousExtension) then
processData.renamePreviousExtensions[eventData.filePreviousExtension] = {}
end
table.insert(processData.renamePreviousExtensions[eventData.filePreviousExtension], eventData)
elseif globals.FILE_OVERWRITE == eventData.operation then
if not utils.TableHasKey(processData.overwriteExtensions, eventData.fileExtension) then
processData.overwriteExtensions[eventData.fileExtension] = {}
end
table.insert(processData.overwriteExtensions[eventData.fileExtension], eventData)
end
end
-- Build the list of canary files to be planted.
function globals.BuildCanaries()
local canaries = {}
local canaryDirNames = {}
local canaryFileNames = {}
local canaryExtensions = {'txt', 'doc', 'docx', 'docm', 'dot', 'xls', 'xlsx', 'xlsm', 'ppt', 'pptx', 'pptm'}
-- Generate a canary file content, for now all canary files have the same content.
local canaryContent = globals.CreateCanaryContent()
-- Get the %windir%.
local windowsPath = GetKnownFolderPath('{F38BF404-1D43-42F2-9305-67DE0B28FC23}')
if globals.namespace.diagnosticMode then
canaryDirNames = {
'aaAntiRansomElastic-DO-NOT-TOUCH-def6d40c-a6a1-442c-adc4-9d57a47e58d7',
'zzAntiRansomElastic-DO-NOT-TOUCH-def6d40c-a6a1-442c-adc4-9d57a47e58d7'
}
canaryFileNames = {'AntiRansomElastic-DO-NOT-TOUCH-def8452b-fc17-414d-afb6-ddeceb5ec54c'}
else
canaryDirNames = {
'aaAntiRansomElastic-DO-NOT-TOUCH-dab6d40c-a6a1-442c-adc4-9d57a47e58d7',
'zzAntiRansomElastic-DO-NOT-TOUCH-dab6d40c-a6a1-442c-adc4-9d57a47e58d7'
}
canaryFileNames = {'AntiRansomElastic-DO-NOT-TOUCH-4568452b-fc17-414d-afb6-ddeceb5ec54c'}
end
for _, dirName in ipairs(canaryDirNames) do
for _, fileName in ipairs(canaryFileNames) do
for _, ext in ipairs(canaryExtensions) do
local canaryFileName = fileName .. '.' .. ext
-- Iterate over all user directories.
for _, userProfile in ipairs(utils.GetAllUserProfiles()) do
local canaryFullPath = userProfile .. '\\' .. dirName .. '\\' .. canaryFileName
local canary = globals.Canary(canaryFullPath, canaryContent)
table.insert(canaries, canary)
end
-- Include also the root directory.
local canaryFullPath = windowsPath .. '\\..\\' .. dirName .. '\\' .. canaryFileName
local canary = globals.Canary(canaryFullPath, canaryContent)
table.insert(canaries, canary)
end
end
end
return canaries
end
------------------------------------------------------------------------
-- SENSOR FUNCTIONS
-- These functions are not grouped under a table so they can be called
-- directly by the sensor
------------------------------------------------------------------------
-- Garbage collection routine which manually nils out ProcessData objects that
-- appear to be expired and then invokes the lua garbage collection.
function GarbageCollect()
utils.DebugLog('*** GarbageCollect INVOKED')
utils.DebugLog('[lua] Current Memory Usage: ' .. collectgarbage('count'))
local totalPidsToNil = 0
for _, v1 in pairs(globals.namespaces) do
globals.SwitchNamespace(v1)
local pidsToNil = {}
for k2, v2 in pairs(globals.namespace.processDataTable) do
utils.DebugLog('[lua] PID: ' .. k2)
utils.DebugLog('[lua] totalScore: ' .. v2.totalScore)
utils.DebugLog('[lua] Number events: ' .. #v2.events)
if #v2.events >= globals.PROCESS_EVENT_THRESHOLD then
utils.DebugLog('[lua] Blow away this PID: ' .. k2)
table.insert(pidsToNil, k2)
totalPidsToNil = totalPidsToNil + 1
end
end
for _, v2 in pairs(pidsToNil) do
globals.namespace.processDataTable[v2] = nil
end
end
if totalPidsToNil > 0 then
collectgarbage()
utils.DebugLog('[lua] Cleaned Up Memory Usage: ' .. collectgarbage('count'))
end
return true
end
-- Sends global variable data to the sensor via `lemit`.
function GetGlobals()
local globalsTable = {}
globalsTable.clear_config = true
globalsTable.threshold = globals.PROCESS_EVENT_THRESHOLD * 1.0
globalsTable.regexes = globals.regexIgnorePaths
local ignoreExtensions = {}
for k, v in pairs(globals.extensionMap) do
if globals.EXTENSION_IGNORED == v.category then
utils.DebugLog('IGNORED: ' .. k)
table.insert(ignoreExtensions, k)
end
end
globalsTable.strings = ignoreExtensions
lemit(globalsTable)
return true
end
-- Removes ProcesData object for the given PID from the relevant namespace(s).
-- @param processId integer: The subject process identifier.
function RemoveProcessData(processId)
utils.DebugLog('*** RemoveProcessData INVOKED')
utils.DebugLog('[lua] Removing processId: ' .. processId)
------------------------------------------
globals.CheckForQueuedDiagnosticAlert(processId)
------------------------------------------
for _, v in pairs(globals.namespaces) do
globals.SwitchNamespace(v)
if utils.TableHasKey(globals.namespace.processDataTable, processId) then
globals.namespace.processDataTable[processId] = nil
utils.DebugLog('[lua] processDataTable entry removed: ' .. processId)
else
utils.DebugLog('[lua] Could not find entry in processDataTable: ' .. processId)
end
end
return true
end
-- uses lemit to send a table to the sensor summarizing all event activity observed;
-- intended for use with the LuaRansomware tool to indicate if an alert was generated
function EventsSummary()
local tempTable = {}
local summary = ''
local totalPids = 0
local totalEvents = 0
local totalScore = 0.0
local numRenameExtensions = 0
local operations = ''
for _, v1 in pairs(globals.namespaces) do
totalPids = 0
totalEvents = 0
totalScore = 0.0
for k2, v2 in pairs(v1.processDataTable) do
if utils.TableHasKey(v2, 'parentProcessId') then
summary = summary .. '\nPID: ' .. k2 .. ' | PPID: ' .. v2.parentProcessId .. ' | numEvents: ' ..
#v2.events .. ' | totalScore:' .. v2.totalScore
else
summary = summary .. '\nPID: ' .. k2 .. ' | numEvents: ' .. #v2.events .. ' | totalScore:' ..
v2.totalScore
end
if utils.TableHasKey(v2, 'children') then
for k3, v3 in pairs(v2.children) do
summary = summary .. '\nCHILD PID: ' .. k2 .. ' | SCORE: ' .. v2
end
end
totalPids = totalPids + 1
totalEvents = totalEvents + #v2.events
totalScore = totalScore + v2.totalScore
local renames = 0
for _, v in pairs(v2.renameExtensions) do
renames = 1
break
end
if 1.0 < v2.totalScore then
operations = operations .. '\n' .. utils.PrintOperationTables(v2)
end
end
if 0 < totalPids then
summary = summary .. '\nPIDS: ' .. totalPids .. ' EVENTS: ' .. totalEvents .. ' TOTAL TRACE SCORE: ' ..
totalScore
summary = summary .. operations
end
end
summary = summary .. '\n<END>'
tempTable.raw_data = summary
tempTable.is_alert = globals.alertGenerated
lemit(tempTable)
return true
end
-- Explicitly enable debug logging.
function EnableLogging()
llog('ENABLED')
globals.logging = true
return true
end
-- This function is called by the ransomware plugin during startup to create
-- canary files.
function CanaryStartup()
utils.DebugLog('Canary Startup Called')
local product = utils.GetProduct()
if product ~= 'elastic' then
utils.DebugLog('Not initiating canary startup (non-elastic endpoint)')
return
end
-- Check compatibility (Elastic 8.6.0+)
local incompatible = utils.CurrentVersionLessThan('8.6.0')
if incompatible then
utils.DebugLog('Not initiating canary startup (version < 8.6.0)')
return
end
utils.DebugLog('Elastic endpoint version is compatible with canary files!')
globals.canaryCompatible = true
-- Clean up any leftovers from previous runs.
CanariesCleanup(globals.namespace.diagnosticMode)
-- If we're not dropping canaries, just exit.
if (false == CanariesEnabled()) then
return
end
-- iterate over each namespace.
for _, namespace in pairs(globals.namespaces) do
globals.SwitchNamespace(namespace)
utils.DebugLog('build the list of canary files to be created')
local canaries = globals.BuildCanaries()
if next(canaries) == nil then
utils.DebugLog('failed to build the list of canary files')
return
end
-- iterate over the list of canary files and plant them.
local canaryPlanted = false
utils.DebugLog('planting canary files')
for _, canary in ipairs(canaries) do
-- PlantCanary invokes the endpoint to plant a canary file in a staging directory,
-- (C:\Program Files\Elastic\Endpoint\temp\<staging_dir>). The lua code is explicitly
-- not involving the choice of this staging directory because it is not interesting
-- for the lua side and can be subject to change in the future.
-- Once all canaries have been planted, a call to`CommitCanaries()` is required to make
-- an atomic move from those staging directories to the appropriate target directories.
local canaryStatus =
PlantCanary(canary.fullpath, canary.content, canary.hidden, canary.force, canary.system)
if canaryStatus == nil then
utils.DebugLog('failed to plant canary: ' .. canary.fullpath)
else
-- Keep track of the canary directory names and canary file names,
-- so we can easily make comparisons later in the CanaryCheck.
if not utils.TableHasValue(globals.namespace.canaryDirNames, canary.dirname) then
table.insert(globals.namespace.canaryDirNames, canary.dirname)
end
if not utils.TableHasValue(globals.namespace.canaryFileNames, canary.filename) then
table.insert(globals.namespace.canaryFileNames, canary.filename)
end
canaryPlanted = true
end
end
-- If no canary files were planted, abort.
if canaryPlanted == false then
utils.DebugLog('Failed to drop any canaries!')
return
end
-- all good, keep track that we have successfully droped canaries.
if globals.namespace.diagnosticMode then
globals.diagnosticCanariesDropped = true
else
globals.productionCanariesDropped = true
end
end
return true
end
-- main function invoked by the endpoint/sensor to process individual file events.
-- @data table: A table representing the raw file event.
function main(data)
for _, namespace in pairs(globals.namespaces) do
globals.SwitchNamespace(namespace)
globals.CleanEventData(data)
namespace:Main(data)
end
return true
end
local Ransomware = {}
function Ransomware:new(o)
o = o or {}
o.totalAlerts = 0
o.canaryDirNames = {}
o.canaryFileNames = {}
o.processDataTable = {}
o.totalCanaryCreateFileAlerts = 0
setmetatable(o, self)
self.__index = self
return o
end
-- Creates a process data object. This table tracks numerous properties of the
-- process such as its ransomware score, its parent and its children.
-- @param processId int: Process identifier.
-- @param parentProcessId int: Parent process identifier.
-- @return table: A table representing the process data.
function Ransomware.ProcessData(processId, parentProcessId)
local obj = {}
obj.events = {}
obj.headerMismatchExtensions = {}
obj.numHeaderMismatchExtensions = 0
obj.entropyMismatchExtensions = {}
obj.numEntropyMismatchExtensions = 0
obj.createExtensions = {}
obj.modifyExtensions = {}
obj.deleteExtensions = {}
obj.renameExtensions = {}
obj.renamePreviousExtensions = {}
obj.overwriteExtensions = {}
obj.subExtensions = {}
obj.longExtensions = {}
obj.appendedPaths = {}
-- Represents a table of unique directories in terms of responsibility
-- that the process has touched.
obj.uniqueDirectoriesByResponsibility = {}
obj.createFileNames = {}
obj.totalEventScore = 0.0
obj.trendScore = 0.0
obj.totalScore = 0.0
obj.processId = processId
obj.parentProcessId = parentProcessId
obj.children = {}
obj.childScore = 0.0
obj.diagnosticAlerted = false
obj.diagnosticAlertQueued = false
obj.alerted = false
obj.activeAnalysis = true
obj.eventThresholdExtended = false
return obj
end
-- Creates an event data object. This table contains attributes related the current
-- event such as its file path, filename, entropy, extension.
-- @param inputData table: Process identifier.
-- @return table: A table representing the event data.
function Ransomware:EventData(inputData)
local obj = {}
obj.processId = inputData.processId
obj.operation = inputData.fileOperation
obj.fileExtension = inputData.fileExtension
obj.entropy = inputData.entropy
obj.filePath = inputData.filePath
obj.fileName = obj.filePath:match('[^\\]+$')
-- alternate data streams were leading to inaccurate process scores; ideally these would
-- be removed on the sensor but this will be handled in lua for the time being
obj.filePath = utils.RemoveAdsFromPath(obj.filePath)
obj.fileName = utils.RemoveAdsFromExtension(obj.fileName)
obj.fileExtension = utils.RemoveAdsFromExtension(obj.fileExtension)
obj.headerString = ''
obj.headerBytes = {}
obj.officeLockFile = inputData.officeLockFile
obj.parentProcessId = globals.INVALID_PROCESS_ID
if utils.TableHasKey(inputData, 'parentProcessId') then
obj.parentProcessId = inputData.parentProcessId
end
obj.renameTransition = globals.DEFAULT_RENAME
obj.alertScore = 0.0
obj.multipleExtension = false
obj.alertMetrics = {}
obj.headerMismatch = false
obj.previousHeaderMismatch = false
obj.entropyStatus = globals.ENTROPY_STATUS_DEFAULT
obj.previousEntropyStatus = globals.ENTROPY_STATUS_DEFAULT
obj.numAbnormalExtensionCharacters = 0
if globals.FILE_RENAME == obj.operation then
obj.filePreviousPath = inputData.filePreviousPath
obj.filePreviousExtension = inputData.filePreviousExtension
obj.filePreviousName = obj.filePreviousPath:match('[^\\]+$')
end
if utils.TableHasKey(inputData, 'headerString') then
obj.headerString = inputData.headerString
elseif utils.TableHasKey(inputData, 'headerBytes') then
obj.headerString = inputData.headerBytes
end
obj.headerBytes = utils.StringToByteArray(obj.headerString)
obj.headerString = utils.Hexlify(obj.headerString)
obj.currentExtensionData = nil
obj.previousExtensionData = nil
obj.normalizedPath = utils.NormalizePath(obj.filePath)
self.SetExtensionData(obj)
return obj
end
-- Create an extension object for the given event data. In case of a rename
-- operation, an additional `previous` extension object is created.
-- @param eventData table: A table containing event data.
-- @return void.
function Ransomware.SetExtensionData(eventData)
if utils.TableHasKey(globals.extensionMap, eventData.fileExtension) then
eventData.currentExtensionData = globals.extensionMap[eventData.fileExtension]
else
eventData.currentExtensionData = globals.unknownExtensionData
end
if globals.FILE_RENAME == eventData.operation then
if eventData.filePreviousExtension ~= eventData.fileExtension then
if utils.TableHasKey(globals.extensionMap, eventData.filePreviousExtension) then
eventData.previousExtensionData = globals.extensionMap[eventData.filePreviousExtension]
else
eventData.previousExtensionData = globals.unknownExtensionData
end
else
eventData.previousExtensionData = eventData.currentExtensionData
end
end
end
-- Turns off active analysis for a specified process.
-- @param processData table: A table containing process data.
-- @return void.
function Ransomware.StopActiveAnalysis(processData)
processData.activeAnalysis = false
end
-- Sends a message to the endpoint informing it to stop monitoring a specified
-- process.
-- @param processData table: A table containing process data.
-- @return table: Table representing the process table.
function Ransomware:SendStopActiveAnalysisMsg(processData)
utils.PrintExtensionTables(processData)
utils.PrintOperationTables(processData)
self.StopActiveAnalysis(processData)
local processTable = {}
processTable.pid = processData.processId
processTable.is_alert = false
-- The sensor/endpoint still both use the 'beta_alert' key
processTable.beta_alert = self.diagnosticMode
processTable.score = processData.totalScore
lemit(processTable)
return processTable
end
-- Skips duplicate file events.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return boolean: True when the event is a duplicate, False otherwise.
function Ransomware.DuplicateEventCheck(eventData, processData)
for _, v in pairs(processData.events) do
if v.filePath == eventData.filePath then
if v.operation == eventData.operation then
if globals.FILE_RENAME ~= v.operation then
utils.DebugLog('SKIPPING DUPLICATE EVENT: ' .. eventData.filePath)
return true
elseif v.filePreviousPath == eventData.filePreviousPath then
utils.DebugLog('SKIPPING DUPLICATE RENAME: ' .. eventData.filePath)
return true
else
utils.DebugLog('VALID rename: ' .. eventData.filePath)
return false
end
end
end
end
return false
end
-- PathHistory evaluates the current event and how it relates to previous events
-- involving the same filepath within the same process. In particular, we seek to
-- find anomalous file modification patterns that may not be apparent when analyzing
-- the current event in a vacuum (e.g. deleting and creating the same filepath).
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return void.
function Ransomware.PathHistory(eventData, processData)
local pathEvents = {}
local previousPathEvents = {}
local pathEventTable = {}
for _, v in pairs(processData.events) do
if not utils.TableHasKey(pathEventTable, v.filePath) then
pathEventTable[v.filePath] = {}
end
table.insert(pathEventTable[v.filePath], v.operation)
end
for _, v in pairs(processData.events) do
if v.filePath == eventData.filePath then
if v.operation == eventData.operation then
utils.DebugLog('SKIPPING DUPLICATE EVENT (DuplicateEventCheck fail): ' .. eventData.filePath)
return true
end
table.insert(pathEvents, v)
elseif globals.FILE_RENAME == eventData.operation then
if v.filePath == eventData.filePreviousPath then
utils.DebugLog('added to previousPathEvents: ' .. v.filePath)
table.insert(previousPathEvents, v)
end
elseif globals.FILE_DELETE == eventData.operation then
local subString = string.find(v.filePath, eventData.filePath, nil, true)
if nil ~= subString then
utils.DebugLog('deleted filePath found as substring in different event with different filePath')
-- Was this filepath previously created or renamed? if so, BREAK
local prevCreate = false
local prevRename = false
if nil ~= pathEventTable[eventData.filePath] then
for _, prevOperation in pairs(pathEventTable[eventData.filePath]) do
if globals.FILE_CREATE_NEW == prevOperation then
utils.DebugLog('globals.FILE_CREATE_NEW == prevOperation')
prevCreate = true
break
elseif globals.FILE_RENAME == prevOperation then
utils.DebugLog('globals.FILE_RENAME == prevOperation')
prevRename = true
break
end
end
end
if prevCreate then
utils.DebugLog('prevCreate detected for eventData.filePath')
break
elseif prevRename then
utils.DebugLog('prevRename detected for eventData.filePath')
break
end
alert.RaiseFileAlertMetric(eventData, 'DELETED_PATH_SUBSTRING_FOUND')
if globals.FILE_CREATE_NEW == v.operation then
utils.DebugLog('substring was previously created...')
local prevDelete = false
for _, prevOperation in pairs(pathEventTable[v.filePath]) do
if globals.FILE_DELETE == prevOperation then
utils.DebugLog('globals.FILE_DELETE == prevOperation')
prevDelete = true
break
end
end
if prevDelete then
utils.DebugLog('prevDelete detected for v.filePath!!!')
break
end
if globals.EXTENSION_SUSPICIOUS == v.currentExtensionData.category then
utils.DebugLog('ALERT_SCORE_CHANGE: DELETE_EXTENSION_BLOCKLIST_PREVIOUSLY_CREATED_FILEPATH: ' ..
globals.config.DELETE_EXTENSION_BLOCKLIST_PREVIOUSLY_CREATED_FILEPATH['score'])
eventData.alertScore = eventData.alertScore +
globals.config.DELETE_EXTENSION_BLOCKLIST_PREVIOUSLY_CREATED_FILEPATH['score']
alert.RaiseFileAlertMetric(eventData, 'DELETE_EXTENSION_BLOCKLIST_PREVIOUSLY_CREATED_FILEPATH')
end
if globals.ENTROPY_REALLY_HIGH < v.entropy and eventData.currentExtensionData.lowEntropy then
if globals.EXTENSION_UNKNOWN == v.currentExtensionData.category then
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST: ' ..
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST['score'])
eventData.alertScore = eventData.alertScore + globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST')
end
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHEST: ' ..
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHEST['score'])
eventData.alertScore = eventData.alertScore + globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHEST['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHEST')
elseif globals.ENTROPY_VERY_HIGH < v.entropy and eventData.currentExtensionData.lowEntropy then
if globals.EXTENSION_UNKNOWN == v.currentExtensionData.category then
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER: ' ..
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER['score'])
eventData.alertScore = eventData.alertScore + globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER')
end
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHER: ' ..
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHER['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHER['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGHER')
elseif globals.ENTROPY_HIGH < v.entropy and eventData.currentExtensionData.lowEntropy then
if globals.EXTENSION_UNKNOWN == v.currentExtensionData.category then
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH: ' ..
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH')
end
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGH: ' ..
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGH['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGH['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_EXTENSION_KNOWN_WITH_LOW_ENTROPY_WITH_PREVIOUSLY_CREATED_SUBSTRING_POSSIBLE_MISMATCH_ENTROPY_HIGH')
elseif globals.ENTROPY_STATUS_REALLY_HIGH == v.entropyStatus then
if globals.EXTENSION_UNKNOWN == v.currentExtensionData.category then
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST: ' ..
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHEST')
end
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHEST: ' ..
globals.config.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHEST['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHEST['score']
alert.RaiseFileAlertMetric(eventData, 'DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHEST')
elseif globals.ENTROPY_STATUS_VERY_HIGH == v.entropyStatus then
if globals.EXTENSION_UNKNOWN == v.currentExtensionData.category then
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER: ' ..
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGHER')
end
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHER: ' ..
globals.config.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHER['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHER['score']
alert.RaiseFileAlertMetric(eventData, 'DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGHER')
elseif globals.ENTROPY_STATUS_HIGH == v.entropyStatus then
if globals.EXTENSION_UNKNOWN == v.currentExtensionData.category then
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH: ' ..
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN_ENTROPY_HIGH')
end
utils.DebugLog('ALERT_SCORE_CHANGE: DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGH: ' ..
globals.config.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGH['score'])
eventData.alertScore = eventData.alertScore +
globals.config.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGH['score']
alert.RaiseFileAlertMetric(eventData, 'DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_ENTROPY_HIGH')
else
if globals.EXTENSION_UNKNOWN == v.currentExtensionData.category then
utils.DebugLog(
'ALERT_SCORE_CHANGE: DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN: ' ..
globals.config.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN['score']
alert.RaiseFileAlertMetric(eventData,
'DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING_EXTENSION_UNKNOWN')
end
utils.DebugLog('ALERT_SCORE_CHANGE: DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING: ' ..
globals.config.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING['score'])
eventData.alertScore = eventData.alertScore +
globals.config.DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING['score']
alert.RaiseFileAlertMetric(eventData, 'DELETE_WITH_PREVIOUSLY_CREATED_SUBSTRING')
end
end
break
else
subString = string.find(v.fileName, '^' .. eventData.fileName, nil, true)
if nil ~= subString then
-- NOTE: commented out since currently unreliable
-- delete then recreated?
end
end
elseif globals.FILE_CREATE_NEW == eventData.operation and globals.FILE_CREATE_NEW == v.operation and
eventData.fileName == v.fileName then
if eventData.entropy == v.entropy then
-- NOTE: could potentially use this in the future for possibly
-- detecting ransom note drops, but need to have hashes as well
-- to determine equivalence.
end
elseif globals.FILE_CREATE_NEW == eventData.operation then
local subString = string.find(eventData.filePath, v.filePath, nil, true)
if nil ~= subString and globals.FILE_DELETE == v.operation then
local noCreate = true
for _, prevOperation in pairs(pathEventTable[v.filePath]) do
if globals.FILE_CREATE_NEW == prevOperation then
noCreate = false
utils.DebugLog('globals.FILE_CREATE_NEW == prevOperation')
end
end
if noCreate then
utils.DebugLog('created filePath contains previously deleted filePath as substring')
utils.DebugLog('v.filePath: ' .. v.filePath)
utils.DebugLog('eventData.filePath: ' .. eventData.filePath)
if eventData.headerMismatch then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_EXTENSION_KNOWN_HEADER_MISMATCH_WITH_PREVIOUSLY_DELETED_SUBSTRING: ' ..
globals.config.CREATE_EXTENSION_KNOWN_HEADER_MISMATCH_WITH_PREVIOUSLY_DELETED_SUBSTRING['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_EXTENSION_KNOWN_HEADER_MISMATCH_WITH_PREVIOUSLY_DELETED_SUBSTRING['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_EXTENSION_KNOWN_HEADER_MISMATCH_WITH_PREVIOUSLY_DELETED_SUBSTRING')
else
utils.DebugLog('no headerMismatch')
end
utils.DebugLog('eventData.fileExtension: ' .. eventData.fileExtension)
if not utils.TableHasKey(globals.extensionMap, eventData.fileExtension) then
if globals.ENTROPY_STATUS_REALLY_HIGH == eventData.entropyStatus then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHEST: ' ..
globals.config.CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHEST['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHEST['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHEST')
elseif globals.ENTROPY_STATUS_VERY_HIGH == eventData.entropyStatus then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHER: ' ..
globals.config.CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHER['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHER['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGHER')
elseif globals.ENTROPY_STATUS_HIGH == eventData.entropyStatus then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGH: ' ..
globals.config.CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGH['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGH['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_WITH_PREVIOUSLY_DELETED_FILEPATH_SUBSTRING_ENTROPY_HIGH')
end
end
break
end
end
end
end
end
-- HeaderCheck is a wrapper over the HeaderComparison function. It performs some
-- sanity checks and filtering of MS office lock files.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return void.
function Ransomware:HeaderCheck(eventData, processData)
if nil == next(eventData.headerBytes) then
return
end
if eventData.officeLockFile then
utils.DebugLog('office lock file skip header check: ' .. eventData.filePath)
return
end
eventData.headerMismatch = self.HeaderComparison(eventData, processData, eventData.currentExtensionData)
utils.DebugLog('eventData.headerMismatch: ' .. tostring(eventData.headerMismatch))
if globals.FILE_RENAME == eventData.operation and eventData.filePreviousExtension ~= eventData.fileExtension then
eventData.previousHeaderMismatch =
self.HeaderComparison(eventData, processData, eventData.previousExtensionData)
utils.DebugLog('eventData.previousHeaderMismatch: ' .. tostring(eventData.previousHeaderMismatch))
end
end
-- Compares the file header to expected magic byte sequences (if they exist)
-- pertaining to its file extension. If sequences exist but no suitable match is
-- found, this anomaly is accounted for and will affect scoring.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @param extensionData: A table containing extension data.
-- @return boolean: True when the a header mismatch is found. False otherwise.
function Ransomware.HeaderComparison(eventData, processData, extensionData)
local headerMismatch = false
if nil == extensionData then
return headerMismatch
end
if #extensionData.magicBytes == 0 then
return headerMismatch
end
local magicBytesTable = extensionData.magicBytes
table.insert(magicBytesTable, globals.t_null_1)
table.insert(magicBytesTable, globals.t_xml_1)
for _, v in pairs(magicBytesTable) do
headerMismatch = false
local bar = table.move(v, 1, 16, 1, {})
local subHeader = table.move(eventData.headerBytes, 1, #v, 1, {})
-- byte by byte comparison of header with known magic byte sequence
for k4, v4 in pairs(subHeader) do
-- utils.DebugLog('subHeader: ' .. v4 .. ' | compare: ' .. bar[k4])
if v4 ~= bar[k4] then
headerMismatch = true
break
end
end
if false == headerMismatch then
break
end
end
if true == headerMismatch then
alert.RaiseFileAlertMetric(eventData, 'HEADER_MISMATCH')
end
-- If the file doesn't match its expected magic byte sequence, the ProcessData object will
-- be checked to determine if any previous mismatches have occurred. Once the number of
-- unique extensions modified meets / exceeds EXTENSION_HEADER_MISMATCH_THRESHOLD,
-- subsequent mismatches will affect the process alert score (prior mismatches will not)
-- NOTE: this is now explicitly enforced in TotalIndividualScore
local fileExtension = ''
if extensionData == eventData.currentExtensionData then
fileExtension = eventData.fileExtension
else
fileExtension = eventData.filePreviousExtension
end
if headerMismatch then
if not utils.TableHasKey(processData.headerMismatchExtensions, fileExtension) then
processData.headerMismatchExtensions[fileExtension] = 0
utils.DebugLog('NEW EXTENSION HEADER MISMATCH: ' .. fileExtension)
processData.numHeaderMismatchExtensions = processData.numHeaderMismatchExtensions + 1
end
processData.headerMismatchExtensions[fileExtension] = processData.headerMismatchExtensions[fileExtension] + 1
end
return headerMismatch
end
-- EntropyCheck is a wrapper over the EntropyComparison function. It performs
-- two entropy comparisons checks; the first one on the extension data of
-- the current event, and the second one on the extension data of original
-- extension data before the rename operation took place.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return void.
function Ransomware:EntropyCheck(eventData, processData)
eventData.entropyStatus = self:EntropyComparison(eventData, processData, eventData.fileExtension,
eventData.currentExtensionData)
if globals.FILE_RENAME == eventData.operation and eventData.filePreviousExtension ~= eventData.fileExtension then
eventData.previousEntropyStatus = self:EntropyComparison(eventData, processData,
eventData.filePreviousExtension, eventData.previousExtensionData)
end
end
-- EntropyComparison evaluates the entropy of a (portion) of the file against
-- a range of predefined entropy values. When an entropy is found to be high,
-- it raises a file alert metric.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @param fileExtension string: The name of the extension.
-- @param extensionData: A table containing extension data.
-- @return integer: An integer representing the entropy status.
function Ransomware:EntropyComparison(eventData, processData, fileExtension, extensionData)
local entropyStatus = globals.ENTROPY_STATUS_DEFAULT
local entropyString = 'ENTROPY_DEFAULT'
if globals.EXTENSION_IGNORED == extensionData.category then
return entropyStatus
end
if globals.ENTROPY_REALLY_HIGH < eventData.entropy then
if extensionData.lowEntropy then
-- Extension is typically low entropy; alert signifies it exceeded its range.
entropyString = 'ENTROPY_MISMATCH_REALLY_HIGH'
entropyStatus = globals.ENTROPY_STATUS_MISMATCH_REALLY_HIGH
self.EntropyMismatch(processData, fileExtension)
else
entropyString = 'ENTROPY_REALLY_HIGH'
entropyStatus = globals.ENTROPY_STATUS_REALLY_HIGH
end
elseif globals.ENTROPY_VERY_HIGH < eventData.entropy then
if extensionData.lowEntropy then
-- Extension is typically low entropy; alert signifies it exceeded its range.
entropyString = 'ENTROPY_MISMATCH_VERY_HIGH'
entropyStatus = globals.ENTROPY_STATUS_MISMATCH_VERY_HIGH
self.EntropyMismatch(processData, fileExtension)
else
entropyString = 'ENTROPY_VERY_HIGH'
entropyStatus = globals.ENTROPY_STATUS_VERY_HIGH
end
elseif globals.ENTROPY_HIGH < eventData.entropy then
entropyString = 'ENTROPY_HIGH'
entropyStatus = globals.ENTROPY_STATUS_HIGH
end
if 'ENTROPY_DEFAULT' ~= entropyString then
alert.RaiseFileAlertMetric(eventData, entropyString)
end
return entropyStatus
end
-- EntropyMismatch keeps track of the mismatched extensions per process.
-- @param processData table: A table containing process data.
-- @param fileExtension string: The name of the extension.
-- @return integer: An integer representing the new number of mismatched extensions
-- for the given extension in the current process.
function Ransomware.EntropyMismatch(processData, fileExtension)
if not utils.TableHasKey(processData.entropyMismatchExtensions, fileExtension) then
processData.entropyMismatchExtensions[fileExtension] = 0
utils.DebugLog('NEW ENTROPY MISMATCH EXTENSION: ' .. fileExtension)
processData.numEntropyMismatchExtensions = processData.numEntropyMismatchExtensions + 1
end
utils.DebugLog('ENTROPY MISMATCH EXTENSION: ' .. fileExtension)
processData.entropyMismatchExtensions[fileExtension] = processData.entropyMismatchExtensions[fileExtension] + 1
return processData.entropyMismatchExtensions[fileExtension]
end
-- RenameCheck examines the transition of the extension during a rename operation.
-- For example: transitioning from a known extension to a ransomware extension.
-- @param eventData table: A table containing event data.
-- @return integer: An integer representing the rename transition.
function Ransomware:RenameCheck(eventData)
local previousExtensionKnown = false
local currentExtensionSuspicious = self.IsRansomExtension(eventData)
if utils.TableHasKey(globals.extensionMap, eventData.filePreviousExtension) then
previousExtensionKnown = true
end
local currentExtensionKnown = false
if utils.TableHasKey(globals.extensionMap, eventData.fileExtension) then
currentExtensionKnown = true
end
local renameString = 'DEFAULT_RENAME'
if previousExtensionKnown and currentExtensionSuspicious then
eventData.renameTransition = globals.KNOWN_TO_SUSPICIOUS
renameString = 'KNOWN_TO_SUSPICIOUS'
elseif previousExtensionKnown and not currentExtensionKnown then
eventData.renameTransition = globals.KNOWN_TO_UNKNOWN
renameString = 'KNOWN_TO_UNKNOWN'
elseif previousExtensionKnown and '' == eventData.fileExtension then
eventData.renameTransition = globals.KNOWN_TO_BLANK
renameString = 'KNOWN_TO_BLANK'
elseif not previousExtensionKnown and currentExtensionSuspicious then
eventData.renameTransition = globals.UNKNOWN_TO_SUSPICIOUS
renameString = 'UNKNOWN_TO_SUSPICIOUS'
elseif not previousExtensionKnown and not currentExtensionKnown then
eventData.renameTransition = globals.UNKNOWN_TO_UNKNOWN
renameString = 'UNKNOWN_TO_UNKNOWN'
end
if 'DEFAULT_RENAME' ~= renameString then
alert.RaiseFileAlertMetric(eventData, renameString)
end
return eventData.renameTransition
end
-- AbnormalExtensionCheck performs various heuristics over the sub parts
-- of a file extension. It iterates over the sub-extensions and examines how
-- they relate to the final extension. For example, a common scenario is when
-- the sub-extension is known and the final extension is unknown.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return void.
function Ransomware.AbnormalExtensionCheck(eventData, processData)
-- TODO: Are all renames made to the same extension format? If we could
-- establish that activity across the board that would be strong proof of a
-- process-wide pattern.
-- Are there multiple extensions?
-- Are one or more extensions legitimate? is the final extension unknown / suspicious?
-- This regex will split the string based on the first instance of '.' and
-- return the second string: e.g. sample.txt.doc ==> txt.doc
local longExtension = eventData.fileName:match('%.(.*)')
if longExtension == nil or longExtension == eventData.fileExtension then
return
end
utils.DebugLog('fileExtension: ' .. eventData.fileExtension .. ' | longExtension: ' .. longExtension)
eventData.multipleExtension = true
-- This regex will split the string based on each instance of '.' in
-- order to iterate through each 'subextension' of the original filename
-- e.g. txt.doc.encrypted ==> {txt, doc, encrypted}
for word in string.gmatch(longExtension, '([^%.]+)') do
utils.DebugLog('WORD FOUND IN LONG EXTENSION: ' .. word)
if word == eventData.fileExtension then
goto continue
end
if not utils.TableHasKey(processData.longExtensions, word) then
processData.longExtensions[word] = 1
else
processData.longExtensions[word] = processData.longExtensions[word] + 1
end
if utils.TableHasKey(globals.extensionMap, string.lower(word)) then
utils.DebugLog('SUBEXTENSION_KNOWN: ' .. word)
utils.DebugLog('ALERT_SCORE_CHANGE: SUBEXTENSION_KNOWN: ' .. globals.config.SUBEXTENSION_KNOWN['score'])
eventData.alertScore = eventData.alertScore + globals.config.SUBEXTENSION_KNOWN['score']
alert.RaiseFileAlertMetric(eventData, 'SUBEXTENSION_KNOWN')
if globals.FILE_CREATE_NEW == eventData.operation and globals.EXTENSION_UNKNOWN ==
eventData.currentExtensionData.category then
utils.DebugLog('CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN: ' .. word)
utils.DebugLog('ALERT_SCORE_CHANGE: CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN: ' ..
globals.config.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN['score'])
eventData.alertScore = eventData.alertScore +
globals.config.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN['score']
alert.RaiseFileAlertMetric(eventData, 'CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN')
if not utils.TableHasKey(processData.subExtensions, word) then
processData.subExtensions[word] = 0
utils.DebugLog('NEW subextension: ' .. word)
end
processData.subExtensions[word] = processData.subExtensions[word] + 1
local totalSubs = 0
local totalUniqueSubs = 0
for _, v in pairs(processData.subExtensions) do
totalSubs = totalSubs + v
totalUniqueSubs = totalUniqueSubs + 1
end
utils.DebugLog('Unique sub-extensions: ' .. totalUniqueSubs .. ' | total entries: ' .. totalSubs)
if 4 < totalUniqueSubs and 100 < totalSubs then
utils.DebugLog('ALERT_SCORE_CHANGE #2.1')
if globals.ENTROPY_STATUS_REALLY_HIGH == eventData.entropyStatus then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHEST: ' ..
globals.config.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHEST['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHEST['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHEST')
elseif globals.ENTROPY_STATUS_VERY_HIGH == eventData.entropyStatus then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHER: ' ..
globals.config.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHER['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHER['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGHER')
elseif globals.ENTROPY_STATUS_HIGH == eventData.entropyStatus then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGH: ' ..
globals.config.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGH['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGH['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_HIGH')
else
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_AVERAGE: ' ..
globals.config.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_AVERAGE['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_AVERAGE['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_AVERAGE')
end
-- for k,v in pairs (processData.subExtensions) do
-- utils.DebugLog(k .. ': ' .. v)
-- end
-- if 4 < #processData.deleteExtensions then
-- eventData.alertScore = eventData.alertScore + 0.5
-- else
-- eventData.alertScore = eventData.alertScore + 0.005
-- end
-- alert.RaiseFileAlertMetric(eventData, 'CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_THRESHOLD_ENTROPY_AVERAGE')
end
if utils.TableHasKey(processData.deleteExtensions, word) then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED: ' ..
globals.config.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_EXTENSION_UNKNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED')
end
elseif globals.FILE_CREATE_NEW == eventData.operation then
utils.DebugLog('ALERT_SCORE_CHANGE: CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN: ' ..
globals.config.CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN['score'])
eventData.alertScore = eventData.alertScore +
globals.config.CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN['score']
alert.RaiseFileAlertMetric(eventData, 'CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN')
if utils.TableHasKey(processData.deleteExtensions, word) then
utils.DebugLog(
'ALERT_SCORE_CHANGE: CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED: ' ..
globals.config.CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED['score'])
eventData.alertScore = eventData.alertScore +
globals.config
.CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED['score']
alert.RaiseFileAlertMetric(eventData,
'CREATE_EXTENSION_KNOWN_SUBEXTENSION_KNOWN_AND_PREVIOUSLY_DELETED')
end
elseif globals.EXTENSION_UNKNOWN == eventData.currentExtensionData.category then
utils.DebugLog('ALERT_SCORE_CHANGE: SUBEXTENSION_KNOWN_EXTENSION_UNKNOWN: ' ..
globals.config.SUBEXTENSION_KNOWN_EXTENSION_UNKNOWN['score'])
eventData.alertScore = eventData.alertScore +
globals.config.SUBEXTENSION_KNOWN_EXTENSION_UNKNOWN['score']
alert.RaiseFileAlertMetric(eventData, 'SUBEXTENSION_KNOWN_EXTENSION_UNKNOWN')
end
-- Maybe also check entropy and header mismatches here?
-- Also check if this extension has also been deleted!
else
for k, _ in pairs(processData.deleteExtensions) do
if word == k then
utils.DebugLog('SUBEXTENSION_UNKNOWN_AND_PREVIOUSLY_DELETED' .. k)
utils.DebugLog('ALERT_SCORE_CHANGE: SUBEXTENSION_UNKNOWN_AND_PREVIOUSLY_DELETED: ' ..
globals.config.SUBEXTENSION_UNKNOWN_AND_PREVIOUSLY_DELETED['score'])
eventData.alertScore = eventData.alertScore +
globals.config.SUBEXTENSION_UNKNOWN_AND_PREVIOUSLY_DELETED['score']
alert.RaiseFileAlertMetric(eventData, 'SUBEXTENSION_UNKNOWN_AND_PREVIOUSLY_DELETED')
end
end
end
::continue::
end
end
-- CanaryCheck is a wrapper over the Lua_CanaryCheck function. It raises a file
-- alert metric and generates an alert when a file event targets our planted
-- canary files.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return boolean: True when a canary activity is seen. False otherwise.
function Ransomware:CanaryCheck(eventData, processData)
-- CanaryCheck limits file creation alerts to globals.CANARY_CREATE_FILE_ALERT_CAP.
if true == globals.Lua_CanaryCheck(eventData) then
table.insert(processData.events, eventData)
if self.diagnosticMode then
processData.beta_alert = true
end
processData.canary_alert = true
alert.RaiseFileAlertMetric(eventData, 'CANARY_ACTIVITY')
-- Update the process extension table (for debugging / display purposes only).
globals.UpdateExtensionTables(eventData, processData)
alert.GenerateAlert(processData, self.diagnosticMode)
return true
end
return false
end
-- Verifies if the extension of the current file event belongs to known ransomware
-- family. When it finds so, it raises a file alert metric.
-- @param eventData table: A table containing event data.
-- @returns boolean: True if the current file events has a ransomware extension,
-- False otherwise.
function Ransomware.IsRansomExtension(eventData)
if globals.EXTENSION_SUSPICIOUS == eventData.currentExtensionData.category then
alert.RaiseFileAlertMetric(eventData, 'RANSOM_EXTENSION')
return true
end
return false
end
-- RansomNoteCheck checks for ransom notes dropped by ransomware. Currently,
-- this check only looks at the filename rather than the file content.
-- @param processData table: A table containing process data.
-- @return void.
function Ransomware.RansomNoteCheck(processData)
-- List of suspicious words seen in ransom note filenames.
local suspiciousWords = {
'crypt',
'lock',
'help',
'save',
'recover',
'restore',
'return',
'read',
'repair',
'instruction',
'attention',
'files',
'coin',
'vault',
'how',
'pay',
'please',
'back',
'worry'
}
-- List of suspicious extensions seen in ransom notes.
local suspiciousExts = {'txt', 'hta', 'rtf', 'png', 'jpg', 'bmp', 'htm', 'html'}
local calculateTrendScore = function(mostCreatedFileName, countOfFiles)
-- lower case the file name and extension.
-- ToDo: replace some characters used to bypass file name matching :
-- cerber: _R_E_A_D___T_H_I_S___[]_.hta
local fileName = mostCreatedFileName:lower()
local extension = processData.createFileNames[mostCreatedFileName][1].fileExtension:lower()
-- [1] check if the file name contains any of the suspicious words.
local suspiciousWordCount = 0
for _, suspiciousWord in pairs(suspiciousWords) do
local match = string.find(fileName, suspiciousWord, nil, true)
if match then
suspiciousWordCount = suspiciousWordCount + 1
end
end
-- [2] check if the file name contains a suspicious extension.
local suspiciousExtension = 0
local suspiciousExtensionName = nil
for _, suspiciousExt in pairs(suspiciousExts) do
if suspiciousExt == extension then
suspiciousExtension = 1
suspiciousExtensionName = extension
break
end
end
-- [3] check if the file has been seen in locations that serve different responsibilities.
local uniqueDirectoriesByResponsibility = {}
local uniqueDirectoriesByResponsibilityCount = 0
for _, fileNameTable in pairs(processData.createFileNames[mostCreatedFileName]) do
local normalizedPath = utils.NormalizePath(fileNameTable.filePath)
if not utils.TableHasValue(uniqueDirectoriesByResponsibility, normalizedPath) then
table.insert(uniqueDirectoriesByResponsibility, normalizedPath)
uniqueDirectoriesByResponsibilityCount = uniqueDirectoriesByResponsibilityCount + 1
end
end
if uniqueDirectoriesByResponsibilityCount > 0 then
uniqueDirectoriesByResponsibilityCount = #uniqueDirectoriesByResponsibility - 1
end
-- if we fail to meet any of the criteria below, the trendScore will be
-- equal to zero (because the values are multiplied):
-- 1. suspicious extension.
-- 2. suspicious word in the filename.
-- 3. more than one file path in terms of responsibility has been touched.
-- 4. the number of files created with this filename must be three or more
-- (enforced before calculateTrendScore is invoked).
local trendScore = 0.0
trendScore = suspiciousWordCount * suspiciousExtension * uniqueDirectoriesByResponsibilityCount * countOfFiles
if trendScore == 0 then
return trendScore
end
-- [5] print some debug logs.
utils.DebugLog(mostCreatedFileName .. ' was created ' .. countOfFiles .. ' times')
if suspiciousWordCount > 0 then
utils.DebugLog(mostCreatedFileName .. ' contains ' .. suspiciousWordCount .. ' suspicious word(s)')
end
if suspiciousExtension > 0 then
utils.DebugLog(mostCreatedFileName .. ' contains a suspicious extension: ' .. suspiciousExtensionName)
end
if uniqueDirectoriesByResponsibilityCount > 0 then
utils.DebugLog(mostCreatedFileName .. ' was created in directories that serve different responsibilities: ')
utils.PrintTable(uniqueDirectoriesByResponsibility)
end
utils.DebugLog('ransom note detection trend score: ' .. trendScore)
return trendScore
end
-- Walk over created files and calculate the trend score for each set of
-- created file names. To have a deterministic and consistent result across
-- multiple runs, we choose the maximum score value calculated.
local trendScore = 0.0
local countOfFiles = 0
local mostCreatedFileName = nil
local maxTrendSCore = 0.0
for fileName, fileNameTable in pairs(processData.createFileNames) do
-- We require at last 3 files to keep processing.
if #processData.createFileNames[fileName] >= 3 then
countOfFiles = #fileNameTable
mostCreatedFileName = fileName
trendScore = calculateTrendScore(mostCreatedFileName, countOfFiles)
if trendScore > maxTrendSCore then
maxTrendSCore = trendScore
end
end
end
processData.trendScore = processData.trendScore + maxTrendSCore
end
-- TotalIndividualScore tallies up the score for this file event after all checks
-- are made. A file alert metric is raised when any of the following conditions
-- are met:
-- 1) When a header or entropy mismatch threshold is reached.
-- 2) When a ransomware extension is found.
-- 3) In a rename operation, the previous file path has a high entropy mismatch
-- or a rename transition is found.
-- 4) When abnormal extension characters are found.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return void.
function Ransomware:TotalIndividualScore(eventData, processData)
if processData.numHeaderMismatchExtensions > 0 then
utils.DebugLog('processData.numHeaderMismatchExtensions: ' .. processData.numHeaderMismatchExtensions)
end
if eventData.headerMismatch then
if globals.HEADER_MISMATCH_THRESHOLD <= processData.numHeaderMismatchExtensions then
utils.DebugLog('processData.numHeaderMismatchExtensions: ' .. processData.numHeaderMismatchExtensions)
utils.DebugLog('ALERT_SCORE_CHANGE: HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET: ' ..
(globals.config.HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET['score'] *
processData.numHeaderMismatchExtensions))
eventData.alertScore = eventData.alertScore +
(globals.config.HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET['score'] *
processData.numHeaderMismatchExtensions)
alert.RaiseFileAlertMetric(eventData, 'HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET')
end
elseif eventData.previousHeaderMismatch then
if globals.HEADER_MISMATCH_THRESHOLD <= processData.numHeaderMismatchExtensions then
utils.DebugLog('processData.numHeaderMismatchExtensions: ' .. processData.numHeaderMismatchExtensions)
utils.DebugLog('ALERT_SCORE_CHANGE: PREVIOUS_HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET: ' ..
(globals.config.PREVIOUS_HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET['score'] *
processData.numHeaderMismatchExtensions))
eventData.alertScore = eventData.alertScore +
(globals.config.PREVIOUS_HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET['score'] *
processData.numHeaderMismatchExtensions)
alert.RaiseFileAlertMetric(eventData, 'PREVIOUS_HEADER_MISMATCH_EXTENSIONS_THRESHOLD_MET')
end
end
if globals.ENTROPY_STATUS_MISMATCH_REALLY_HIGH == eventData.entropyStatus then
if globals.ENTROPY_MISMATCH_THRESHOLD <= processData.numEntropyMismatchExtensions then
utils.DebugLog('ALERT_SCORE_CHANGE: ENTROPY_MISMATCH_HIGHEST: ' ..
(globals.config.ENTROPY_MISMATCH_HIGHEST['score'] *
processData.numHeaderMismatchExtensions))
eventData.alertScore = eventData.alertScore +
(globals.config.ENTROPY_MISMATCH_HIGHEST['score'] *
processData.numEntropyMismatchExtensions)
alert.RaiseFileAlertMetric(eventData, 'ENTROPY_MISMATCH_HIGHEST')
if eventData.headerMismatch then
utils.DebugLog('ALERT_SCORE_CHANGE: ENTROPY_MISMATCH_HIGHEST_WITH_HEADER_MISMATCH: ' ..
globals.config.ENTROPY_MISMATCH_HIGHEST_WITH_HEADER_MISMATCH['score'])
eventData.alertScore = eventData.alertScore +
globals.config.ENTROPY_MISMATCH_HIGHEST_WITH_HEADER_MISMATCH['score']
alert.RaiseFileAlertMetric(eventData, 'ENTROPY_MISMATCH_HIGHEST_WITH_HEADER_MISMATCH')
end
end
elseif globals.ENTROPY_STATUS_MISMATCH_VERY_HIGH == eventData.entropyStatus then
if globals.ENTROPY_MISMATCH_THRESHOLD <= processData.numEntropyMismatchExtensions then
utils.DebugLog('ALERT_SCORE_CHANGE: ENTROPY_MISMATCH_HIGHER: ' ..
(globals.config.ENTROPY_MISMATCH_HIGHER['score'] *
processData.numEntropyMismatchExtensions))
eventData.alertScore = eventData.alertScore +
(globals.config.ENTROPY_MISMATCH_HIGHER['score'] *
processData.numEntropyMismatchExtensions)
alert.RaiseFileAlertMetric(eventData, 'ENTROPY_MISMATCH_HIGHER')
if eventData.headerMismatch then
utils.DebugLog('ALERT_SCORE_CHANGE: ENTROPY_MISMATCH_HIGHER_WITH_HEADER_MISMATCH: ' ..
globals.config.ENTROPY_MISMATCH_HIGHER_WITH_HEADER_MISMATCH['score'])
eventData.alertScore = eventData.alertScore +
globals.config.ENTROPY_MISMATCH_HIGHER_WITH_HEADER_MISMATCH['score']
alert.RaiseFileAlertMetric(eventData, 'ENTROPY_MISMATCH_HIGHER_WITH_HEADER_MISMATCH')
end
end
elseif globals.ENTROPY_STATUS_VERY_HIGH == eventData.entropyStatus then
utils.DebugLog('ALERT_SCORE_CHANGE: ENTROPY_HIGHER: ' .. globals.config.ENTROPY_HIGHER['score'])
eventData.alertScore = eventData.alertScore + globals.config.ENTROPY_HIGHER['score']
alert.RaiseFileAlertMetric(eventData, 'ENTROPY_HIGHER')
if not utils.TableHasKey(globals.extensionMap, eventData.fileExtension) then
utils.DebugLog('ALERT_SCORE_CHANGE: ENTROPY_HIGHER_EXTENSION_UNKNOWN: ' ..
globals.config.ENTROPY_HIGHER_EXTENSION_UNKNOWN['score'])
eventData.alertScore = eventData.alertScore + globals.config.ENTROPY_HIGHER_EXTENSION_UNKNOWN['score']
alert.RaiseFileAlertMetric(eventData, 'ENTROPY_HIGHER_EXTENSION_UNKNOWN')
end
end
if self.IsRansomExtension(eventData) then
utils.DebugLog('ALERT_SCORE_CHANGE: EXTENSION_BLOCKLIST: ' .. globals.config.EXTENSION_BLOCKLIST['score'])
eventData.alertScore = eventData.alertScore + globals.config.EXTENSION_BLOCKLIST['score']
alert.RaiseFileAlertMetric(eventData, 'EXTENSION_BLOCKLIST')
end
if globals.FILE_RENAME == eventData.operation then
if globals.ENTROPY_STATUS_MISMATCH_REALLY_HIGH == eventData.previousEntropyStatus then
utils.DebugLog('ALERT_SCORE_CHANGE: RENAME_ENTROPY_MISMATCH_HIGHEST: ' ..
(globals.config.RENAME_ENTROPY_MISMATCH_HIGHEST['score'] *
processData.numEntropyMismatchExtensions))
eventData.alertScore = eventData.alertScore +
(globals.config.RENAME_ENTROPY_MISMATCH_HIGHEST *
processData.numEntropyMismatchExtensions)
alert.RaiseFileAlertMetric(eventData, 'RENAME_ENTROPY_MISMATCH_HIGHEST')
elseif globals.ENTROPY_STATUS_MISMATCH_VERY_HIGH == eventData.previousEntropyStatus then
utils.DebugLog('ALERT_SCORE_CHANGE: RENAME_ENTROPY_MISMATCH_HIGHER: ' ..
(globals.config.RENAME_ENTROPY_MISMATCH_HIGHER['score'] *
processData.numEntropyMismatchExtensions))
eventData.alertScore = eventData.alertScore +
(globals.config.RENAME_ENTROPY_MISMATCH_HIGHER['score'] *
processData.numEntropyMismatchExtensions)
alert.RaiseFileAlertMetric(eventData, 'RENAME_ENTROPY_MISMATCH_HIGHER')
end
-------------------------------------------------------------------
if globals.KNOWN_TO_SUSPICIOUS == eventData.renameTransition then
utils.DebugLog('ALERT_SCORE_CHANGE: RENAME_EXTENSION_KNOWN_TO_BLOCKLIST: ' ..
globals.config.RENAME_EXTENSION_KNOWN_TO_BLOCKLIST['score'])
eventData.alertScore = eventData.alertScore + globals.config.RENAME_EXTENSION_KNOWN_TO_BLOCKLIST['score']
alert.RaiseFileAlertMetric(eventData, 'RENAME_EXTENSION_KNOWN_TO_BLOCKLIST')
elseif globals.KNOWN_TO_UNKNOWN == eventData.renameTransition then
if eventData.multipleExtension then
utils.DebugLog('ALERT_SCORE_CHANGE: RENAME_EXTENSION_KNOWN_TO_UNKNOWN_MULTIPLE: ' ..
globals.config.RENAME_EXTENSION_KNOWN_TO_UNKNOWN_MULTIPLE['score'])
eventData.alertScore = eventData.alertScore +
globals.config.RENAME_EXTENSION_KNOWN_TO_UNKNOWN_MULTIPLE['score']
alert.RaiseFileAlertMetric(eventData, 'RENAME_EXTENSION_KNOWN_TO_UNKNOWN_MULTIPLE')
else
utils.DebugLog('ALERT_SCORE_CHANGE: RENAME_EXTENSION_KNOWN_TO_UNKNOWN: ' ..
globals.config.RENAME_EXTENSION_KNOWN_TO_UNKNOWN['score'])
eventData.alertScore = eventData.alertScore + globals.config.RENAME_EXTENSION_KNOWN_TO_UNKNOWN['score']
alert.RaiseFileAlertMetric(eventData, 'RENAME_EXTENSION_KNOWN_TO_UNKNOWN')
end
local subString = string.find(eventData.filePath, eventData.filePreviousPath, nil, true)
if nil ~= subString then
utils.DebugLog('filePreviousPath found in filePath!')
if not utils.TableHasKey(processData.appendedPaths, eventData.filePreviousExtension) then
processData.appendedPaths[eventData.filePreviousExtension] = 0
end
processData.appendedPaths[eventData.filePreviousExtension] =
processData.appendedPaths[eventData.filePreviousExtension] + 1
for k, v in pairs(processData.appendedPaths) do
utils.DebugLog(k .. ' | ' .. v)
end
end
elseif globals.KNOWN_TO_BLANK == eventData.renameTransition then
utils.DebugLog('ALERT_SCORE_CHANGE: RENAME_EXTENSION_KNOWN_TO_BLANK: ' ..
globals.config.RENAME_EXTENSION_KNOWN_TO_BLANK['score'])
eventData.alertScore = eventData.alertScore + globals.config.RENAME_EXTENSION_KNOWN_TO_BLANK['score']
alert.RaiseFileAlertMetric(eventData, 'RENAME_EXTENSION_KNOWN_TO_BLANK')
elseif globals.UNKNOWN_TO_SUSPICIOUS == eventData.renameTransition then
utils.DebugLog('ALERT_SCORE_CHANGE: RENAME_EXTENSION_UNKNOWN_TO_BLOCKLIST: ' ..
globals.config.RENAME_EXTENSION_UNKNOWN_TO_BLOCKLIST['score'])
eventData.alertScore = eventData.alertScore + globals.config.RENAME_EXTENSION_UNKNOWN_TO_BLOCKLIST['score']
alert.RaiseFileAlertMetric(eventData, 'RENAME_EXTENSION_UNKNOWN_TO_BLOCKLIST')
elseif globals.UNKNOWN_TO_UNKNOWN == eventData.renameTransition then
utils.DebugLog('ALERT_SCORE_CHANGE: RENAME_EXTENSION_UNKNOWN_TO_UNKNOWN: ' ..
globals.config.RENAME_EXTENSION_UNKNOWN_TO_UNKNOWN['score'])
eventData.alertScore = eventData.alertScore + globals.config.RENAME_EXTENSION_UNKNOWN_TO_UNKNOWN['score']
alert.RaiseFileAlertMetric(eventData, 'RENAME_EXTENSION_UNKNOWN_TO_UNKNOWN')
end
end
-- TODO: Implement the numAbnormalExtensionCharacters check.
if 0 < eventData.numAbnormalExtensionCharacters then
utils.DebugLog('ALERT_SCORE_CHANGE: ABNORMAL_EXTENSION_CHARACTERS: ' ..
(globals.config.ABNORMAL_EXTENSION_CHARACTERS['score'] *
eventData.numAbnormalExtensionCharacters))
eventData.alertScore = eventData.alertScore +
(globals.config.ABNORMAL_EXTENSION_CHARACTERS['score'] *
eventData.numAbnormalExtensionCharacters)
alert.RaiseFileAlertMetric(eventData, 'ABNORMAL_EXTENSION_CHARACTERS')
end
end
-- Appends child process data to the parentProcessData table. This is used in
-- the endpoint to include a list of suspicious child processes spawned by
-- ransomware within the parent alert under the 'Ransomware.child_processes'
-- ECS key. This function creates an alert for each individual child process and
-- appends them to an array. Currently, this is capped to add a max of five
-- children and *only* if their score is non-zero.
-- @param parentProcessData table: Table containing parent process data.
-- @param ransomwareChildProcesses table: Output table which child process alerts
-- will be appended to.
-- @return bool: Returns true on success and false on failure.
function Ransomware:AppendChildProcesses(parentProcessData, ransomwareChildProcesses)
-- Sanity check input param
if nil == parentProcessData then
return false
end
-- Loop over children and create a mini alert for each one
local counter = 0
for k, _ in pairs(parentProcessData.children) do
-- Check if we have exceed max child cap and break if so
if counter >= globals.MAX_CHILD_PROCESSES then
break
end
local childProcessData = self.processDataTable[k]
-- Only add child if total score is non-zero
if nil ~= childProcessData and 0.0 ~= childProcessData.totalScore then
-- Copy over basic alert details
local childProcessTable = {}
childProcessTable.pid = childProcessData.processId
childProcessTable.score = childProcessData.totalScore
childProcessTable.alert_files = {}
-- Create a mini alert for the specified child process data
alert.GenerateElasticAlert(childProcessTable, childProcessData)
-- Append alert to ransomwareChildProcesses array
table.insert(ransomwareChildProcesses, childProcessTable)
-- Increment counter
counter = counter + 1
end
end
return true
end
local Production = Ransomware:new({nameString = 'production', diagnosticMode = false})
-- Checks whether we want to extend the event threshold for a specific process.
-- Occasionally, ransomware will create/drop ransomware notes and start renaming
-- files *before* starting the encryption process. If this happens in the first
-- 200 events, there is a chance we will not see any file encryption behavior
-- and so fail to alert. This function performs basic heuristics on the given
-- file events and will return a decision as to whether the event threshold
-- should be extended for the given process. This function is essentially a
-- decision tree and looks for:
-- 1) A high proportion of create file events.
-- 2) If there are creates, then max 2 create extensions (e.g. .txt + .lock).
-- 3) If there are renames, then max 1 rename extensions (e.g. .lock).
-- 3) A large number of unique directories touched (e.g. dropping ransom notes across the file system).
-- 4) A reasonably high total score for current activity (NB CURRENTLY NOT IMPLEMENTED).
-- Hence, it is specifically looking for ransomware dropping ransom notes across the file system.
-- @param processData table: Table containing process data.
-- @return boolean: True to extend event threshold. False otherwise.
function Production.ExtendEventThresholdCheck(processData)
-- [1] Calculate percent of creates for all current file events.
-- NB This will be calculated at 200 *and* 400 file events (if required)
-- to see if we want to extend the event threshold even further.
local numberOfCreates = 0
local numberOfCreateExtensions = 0
local percentOfCreates = 0.0
local numberOfRenames = 0
local numberOfRenameExtensions = 0
local uniqueDirectories = {}
local totalUniqueDirectories = 0
-- Calculate the percent of creates (and unique directories for later use).
for _, fileEvents in pairs(processData.createExtensions) do
numberOfCreateExtensions = numberOfCreateExtensions + 1
numberOfCreates = numberOfCreates + #fileEvents
for _, fileEvent in pairs(fileEvents) do
-- This regex will split the file path based on the last instance of '\'
-- and so will return the current directory of the file event.
local currentDir = fileEvent.filePath:match('^(.*)\\')
if not utils.TableHasKey(uniqueDirectories, currentDir) then
uniqueDirectories[currentDir] = 1
totalUniqueDirectories = totalUniqueDirectories + 1
else
uniqueDirectories[currentDir] = uniqueDirectories[currentDir] + 1
end
end
end
percentOfCreates = (numberOfCreates / #processData.events) * 100
-- Calculate the number of renames.
for _, fileEvents in pairs(processData.renameExtensions) do
numberOfRenameExtensions = numberOfRenameExtensions + 1
numberOfRenames = numberOfRenames + #fileEvents
end
-- If number of creates and renames are both zero then bail.
if (numberOfRenames == 0 and numberOfCreates == 0) then
return false
end
-- [2] If we have over 70% of the given file events being creates then carry
-- on checking if we should extend the event threshold. This heuristic was
-- generated via looking at the first 200 file events for a number of samples
-- which immediately drop a number of ransom notes.
-- (e.g. clop 70f42cc9fca43dc1fdfa584b37ecbc81761fb996cb358b6f569d734fa8cce4e3)
-- NB If necessary in future this could be expanded to include samples which
-- include a large number of renames within the first 200 events by checking
-- if percentOfRenames >= 70 and whether some unique file threshold is reached
-- (as this will not involve dropping files in loads of different dirs but
-- rather modifying many in the same dir).
if not (percentOfCreates >= 70) then
return false
end
-- [3] If we have create file events, check we have at most 2 extensions. NB
-- This should technically be at most 2 (e.g. ransom note + ransom extension)
-- but ransomware sometimes drops binaries/other random stuff beforehand so
-- this could be incremented in future (with risk of FPs).
if (numberOfCreates ~= 0) then
if not (numberOfCreateExtensions <= 2) then
return false
end
end
-- [4] If we have rename file events, check we only have 1 extension
-- (e.g. ransom extension).
if (numberOfRenames ~= 0) then
if not (numberOfRenameExtensions == 1) then
return false
end
end
-- [5] How many directories has the process touched? If it has created files
-- in over X directories it's probably suspicious. Normalization constant
-- was tested via looking at a number of samples which ranged between 97-154
-- unique dirs after 200 events.
if not (totalUniqueDirectories >= (#processData.events / 2.2)) then
return false
end
-- [6] If we reached this stage the process is suspicious enough to continue
-- monitoring for a bit longer.
utils.DebugLog('Extending Event Threshold for PID: ' .. processData.processId)
return true
end
-- Anomalous file modification patterns can be found through comparing ratios
-- pertaining to the number of deletes, creates, renames, etc. observed within a
-- process along with ratios of any corresponding file extensions. One scenario
-- would be a file extension being involved in multiple rename events to different
-- unique extensions (e.g. file.crypt => file.txt; test.crypt => test.doc;
-- sample.crypt => sample.xls). This is useful for finding generic patterns
-- that may be easy to identify when a user manually inspects event logs, but may
-- be more difficult to determine as events are analyzed sequentially.
-- @param processData table: A table containing process data.
-- @return void.
function Production.TrendAnalysis(processData)
-- uniformity in renames?
local numRenames = 0
local numRenameExtensions = 0
local numRenamePreviousExtensions = 0
for _, v in pairs(processData.renameExtensions) do
numRenameExtensions = numRenameExtensions + 1
numRenames = numRenames + #v
end
for _, _ in pairs(processData.renamePreviousExtensions) do
numRenamePreviousExtensions = numRenamePreviousExtensions + 1
end
if 20 < numRenames and 0 < numRenameExtensions then
local renameExtensionRatio = numRenamePreviousExtensions / numRenameExtensions
utils.DebugLog('numRenames : ' .. numRenames .. ' | numRenameExtensions: ' .. numRenameExtensions ..
' | numRenamePreviousExtensions: ' .. numRenamePreviousExtensions)
if 2.0 < renameExtensionRatio then
utils.DebugLog('Previous to Current Ratio > 2.0: ' .. renameExtensionRatio)
-- NOTE: reduced weight 0.1 => 0.01
utils.DebugLog('TREND_SCORE_CHANGE: TREND_SCORE_RENAME_EXTENSION_RATIO: ' ..
(globals.config.TREND_SCORE_RENAME_EXTENSION_RATIO['score'] * renameExtensionRatio))
processData.trendScore = processData.trendScore +
(globals.config.TREND_SCORE_RENAME_EXTENSION_RATIO['score'] *
renameExtensionRatio)
utils.DebugLog('TREND_SCORE_CHANGE: TREND_SCORE_NUM_RENAMES: ' ..
(globals.config.TREND_SCORE_NUM_RENAMES['score'] * numRenames))
processData.trendScore = processData.trendScore +
(globals.config.TREND_SCORE_NUM_RENAMES['score'] * numRenames)
end
if 1 == numRenamePreviousExtensions and 3 < numRenameExtensions then
-- NOTE: changed weight by * 0.01
renameExtensionRatio = (numRenameExtensions / numRenamePreviousExtensions) *
globals.config.TREND_SCORE_SINGLE_PREV_RENAME_EXTENSION['score']
processData.trendScore = processData.trendScore + renameExtensionRatio
utils.DebugLog('TREND_SCORE_CHANGE: TREND_SCORE_SINGLE_PREV_RENAME_EXTENSION: ' .. renameExtensionRatio)
end
end
------------------------------------
-- TODO: Look back at last ~20 modified; is there a discernible pattern?
-- e.g. CREATE .tmp; DELETE .*, RENAME .tmp->.zip
-- TODO: Is most everything created later renamed? To ZIP?
-- uniformity in creates?
local numCreates = 0
local numCreateExtensions = 0
for _, v in pairs(processData.createExtensions) do
numCreateExtensions = numCreateExtensions + 1
numCreates = numCreates + #v
end
local numDeletes = 0
local numDeleteExtensions = 0
for _, v in pairs(processData.deleteExtensions) do
numDeleteExtensions = numDeleteExtensions + 1
numDeletes = numDeletes + #v
end
local deleteCreateRatio = 0
if 0 < numCreateExtensions then
deleteCreateRatio = numDeleteExtensions / numCreateExtensions
utils.DebugLog('deleteCreateRatio: ' .. deleteCreateRatio)
if 1.0 <= deleteCreateRatio then
-- NOTE: changed weight by * 0.01
processData.trendScore = processData.trendScore +
(deleteCreateRatio * globals.config.TREND_SCORE_DELETE_CREATE_RATIO['score'])
utils.DebugLog('TREND_SCORE_CHANGE: TREND_SCORE_DELETE_CREATE_RATIO: ' ..
(deleteCreateRatio * globals.config.TREND_SCORE_DELETE_CREATE_RATIO['score']))
if numCreates > numDeletes then
processData.trendScore = processData.trendScore +
globals.config.TREND_SCORE_MORE_CREATES_THAN_DELETES['score']
utils.DebugLog('TREND_SCORE_CHANGE: TREND_SCORE_MORE_CREATES_THAN_DELETES: ' ..
globals.config.TREND_SCORE_MORE_CREATES_THAN_DELETES['score'])
end
end
end
end
-- TotalProcessScore tallies up the score of the entire process (parent/children).
-- If the parent-child score threshold is reached, an alert is generated. This
-- function also adds the trend score to the total score when the process trend
-- floor is reached.
-- @param eventData table: A table containing event data.
-- @param processData table: A table containing process data.
-- @return void.
function Production:TotalProcessScore(eventData, processData)
processData.totalEventScore = processData.totalEventScore + eventData.alertScore
processData.totalScore = processData.totalEventScore
-- Parent process data score tallying.
if globals.INVALID_PROCESS_ID ~= eventData.parentProcessId then
-- No need to calculate a child score for an invalid parent.
local parentProcessData = self.processDataTable[eventData.parentProcessId]
parentProcessData.children[eventData.processId] = processData.totalScore
local childScore = 0.0
for _, v in pairs(parentProcessData.children) do
childScore = childScore + v
end
utils.DebugLog('child Score: ' .. childScore)
if (childScore >= globals.PROCESS_PARENT_CHILD_ALERT_SCORE_THRESHOLD) then
utils.DebugLog('PARENT-CHILD ALERT: ' .. eventData.parentProcessId)
parentProcessData.totalScore = parentProcessData.totalScore +
globals.PROCESS_PARENT_CHILD_ALERT_SCORE_THRESHOLD
if false == parentProcessData.alerted then
utils.DebugLog('parentProcessData alert PID: ' .. parentProcessData.processId)
-- If running with elastic endpoint, add child processes to parentProcessData.
local product = utils.GetProduct()
if product == 'elastic' then
-- Append the child processes alert data so we can capture
-- them in the 'Ransomware.child_processes' ECS field.
local ransomwareChildProcesses = {}
self:AppendChildProcesses(parentProcessData, ransomwareChildProcesses)
parentProcessData['child_processes'] = ransomwareChildProcesses
end
-- Generate parent alert for both endgame and elastic.
alert.GenerateAlert(parentProcessData, true)
end
else
utils.DebugLog('PPID ' .. eventData.parentProcessId .. ' | CHILD SCORE: ' .. childScore)
end
end
if globals.PROCESS_TREND_FLOOR < #processData.events then
if 0.0 < processData.trendScore then
processData.totalScore = processData.totalScore + processData.trendScore
end
end
utils.DebugLog('PID: ' .. eventData.processId .. ' | TOTAL #Events: ' .. #processData.events ..
' | TOTAL Event Score: ' .. processData.totalEventScore .. ' | TOTAL Event + Trend Score: ' ..
processData.totalScore)
end
-- Main represents the entry point for this module. It calls several checks
-- that can identify ransomware activity. Each file operation can contribute
-- to the elevation of the score. When the score reaches the alert threshold,
-- an alert is generated and we stop monitoring the process. When a batch of
-- file events (200 window) is processed, a possibility of extension can take
-- place if certain conditions are met.
-- @inputData table: A table representing the input data.
-- @return: boolean: True always. TODO: fix the return value.
function Production:Main(inputData)
local currentProcessData = nil
local currentEventData = nil
if not utils.TableHasKey(self.processDataTable, inputData.processId) then
self.processDataTable[inputData.processId] = Ransomware.ProcessData(inputData.processId,
inputData.parentProcessId)
end
currentProcessData = self.processDataTable[inputData.processId]
if currentProcessData.activeAnalysis then
currentEventData = self:EventData(inputData)
else
return true
end
if not utils.TableHasKey(self.processDataTable, currentEventData.parentProcessId) then
-- Create a ProcessData object if one does not currently exist for the parent.
-- At this point we do not know the identity of the process's parent, so
-- it will be set to globals.INVALID_PROCESS_ID by default. This will be
-- rectified later when an event belonging to this parent is analyzed
-- (and its actual PPID is passed along).
self.processDataTable[currentEventData.parentProcessId] = Ransomware.ProcessData(
currentEventData.parentProcessId, globals.INVALID_PROCESS_ID)
elseif currentProcessData.parentProcessId ~= currentEventData.parentProcessId then
-- If PPIDs do not match, it's likely this ProcessData object was created earlier
-- for an event originating from a child of this process (see if condition above).
if currentProcessData.parentProcessId == globals.INVALID_PROCESS_ID then
currentProcessData.parentProcessId = currentEventData.parentProcessId
end
end
if not utils.TableHasValue(currentProcessData.uniqueDirectoriesByResponsibility, currentEventData.normalizedPath) then
table.insert(currentProcessData.uniqueDirectoriesByResponsibility, currentEventData.normalizedPath)
end
-- Keep track of created file names.
if currentEventData.operation == globals.FILE_CREATE_NEW then
if not utils.TableHasKey(currentProcessData.createFileNames, currentEventData.fileName) then
currentProcessData.createFileNames[currentEventData.fileName] = {}
end
table.insert(currentProcessData.createFileNames[currentEventData.fileName],
{['fileExtension'] = currentEventData.fileExtension, ['filePath'] = currentEventData.filePath})
end
-- Zero out trend score.
currentProcessData.trendScore = 0.0
-- Duplicate Event Check.
if self.DuplicateEventCheck(currentEventData, currentProcessData) then
return true
end
-- Canary File Check.
if (true == globals.canaryCompatible) and (true == globals.productionCanariesDropped) then
-- If this is a qualifying canary event, the alert will be generated,
-- so we can exit.
if Ransomware:CanaryCheck(currentEventData, currentProcessData) then
return true
end
end
-- Header Check.
self:HeaderCheck(currentEventData, currentProcessData)
-- Entropy Check.
self:EntropyCheck(currentEventData, currentProcessData)
-- Path History.
self.PathHistory(currentEventData, currentProcessData)
-- Rename Check.
if globals.FILE_RENAME == currentEventData.operation then
self:RenameCheck(currentEventData)
end
-- Check for multiple extensions, odd characters in extension.
self.AbnormalExtensionCheck(currentEventData, currentProcessData)
-- Ransom Note Check.
if globals.PROCESS_TREND_FLOOR < #currentProcessData.events then
self.RansomNoteCheck(currentProcessData)
end
-- Tally up score for file on its own.
self:TotalIndividualScore(currentEventData, currentProcessData)
-- Process trend evaluation.
if globals.PROCESS_TREND_FLOOR < #currentProcessData.events then
self.TrendAnalysis(currentProcessData)
end
-- Update the process extension table.
globals.UpdateExtensionTables(currentEventData, currentProcessData)
-- Tally up score for the entire process.
self:TotalProcessScore(currentEventData, currentProcessData)
table.insert(currentProcessData.events, currentEventData)
if globals.FILE_RENAME == currentEventData.operation then
utils.DebugLog(currentEventData.operation .. ' | ' .. string.sub(currentEventData.entropy, 1, 4) .. ' | ' ..
currentEventData.alertScore .. '-' .. currentProcessData.totalEventScore .. ' ' ..
currentEventData.filePreviousPath .. ' => ' .. currentEventData.filePath)
else
utils.DebugLog(currentEventData.operation .. ' | ' .. string.sub(currentEventData.entropy, 1, 4) .. ' | ' ..
currentEventData.alertScore .. '-' .. currentProcessData.totalEventScore .. ' ' ..
currentEventData.filePath)
end
if globals.PROCESS_ALERT_SCORE_THRESHOLD <= currentProcessData.totalScore then
-- Generate the alert as expected.
alert.GenerateAlert(currentProcessData, self.diagnosticMode)
-- Stop actively monitoring the process.
self:StopActiveAnalysis(currentProcessData)
elseif globals.PROCESS_EVENT_THRESHOLD == #currentProcessData.events then
-- Check if we want to extend the event threshold for this process.
if not self.ExtendEventThresholdCheck(currentProcessData) then
self:SendStopActiveAnalysisMsg(currentProcessData)
end
elseif globals.PROCESS_EXTENDED_EVENT_THRESHOLD == #currentProcessData.events then
-- Check if we want to extend the event threshold *again* for this process.
if not self.ExtendEventThresholdCheck(currentProcessData) then
self:SendStopActiveAnalysisMsg(currentProcessData)
end
elseif globals.PROCESS_FINAL_EXTENDED_EVENT_THRESHOLD == #currentProcessData.events then
-- In this case we have reached the final process event threshold and
-- still not alerted, so stop monitoring.
self:SendStopActiveAnalysisMsg(currentProcessData)
end
return true
end
globals.namespaces = {Production}
globals.SwitchNamespace(Production)