├── LICENSE.md ├── README.md ├── yara-suricata.rules └── yara.lua /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | Anyone is free to copy, modify, publish, use, compile, sell, or 3 | distribute this software, either in source code form or as a compiled 4 | binary, for any purpose, commercial or non-commercial, and by any 5 | means. 6 | In jurisdictions that recognize copyright laws, the author or authors 7 | of this software dedicate any and all copyright interest in the 8 | software to the public domain. We make this dedication for the benefit 9 | of the public at large and to the detriment of our heirs and 10 | successors. We intend this dedication to be an overt act of 11 | relinquishment in perpetuity of all present and future rights to this 12 | software under copyright law. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 17 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 18 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 19 | OTHER DEALINGS IN THE SOFTWARE. 20 | For more information, please refer to 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yara-suricata 2 | A Yara Lua output script for Suricata (https://suricata-ids.org/) 3 | 4 | # Usage 5 | In order to use this script, you will need: 6 | 1. A working Yara executable 7 | 2. Yara rules 8 | 3. Suricata file-store version 2 module, which means you'll need (at the time of this writing) Suricata 4.1.0 Beta. 9 | 4. Suricata 4.1.0+ compiled with Lua or LuaJIT support and NSS support (for hashing) 10 | 11 | The script will need to be placed in the Suricata output scripts directory, and specified by name in output scripts section in the Suricata config file(suricata.yaml), please see https://suricata.readthedocs.io/en/latest/output/lua-output.html#yaml for the correct syntax. 12 | 13 | Once installed and configured, you'll need to configure the rules to store the desired files to be scanned by Yara. 14 | There are 4 configuration options within the script that you may need to customize to suit your environment. 15 | They are as follows: 16 | 17 | suricata_filestore: The path to your Suricata file store, defaults to /filestore 18 | 19 | yara_path: The path to your Yara executable, defaults to '/usr/bin/yara' 20 | 21 | yara_rules_path: The path to your compiled Yara rules file for executable file types. Defaults to '/usr/share/yara/rules.yar' 22 | 23 | yara_non_pe_rules_path: The path to your compiled Yara rules file for non-executable file types 24 | 25 | yara_log_name: The name of the Yara log. The name defaults to 'yara.json', under the 26 | 27 | Output looks like so: 28 | 29 | `{"filename": "/f.tmp", "rules": ["BlackEnergy_Driver_USBMDM"],"size": 60928,"sha256": "244dd8018177ea5a92c70a7be94334fa457c1aab8a1c1ea51580d7da500c3ad5"}` 30 | 31 | If there are no rule hits, the script will delete the file. 32 | -------------------------------------------------------------------------------- /yara-suricata.rules: -------------------------------------------------------------------------------- 1 | # This is a default set of rules that will store executables between 5KB and 5MB in size sent over HTTP 2 | # and Word and RTF documents between 5KB and 1MB in size. 3 | 4 | 5 | # Executables 6 | alert http any any -> any any (msg:"FILESTORE executable file"; flow:established,to_client; filesize:5KB<>5MB; filemagic:"executable"; filestore; noalert; sid:95500001; rev:1;) 7 | #alert smtp $EXTERNAL_NET any -> $HOME_NET any (msg:"FILESTORE executable file"; filesize:5KB<>5MB; filemagic:"executable"; filestore; noalert; sid:95500002; rev:1;) 8 | #alert ftp-data any any -> any any (msg:"FILESTORE executable file"; flow:established,to_client; filesize:5KB<>5MB; filemagic:"executable"; filestore; noalert; sid:95500003; rev:1;) 9 | 10 | # Word docs 11 | alert http any any -> any any (msg:"FILESTORE Word document"; flow:established,to_client; filesize:5KB<>1MB; filemagic:"Microsoft Word"; filestore; noalert; sid:95500004; rev:1;) 12 | #alert smtp $EXTERNAL_NET any -> $HOME_NET any (msg:"FILESTORE Word document"; filesize:5KB<>1MB; filemagic:"Microsoft Word"; filestore; noalert; sid:95500005; rev:1;) 13 | 14 | 15 | # RTF 16 | alert http any any -> any any (msg:"FILESTORE RTF file"; flow:established,to_client; filesize:5KB<>1MB; filemagic:"Rich Text Format"; filestore; noalert; sid:95500006; rev:1;) 17 | #alert smtp $EXTERNAL_NET any -> $HOME_NET any (msg:"FILESTORE RTF file"; filesize:5KB<>1MB; filemagic:"Rich Text Format"; filestore; noalert; sid:95500007; rev:1;) 18 | -------------------------------------------------------------------------------- /yara.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | yara.lua: A Yara output script for Suricata 3 | Written by Elazar Broad 4 | 5 | This is free and unencumbered software released into the public domain. 6 | 7 | Anyone is free to copy, modify, publish, use, compile, sell, or 8 | distribute this software, either in source code form or as a compiled 9 | binary, for any purpose, commercial or non-commercial, and by any 10 | means. 11 | 12 | In jurisdictions that recognize copyright laws, the author or authors 13 | of this software dedicate any and all copyright interest in the 14 | software to the public domain. We make this dedication for the benefit 15 | of the public at large and to the detriment of our heirs and 16 | successors. We intend this dedication to be an overt act of 17 | relinquishment in perpetuity of all present and future rights to this 18 | software under copyright law. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 23 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 24 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 25 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | OTHER DEALINGS IN THE SOFTWARE. 27 | 28 | For more information, please refer to 29 | ]]-- 30 | 31 | function init (args) 32 | local needs = {} 33 | needs['type'] = 'file' 34 | return needs 35 | end 36 | 37 | function setup (args) 38 | --Ensure that the log path has both a leading and trailing slash 39 | suricata_log_path = fix_path(SCLogPath()) 40 | 41 | -- Configure as needed 42 | suricata_filestore = suricata_log_path .. 'filestore' 43 | yara_path = '/usr/bin/yara' 44 | -- TODO: maybe use Yara rule tags instead of separate files 45 | yara_rules_path = '/usr/share/yara-rules/rules.yar' 46 | yara_non_pe_rules_path = '/usr/share/yara-rules/non_pe_rules.yar' 47 | yara_log_name = 'yara.json' 48 | 49 | yara_log = assert(io.open(string.format("%s%s", suricata_log_path, yara_log_name), 'a')) 50 | end 51 | 52 | function log (args) 53 | ret, output = run_yara() 54 | if ret then 55 | yara_log:write(output, '\n') 56 | yara_log:flush() 57 | end 58 | end 59 | 60 | function run_yara () 61 | state, stored = SCFileState() 62 | if stored then 63 | name, size, magic, md5, sha1, sha256 = SCFileInfo() 64 | SCLogDebug(string.format('state: %s, stored: %s, name: %s, size: %s, magic: %s, md5: %s, sha1: %s, sha256: %s', state, stored, name, size, magic, md5, sha1, sha256)) 65 | if sha256 ~= nil and string.len(sha256) == 64 then 66 | local file_path = string.format('%s/%s/%s', 67 | suricata_filestore, 68 | string.sub(sha256, 0, 2), 69 | sha256) 70 | 71 | local rule_path = yara_rules_path 72 | if not string.match(magic, "executable") then 73 | rule_path = yara_non_pe_rules_path 74 | end 75 | 76 | local yara_command = string.format('%s -C -w %s %s', 77 | yara_path, 78 | rule_path, 79 | file_path) 80 | 81 | local has_rule_hit = false 82 | local ret = {} 83 | ret['filename'] = name 84 | ret['size'] = size 85 | ret['magic'] = magic 86 | ret['sha256'] = sha256 87 | ret['rules'] = {} 88 | 89 | local yara_pipe = assert(io.popen(yara_command)) 90 | yara_output = yara_pipe:read('*a') 91 | yara_pipe:close() 92 | for line in yara_output:gmatch('[^\r\n]+') do 93 | rule, file = string.match(line, '^(.+)[%s]+(.+)$') 94 | SCLogDebug(string.format("rule: %s, file: %s, filepath: %s", rule, file, file_path)) 95 | if rule ~= nil and rule ~= '' and file == file_path then 96 | table.insert(ret['rules'], rule) 97 | has_rule_hit = true 98 | end 99 | end 100 | 101 | if has_rule_hit then 102 | return true, table_to_json(ret) 103 | else 104 | os.remove(file_path) 105 | end 106 | end 107 | end 108 | 109 | return false, nil 110 | end 111 | 112 | function table_to_json (t) 113 | local s = '' 114 | local open_char, close_char = '{', '}' 115 | local have_list = false 116 | 117 | for k, v in pairs(t) do 118 | if type(v) == 'table' then 119 | v = table_to_json(v) 120 | end 121 | 122 | qc = '"' 123 | if have_list or type(k) == 'number' then 124 | have_list = true 125 | s = string.format('%s"%s",', s, v) 126 | else 127 | if string.find(v, '^[%[%{]') ~= nil or string.find(v, '^%d+$') ~= nil then 128 | qc = '' 129 | end 130 | s = string.format('%s"%s":%s%s%s,', s, k, qc, v, qc) 131 | end 132 | end 133 | 134 | if have_list then 135 | open_char, close_char = '[', ']' 136 | end 137 | 138 | --Strip trailing comma 139 | s = s:sub(1, -2) 140 | 141 | return open_char .. s .. close_char 142 | end 143 | 144 | function fix_path(s) 145 | s = s:gsub('^%/?(.+)', '/%1') 146 | if s:match('%/$') == nil then 147 | s = string.format('%s/', s) 148 | end 149 | return s 150 | end 151 | 152 | function deinit (args) 153 | yara_log:close() 154 | SCLogInfo('yara.lua deinit') 155 | end 156 | --------------------------------------------------------------------------------