Browse Source

[Feature] external_services - oletools (olefy) support

tags/1.9.0
Carsten Rosenberg 5 years ago
parent
commit
7848a14b94
2 changed files with 278 additions and 1 deletions
  1. 2
    1
      lualib/lua_scanners/init.lua
  2. 276
    0
      lualib/lua_scanners/oletools.lua

+ 2
- 1
lualib/lua_scanners/init.lua View File

@@ -39,6 +39,7 @@ require_scanner('sophos')

-- Other scanners
require_scanner('dcc')
require_scanner('oletools')

exports.add_scanner = function(name, t, conf_func, check_func)
assert(type(conf_func) == 'function' and type(check_func) == 'function',
@@ -59,4 +60,4 @@ exports.filter = function(t)
end, exports))
end

return exports
return exports

+ 276
- 0
lualib/lua_scanners/oletools.lua View File

@@ -0,0 +1,276 @@
--[[
Copyright (c) 2018, Vsevolod Stakhov <vsevolod@highsecure.ru>
Copyright (c) 2018, Carsten Rosenberg <c.rosenberg@heinlein-support.de>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]--

--[[[
-- @module dcc
-- This module contains dcc access functions
--]]

local lua_util = require "lua_util"
local tcp = require "rspamd_tcp"
local upstream_list = require "rspamd_upstream_list"
local rspamd_logger = require "rspamd_logger"
local ucl = require "ucl"
local common = require "lua_scanners/common"
local fun = require "fun"

local module_name = 'oletools'

local function oletools_check(task, content, digest, rule)
local function oletools_check_uncached ()
local upstream = rule.upstreams:get_upstream_round_robin()
local addr = upstream:get_addr()
local retransmits = rule.retransmits

local function oletools_callback(err, data, conn)

local function oletools_requery()
-- set current upstream to fail because an error occurred
upstream:fail()

-- retry with another upstream until retransmits exceeds
if retransmits > 0 then

retransmits = retransmits - 1

lua_util.debugm(rule.module_name, task, '%s: Request Error: %s - retries left: %s',
rule.log_prefix, err, retransmits)

-- Select a different upstream!
upstream = rule.upstreams:get_upstream_round_robin()
addr = upstream:get_addr()

lua_util.debugm(rule.module_name, task, '%s: retry IP: %s:%s',
rule.log_prefix, addr, addr:get_port())

tcp.request({
task = task,
host = addr:to_string(),
port = addr:get_port(),
timeout = rule['timeout'],
shutdown = true,
data = content,
callback = oletools_callback,
})
else
rspamd_logger.errx(task, '%s: failed to scan, maximum retransmits '..
'exceed', rule.log_prefix)
task:insert_result(rule['symbol_fail'], 0.0, 'failed to scan and '..
'retransmits exceed')
end
end

if err then

oletools_requery()

else
-- Parse the response
if upstream then upstream:ok() end

data = tostring(data)
lua_util.debugm(rule.module_name, task, 'data: %s', tostring(data))

local ucl_parser = ucl.parser()
local ok, ucl_err = ucl_parser:parse_string(tostring(data))
if not ok then
rspamd_logger.errx(task, "%s: error parsing json response: %s",
rule.log_prefix, ucl_err)
return
end

local result = ucl_parser:get_object()

local oletools_rc = {
[0] = 'RETURN_OK',
[1] = 'RETURN_WARNINGS',
[2] = 'RETURN_WRONG_ARGS',
[3] = 'RETURN_FILE_NOT_FOUND',
[4] = 'RETURN_XGLOB_ERR',
[5] = 'RETURN_OPEN_ERROR',
[6] = 'RETURN_PARSE_ERROR',
[7] = 'RETURN_SEVERAL_ERRS',
[8] = 'RETURN_UNEXPECTED',
[9] = 'RETURN_ENCRYPTED',
}

--lua_util.debugm(rule.module_name, task, '%s: result: %s', rule.log_prefix, result)
lua_util.debugm(rule.module_name, task, '%s: filename: %s', rule.log_prefix, result[2]['file'])
lua_util.debugm(rule.module_name, task, '%s: type: %s', rule.log_prefix, result[2]['type'])

if result[1].error ~= nil then
rspamd_logger.errx(task, '%s: ERROR found: %s', rule.log_prefix,
result[1].error)
oletools_requery()
elseif result[3]['return_code'] == 9 then
rspamd_logger.warnx(task, '%s: File is encrypted.', rule.log_prefix)
elseif result[3]['return_code'] > 6 then
rspamd_logger.errx(task, '%s: Oletools Error Returned: %s',
rule.log_prefix, oletools_rc[result[3]['return_code']])
elseif result[3]['return_code'] > 1 then
rspamd_logger.errx(task, '%s: Oletools Error Returned: %s',
rule.log_prefix, oletools_rc[result[3]['return_code']])
oletools_requery()
elseif result[2]['analysis'] == 'null' and #result[2]['macros'] == 0 then
if rule.log_clean == true then
rspamd_logger.infox(task, '%s: Scanned Macro is OK', rule.log_prefix)
else
lua_util.debugm(rule.module_name, task, '%s: No Macro found', rule.log_prefix)
end
elseif #result[2]['macros'] > 0 then

for _,m in ipairs(result[2]['macros']) do
lua_util.debugm(rule.module_name, task, '%s: macros found - code: %s, ole_stream: %s, '..
'vba_filename: %s', rule.log_prefix, m.code, m.ole_stream, m.vba_filename)
end

local macro_autoexec = false
local macro_suspicious = false
local macro_keyword_table = {}

for _,a in ipairs(result[2]['analysis']) do
if a.type ~= 'AutoExec' or a.type ~= 'Suspicious' then
lua_util.debugm(rule.module_name, task, '%s: threat found - type: %s, keyword: %s, '..
'description: %s', rule.log_prefix, a.type, a.keyword, a.description)
end
if a.type == 'AutoExec' then
macro_autoexec = true
if rule.extended == true then
table.insert(macro_keyword_table, a.keyword)
end
elseif a.type == 'Suspicious'
and a.keyword ~= 'Base64 Strings'
and a.keyword ~= 'Hex Strings'
then
macro_suspicious = true
if rule.extended == true then
table.insert(macro_keyword_table, a.keyword)
end
end
end

if macro_autoexec then
table.insert(macro_keyword_table, 'AutoExec')
end
if macro_suspicious then
table.insert(macro_keyword_table, 'Suspicious')
end

lua_util.debugm(rule.module_name, task, '%s: extended: %s', rule.log_prefix, rule.extended)
if rule.extended == false and macro_autoexec and macro_suspicious then

lua_util.debugm(rule.module_name, task, '%s: found macro_autoexec and '..
'macro_suspicious', rule.log_prefix)
local threat = 'AutoExec+Suspicious'
common.yield_result(task, rule, threat, rule.default_score)
common.save_av_cache(task, digest, rule, threat, rule.default_score)

elseif rule.extended == true and #macro_keyword_table > 0 then

common.yield_result(task, rule, macro_keyword_table, rule.default_score)
common.save_av_cache(task, digest, rule, macro_keyword_table, rule.default_score)

elseif rule.log_clean == true then
rspamd_logger.infox(task, '%s: Scanned Macro is OK', rule.log_prefix)
end

else
rspamd_logger.warnx(task, '%s: unhandled response', rule.log_prefix)
end
end
end

tcp.request({
task = task,
host = addr:to_string(),
port = addr:get_port(),
timeout = rule['timeout'],
shutdown = true,
data = content,
callback = oletools_callback,
})

end
if common.need_av_check(task, content, rule) then
if common.check_av_cache(task, digest, rule, oletools_check_uncached) then
return
else
oletools_check_uncached()
end
end
end

local function oletools_config(opts)

local oletools_conf = {
module_name = module_name,
scan_mime_parts = false,
scan_text_mime = false,
scan_image_mime = false,
default_port = 5954,
timeout = 15.0,
log_clean = false,
retransmits = 2,
cache_expire = 7200, -- expire redis in 2h
message = '${SCANNER}: Oletools threat message found: "${VIRUS}"',
detection_category = "office macro",
default_score = 1,
action = false,
extended = false,
}

oletools_conf = lua_util.override_defaults(oletools_conf, opts)

if not oletools_conf.prefix then
oletools_conf.prefix = 'rs_av_' .. oletools_conf.name .. '_'
end

if not oletools_conf.log_prefix then
if oletools_conf.name:lower() == oletools_conf.type:lower() then
oletools_conf.log_prefix = oletools_conf.name
else
oletools_conf.log_prefix = oletools_conf.name .. ' (' .. oletools_conf.type .. ')'
end
end

if not oletools_conf.servers then
rspamd_logger.errx(rspamd_config, 'no servers defined')

return nil
end

oletools_conf.upstreams = upstream_list.create(rspamd_config,
oletools_conf.servers,
oletools_conf.default_port)

if oletools_conf.upstreams then
lua_util.add_debug_alias('antivirus', oletools_conf.module_name)
return oletools_conf
end

rspamd_logger.errx(rspamd_config, 'cannot parse servers %s',
oletools_conf.servers)
return nil
end

return {
type = {module_name,'office macro scanner', 'hash', 'scanner'},
description = 'oletools office macro scanner',
configure = oletools_config,
check = oletools_check,
name = module_name
}

Loading…
Cancel
Save