├── baseline.yar ├── requirements.txt ├── TODO.txt ├── CHANGELOG ├── LICENSE ├── bad_rule.yar ├── .gitignore ├── yara_mem_usage.py ├── test-rules.yar ├── README.md ├── panopticon.py └── baseline_50.yar /baseline.yar: -------------------------------------------------------------------------------- 1 | baseline_50.yar -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | plyara>=2.1.0 2 | yara-python 3 | rich 4 | psutil 5 | -------------------------------------------------------------------------------- /TODO.txt: -------------------------------------------------------------------------------- 1 | # TODO: zu testende rule 10* rein, _1, ... 2 | # price of nocase, regex, ... 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | 2 | 0.2.0 to 0.3.0 3 | 4 | - Rules are measured by how much they slow down a whole scan if they're added to 100+ rules in baseline.yar. This might happen due to bad "atom quality" as explained in yara/libyara/atoms.c 5 | - Increase accuracy by: 6 | -- Reading samples into RAM (to avoid OS calls to get them from the file system cache) 7 | -- Recommending using real time priority and processing bindung in Linux (needs root permissions) 8 | - Fast mode: Skip a rule on the first scan which is fast enough 9 | - Progress bar 10 | - Pass Thor/Loki environment variables to yara-python 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Florian Roth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bad_rule.yar: -------------------------------------------------------------------------------- 1 | import "pe" 2 | import "math" 3 | import "hash" 4 | 5 | /* 6 | WARNING: 7 | This YARA rule is from hell. It represents the worst possible YARA rule 8 | and could cause performance issues in any product that allows you to run 9 | custom YARA rules. It has not been designed to reflect a the work of 10 | an author with an intentionally malicious mindset, but combines strings 11 | and conditions found in real world examples. 12 | Please use it with care. 13 | FOR EDUCATIONAL PURPOSES ONLY! ;D 14 | */ 15 | 16 | rule Dantes_YARA_Inferno : WARNING { 17 | meta: 18 | author = "Florian Roth" 19 | description = "This rule is just really, really bad." 20 | reference = "Internal Research" 21 | date = "2022-11-30" 22 | score = 0 23 | strings: 24 | // Short atom 25 | $mz_header = "MZ" // short atom and not fixed with e.g. "$mz at 0" in the condition 26 | $zero_seq = { 00 ?? ?? ?? [1-4] 00 } // only zeros - this should cause YARA error 30 (too many matches) 27 | // Unnecessary shit 28 | $dos_stub = "This program cannot be run in DOS mode" nocase ascii wide // don't use nocase or wide if the casing and character set is known 29 | // Bad regex 30 | $regex1 = /\.text.*/ nocase // short anchor and the use of .* 31 | $regex2 = /(open|close).*(http|ftp)/ nocase // no anchor at all and .* ... really bad 32 | $regex3 = /\\[\w]+\.[\w]{3}/ // short anchor, all kinds of chars 33 | // Repeating characters 34 | $pat1 = "AAAAAAA" // this could cause YARA error 30 (too many matches) on some files 35 | condition: 36 | // no header and file size check 37 | all of them 38 | // loops are bad 39 | and for any i in (0..pe.number_of_resources-1): ( 40 | // hash calculation on resources uses up many cpu cycles 41 | hash.sha256(pe.resources[i].offset,pe.resources[i].length) == "6d9bd1110034b754d9320060c3ea84a9f18b07fab5b8e0078ce11a4cbae465fa" 42 | ) 43 | // an even worse loop 44 | and for all i in (1..#zero_seq) : (@zero_seq[i] < 10000) 45 | // calculating entropy over large portions of the file 46 | and math.entropy(0, filesize) > 0 47 | } 48 | 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | samples 131 | *.swp 132 | tmp/ 133 | samples_mix/ 134 | warning_rules.yar 135 | .vscode/settings.json 136 | -------------------------------------------------------------------------------- /yara_mem_usage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | # yara memory measurement 5 | # shows the increase in used memory if itself after loading and compiling a .yar file with yara-python 6 | # by arnim rupp 7 | 8 | 9 | import os, psutil 10 | import argparse 11 | import yara 12 | import resource 13 | import gc 14 | 15 | def get_used_mem(process): 16 | proc_mem = int(process.memory_info().rss/1024) 17 | res_get = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 18 | if debug: 19 | print("process.memory_info(): " +str(proc_mem) + " - resource.getrusage: " + str(res_get) ) 20 | return proc_mem, res_get 21 | 22 | 23 | def load_and_unload_single_rule(): 24 | # compile simple rule to activate yara 25 | rules = yara.compile(source='rule foo: bar {strings: $a = "lmn" condition: $a}') 26 | 27 | if debug: print("Compiled mini rule") 28 | before, before_res = get_used_mem(process) 29 | 30 | del rules 31 | 32 | if debug: print("Deleted rules") 33 | before, before_res = get_used_mem(process) 34 | 35 | gc.collect() 36 | 37 | if debug: print("Did garbage collection") 38 | before, before_res = get_used_mem(process) 39 | 40 | 41 | if __name__ == '__main__': 42 | 43 | parser = argparse.ArgumentParser(description='yara memory measurement') 44 | parser.add_argument('RULES_FILE', help='Path to rules file') 45 | parser.add_argument('-d', help='Debug', action='store_true', default=False) 46 | parser.add_argument('-t','--thor', help='Use Thor/loki environment variables (needs more memory)', action='store_true', default=False) 47 | args = parser.parse_args() 48 | 49 | debug = args.d 50 | 51 | process = psutil.Process(os.getpid()) 52 | 53 | rulesfile = args.RULES_FILE 54 | 55 | if debug: print("Just started python") 56 | before, before_res = get_used_mem(process) 57 | 58 | load_and_unload_single_rule() 59 | 60 | gc.collect() 61 | if debug: print("Did garbage collection") 62 | before, before_res = get_used_mem(process) 63 | 64 | if not args.thor: 65 | rules = yara.compile(filepaths={ 'rules':rulesfile }) 66 | else: 67 | rules = yara.compile(filepaths={ 68 | 'rules':rulesfile 69 | }, 70 | externals={ 71 | 'filename': "", 72 | 'filepath': "", 73 | 'extension': "", 74 | 'filetype': "", 75 | 'md5': "", 76 | }) 77 | 78 | if debug: print("Compile rules to evaluate") 79 | after, after_res = get_used_mem(process) 80 | 81 | print(rulesfile + ': -- resource: {:,}'.format(after_res - before_res ) + ' KB') 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /test-rules.yar: -------------------------------------------------------------------------------- 1 | 2 | rule Embedded_EXE_Cloaking { 3 | meta: 4 | description = "Detects an embedded executable in a non-executable file" 5 | author = "Florian Roth" 6 | date = "2015/02/27" 7 | score = 60 8 | nodeepdive = 1 9 | strings: 10 | $noex_png = { 89 50 4E 47 } 11 | $noex_pdf = { 25 50 44 46 } 12 | $noex_rtf = { 7B 5C 72 74 66 31 } 13 | $noex_jpg = { FF D8 FF E0 } 14 | $noex_gif = { 47 49 46 38 } 15 | $mz = { 4D 5A } 16 | $a1 = "This program cannot be run in DOS mode" 17 | $a2 = "This program must be run under Win32" 18 | condition: 19 | ( 20 | ( $noex_png at 0 ) or 21 | ( $noex_pdf at 0 ) or 22 | ( $noex_rtf at 0 ) or 23 | ( $noex_jpg at 0 ) or 24 | ( $noex_gif at 0 ) 25 | ) 26 | and 27 | for any i in (1..#mz): ( @a1 < ( @mz[i] + 200 ) or @a2 < ( @mz[i] + 200 ) ) 28 | } 29 | 30 | rule Methodology_Suspicious_Shortcut_LOLcommand 31 | { 32 | meta: 33 | author = "@itsreallynick (Nick Carr)" 34 | reference = "https://twitter.com/ItsReallyNick/status/1176601500069576704" 35 | description = "Detects possible shortcut usage for .URL persistence" 36 | score = 50 37 | date = "27.09.2019" 38 | modified = "2021-02-14" 39 | strings: 40 | $file1 = /[\x0a\x0d](IconFile|(Base|)URL)\s*=[^\x0d]*(powershell|cmd|certutil|mshta|wscript|cscript|rundll32|wmic|regsvr32|msbuild)(\.exe|)[^\x0d]{2,50}\x0d/ nocase 41 | $url_clsid = "[{000214A0-0000-0000-C000-000000000046}]" 42 | $url_explicit = "[InternetShortcut]" nocase 43 | condition: 44 | any of ($url*) and any of ($file*) 45 | and uint16(0) != 0x5A4D and uint32(0) != 0x464c457f and uint32(0) != 0xBEBAFECA and uint32(0) != 0xFEEDFACE and uint32(0) != 0xFEEDFACF and uint32(0) != 0xCEFAEDFE 46 | and filesize < 30KB 47 | } 48 | 49 | rule APT_Trojan_Win_REDFLARE_5 50 | { 51 | meta: 52 | date = "2020-12-01" 53 | modified = "2020-12-01" 54 | md5 = "dfbb1b988c239ade4c23856e42d4127b, 3322fba40c4de7e3de0fda1123b0bf5d" 55 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 56 | author = "FireEye" 57 | strings: 58 | $s1 = "AdjustTokenPrivileges" fullword 59 | $s2 = "LookupPrivilegeValueW" fullword 60 | $s3 = "ImpersonateLoggedOnUser" fullword 61 | $s4 = "runCommand" fullword 62 | $steal_token = { FF 15 [4] 85 C0 [1-40] C7 44 24 ?? 01 00 00 00 [0-20] C7 44 24 ?? 02 00 00 00 [0-20] FF 15 [4] FF [1-5] 85 C0 [4-40] 00 04 00 00 FF 15 [4-5] 85 C0 [2-20] ( BA 0F 00 00 00 | 6A 0F ) [1-4] FF 15 [4] 85 C0 74 [1-20] FF 15 [4] 85 C0 74 [1-20] ( 6A 0B | B9 0B 00 00 00 ) E8 } 63 | condition: 64 | (uint16(0) == 0x5A4D) and (uint32(uint32(0x3C)) == 0x00004550) and all of them 65 | } 66 | 67 | rule password_dump_TESTING { 68 | meta: 69 | author = "Florian Roth" 70 | description = "Detects output files of common password dumpers (pwdump/wce)" 71 | date = "23/04/2014" 72 | score = 55 73 | strings: 74 | $pwdump = /[\w]{3,15}:[A-Z0-9]{3,15}:[A-F0-9]{32}:[A-F0-9]{32}/ fullword 75 | $pwdump_nolm = /[\w]{3,15}:[A-Z0-9]{3,15}:NO PASSWORD[\*]{21}:[A-F0-9]{32}/ fullword 76 | condition: 77 | filesize < 400 and $pwdump at 0 or $pwdump_nolm at 0 78 | } 79 | 80 | rule TEST_String_Short { 81 | meta: 82 | description = "Detects Taskmasters tool" 83 | author = "Florian Roth" 84 | reference = "https://www.youtube.com/watch?v=XYuclHsoQO4&feature=youtu.be" 85 | date = "2019-11-11" 86 | strings: 87 | $s1 = "Created Date: " fullword 88 | $s4 = "IEX" fullword ascii 89 | condition: 90 | all of them 91 | } 92 | 93 | 94 | rule TEST_Regex_Short { 95 | meta: 96 | description = "Detects Taskmasters tool" 97 | author = "Florian Roth" 98 | reference = "https://www.youtube.com/watch?v=XYuclHsoQO4&feature=youtu.be" 99 | date = "2019-11-11" 100 | strings: 101 | $s1 = /[A-Z]{40}/ 102 | $sr1 = /CE[\x0d]+/ nocase 103 | condition: 104 | all of them 105 | } 106 | 107 | rule Hunting_Rule_ShikataGaNai { 108 | meta: 109 | author = "Steven Miller" 110 | company = "FireEye" 111 | score = 65 112 | description = "Hunting rule to detect obfuscation applied by XOR used by ShikataGaNai algorithm used by msfvenom" 113 | reference = "https://www.fireeye.com/blog/threat-research/2019/10/shikata-ga-nai-encoder-still-going-strong.html" 114 | strings: 115 | $varInitializeAndXorCondition1_XorEAX = { B8 ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 59 | 5A | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 40 | 41 | 42 | 43 | 45 | 46 | 47 ) ?? } 116 | $varInitializeAndXorCondition1_XorEBP = { BD ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 59 | 5A | 5B | 5C | 5E | 5F ) [0-50] 31 ( 68 | 69 | 6A | 6B | 6D | 6E | 6F ) ?? } 117 | $varInitializeAndXorCondition1_XorEBX = { BB ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 59 | 5A | 5C | 5D | 5E | 5F ) [0-50] 31 ( 58 | 59 | 5A | 5B | 5D | 5E | 5F ) ?? } 118 | $varInitializeAndXorCondition1_XorECX = { B9 ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 5A | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 48 | 49 | 4A | 4B | 4D | 4E | 4F ) ?? } 119 | $varInitializeAndXorCondition1_XorEDI = { BF ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 59 | 5A | 5B | 5C | 5D | 5E ) [0-50] 31 ( 78 | 79 | 7A | 7B | 7D | 7E | 7F ) ?? } 120 | $varInitializeAndXorCondition1_XorEDX = { BA ?? ?? ?? ?? [0-30] D9 74 24 F4 [0-10] ( 58 | 59 | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 50 | 51 | 52 | 53 | 55 | 56 | 57 ) ?? } 121 | $varInitializeAndXorCondition2_XorEAX = { D9 74 24 F4 [0-30] B8 ?? ?? ?? ?? [0-10] ( 59 | 5A | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 40 | 41 | 42 | 43 | 45 | 46 | 47 ) ?? } 122 | $varInitializeAndXorCondition2_XorEBP = { D9 74 24 F4 [0-30] BD ?? ?? ?? ?? [0-10] ( 58 | 59 | 5A | 5B | 5C | 5E | 5F ) [0-50] 31 ( 68 | 69 | 6A | 6B | 6D | 6E | 6F ) ?? } 123 | $varInitializeAndXorCondition2_XorEBX = { D9 74 24 F4 [0-30] BB ?? ?? ?? ?? [0-10] ( 58 | 59 | 5A | 5C | 5D | 5E | 5F ) [0-50] 31 ( 58 | 59 | 5A | 5B | 5D | 5E | 5F ) ?? } 124 | $varInitializeAndXorCondition2_XorECX = { D9 74 24 F4 [0-30] B9 ?? ?? ?? ?? [0-10] ( 58 | 5A | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 48 | 49 | 4A | 4B | 4D | 4E | 4F ) ?? } 125 | $varInitializeAndXorCondition2_XorEDI = { D9 74 24 F4 [0-30] BF ?? ?? ?? ?? [0-10] ( 58 | 59 | 5A | 5B | 5C | 5D | 5E ) [0-50] 31 ( 78 | 79 | 7A | 7B | 7D | 7E | 7F ) ?? } 126 | $varInitializeAndXorCondition2_XorEDX = { D9 74 24 F4 [0-30] BA ?? ?? ?? ?? [0-10] ( 58 | 59 | 5B | 5C | 5D | 5E | 5F ) [0-50] 31 ( 50 | 51 | 52 | 53 | 55 | 56 | 57 ) ?? } 127 | condition: 128 | any of them 129 | } 130 | 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Panopticon 2 | 3 | A YARA rule performance measurement tool 4 | 5 | ## What it does 6 | 7 | It runs a YARA rule set against a set of samples and measures the duration of a set of cycles over that sample set. 8 | 9 | Bad rules are the ones with bad atom quality as explained in [YARA Performance Guidelines](https://github.com/Neo23x0/YARA-Performance-Guidelines/). They slow down the whole search as more data needs to be evaluated by the YARA engine. YARA complains by itself about some rules slowing down the search, e.g. if they contain a regex with .* but [^a]* isn't alerted but equally bad. 10 | 11 | The number of iterations over the sample set gets evaluated automatically by providing the number of seconds each rule should be tested against the sample set (default: 30). 12 | 13 | ## Usage 14 | 15 | ```bash 16 | usage: panopticon.py [-h] [-f yara files [yara files ...]] 17 | [-d yara files [yara files ...]] [-l logfile] 18 | [-i iterations] [-s seconds] [-c baseline_calib_times] 19 | [-m baseline_test_times] [-S] [-a A] 20 | 21 | YARA RULE PERFORMANCE TESTER 22 | 23 | optional arguments: 24 | -h, --help show this help message and exit 25 | -f yara files [yara files ...] 26 | Path to input files (YARA rules, separated by space) 27 | -d yara files [yara files ...] 28 | Path to input directory (YARA rules folders, separated 29 | by space) 30 | -l logfile Log file (default: panopticon.log) 31 | -i iterations Number of iterations (default: auto) 32 | -s seconds Number of seconds to spend for each rule's measurement 33 | -c baseline_calib_times 34 | How often to run the iterations on the calibration 35 | rule 36 | -m baseline_test_times 37 | Number of normal runs to measure accuracy of 38 | calibration rule 39 | -S Slow mode, don't skip rule on first quick scan 40 | -a A Alert bonus in percent: Only alert rules which slow 41 | down scans by this much percent (+ measured 42 | inaccuracy) 43 | 44 | ``` 45 | 46 | ## Prerequisites 47 | 48 | You need to find/build a good sample set that reflects best the use case in which you plan to use your YARA rules. Copy about 10-100 MB of the usual files into the samples directory, e.g. some exe, elf, doc, txt, php, ... This should not be malware or cause any matches because the processing of them would skew the results if some rules fire and some don't. 49 | 50 | The baseline.yar should contain at least 50 rules to prevent single rules with lots of strings to cause a relative slowdown of the scanning. 51 | 52 | ## Considerations 53 | 54 | The measurements are influenced by the system load. If you run this on your Desktop system with many different running and active processes, the results can be distorted. 55 | 56 | The more seconds you give the measurements (parameter -s ), the better are the results. In fast mode (default) panopticon will stop measuring a rule, as soon as one scan was fast enough. So a high value here doesn't mean, that the whole test will take long, but it will give better results. 57 | 58 | Give the panopticon process maximum priority in the OS, e.g. "chrt -r 99 ./panopticon.py" (if your kernel has realtime extension) or "nice -n -20 ./panopticon.py" on Linux. 59 | 60 | ### On Linux: Reserving a physical CPU core 61 | Follow instructions on https://unix.stackexchange.com/questions/326579/how-to-ensure-exclusive-cpu-availability-for-a-running-process to exclude all virtual cores of a physical core from the normal scheduler. See "core id" in the output of "cat /proc/cpuinfo", e.g. on a core i7 processors 3 and 7 share core id 3. After report start with: 62 | 63 | ```bash 64 | chrt -r 99 taskset -c 7 ./panopticon.py 65 | ``` 66 | 67 | This command should show several kernel processes (square bracket) but only one in userland (no square brackets): 68 | 69 | ```bash 70 | ps -eo psr,command | tr -s " " | grep "^ [3|7]" 71 | ``` 72 | 73 | ## Getting Started 74 | 75 | 1. Clone this repo and cd to it `git clone https://github.com/Neo23x0/panopticon.git && cd panopticon` 76 | 2. Install the requirements `pip3 install -r requirements.txt` 77 | 3. Place your samples into the `./samples` sub folder (see section "Prerequisites" for help on that matter) 78 | 4. Start a measurement with `python3 panopticon.py -f your-yara-rules.yar` 79 | 80 | ### Example 81 | 82 | Try the `test-rules.yar` that are included in this repository. 83 | 84 | ```bash 85 | python panopticon.py -f test-rules.yar 86 | ``` 87 | 88 | ## Expected Output 89 | 90 | 91 | ```bash 92 | ___ __ _ 93 | / _ \___ ____ ___ ___ / /_(_)______ ___ 94 | / ___/ _ `/ _ \/ _ \/ _ \/ __/ / __/ _ \/ _ \ 95 | /_/ \_,_/_//_/\___/ .__/\__/_/\__/\___/_//_/ 96 | by Florian Roth /_/ v0.3.0 97 | and Arnim Rupp 98 | 99 | YARA Rule Performance Testing 100 | [INFO ] Starting measurement at: 2021-02-14 12:09:35 101 | [INFO ] Number of calibration rules: 100 102 | [INFO ] Processing test-rules.yar ... 103 | [INFO ] Parsed 6 rules from test-rules.yar 104 | [INFO ] Rule: - best of 1 - duration: 0.0969 s 105 | [INFO ] Auto-evaluation calculated that the defined 3 seconds per rule could be accomplished by 31 cycles per rule over the given sample set of 45 samples 106 | [INFO ] Running 31 cycles over the sample set 107 | [INFO ] Now the benchmarking begins ... 108 | [INFO ] Running baseline measure 3 times with 31 cycles each to get a good average, dropping the worst result 109 | [INFO ] Rule: Baseline - best of 31 - duration: 0.0940 s 110 | [INFO ] Rule: Baseline - best of 31 - duration: 0.0935 s 111 | [INFO ] Rule: Baseline - best of 31 - duration: 0.0935 s 112 | [INFO ] Calibrate average baseline duration: 0.09349751472473145 113 | [INFO ] Running baseline measure 3 times with 31 cycles (dropping the worst) to measure inaccuracy 114 | [INFO ] Rule: Baseline - best of 31 - duration: 0.0939 s 115 | [INFO ] Rule: Baseline - best of 31 - duration: 0.0936 s 116 | [INFO ] Rule: Baseline - best of 31 - duration: 0.0936 s 117 | [INFO ] Min diff 0.089249968762517 % -- max diff 0 % 118 | [INFO ] Setting warning diff to: 2.6441297745545915 119 | [INFO ] Calibrations done, now checking the rules 120 | [INFO ] Rule is fast enough, not measuring any further Embedded_EXE_Cloaking due to fast mode, diff 2.0181 % below alerting level: 2.6441 % 121 | [WARNING] Rule Methodology_Suspicious_Shortcut_LOLcommand slows down a search with 100 rules by 16.0362 % (Measured by best of 31 runs) 122 | [WARNING] Rule password_dump_TESTING slows down a search with 100 rules by 3.0184 % (Measured by best of 31 runs) 123 | [WARNING] Rule APT_Trojan_Win_REDFLARE_5 slows down a search with 100 rules by 9.8361 % (Measured by best of 31 runs) 124 | [INFO ] Rule is fast enough, not measuring any further APT_CN_Taskmasters_TimeStompingTool_Nov19_1 due to fast mode, diff 0.7347 % below alerting level: 2.6441 % 125 | [INFO ] Rule is fast enough, not measuring any further Hunting_Rule_ShikataGaNai due to fast mode, diff 2.4929 % below alerting level: 2.6441 % 126 | Processing rules... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00 127 | Warnings ━━━━━━━━━━━━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━ 50% 0:00:10 128 | ``` 129 | 130 | ## yara_mem_usage.py 131 | 132 | yara_mem_usage.py is a small script which measures the difference in used memory before and after loading and compiling a .yar file using 2 different methods. 133 | 134 | Example: 135 | ```bash 136 | $ ./yara_mem_usage.py all.yar 137 | all.yar: psutil: 17,464 KB -- resource: 17,476 KB 138 | ``` 139 | 140 | ## Contact 141 | 142 | Follow me on [twitter](https://twitter.com/cyb3rops). 143 | -------------------------------------------------------------------------------- /panopticon.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | # -*- coding: iso-8859-1 -*- 5 | # -*- coding: utf-8 -*- 6 | # 7 | # Panopticon 8 | # Florian Roth 9 | # Arnim Rupp 10 | # 11 | # IMPORTANT: Requires plyara 12 | 13 | __version__ = "0.4.0" 14 | 15 | import os 16 | import argparse 17 | import logging 18 | import platform 19 | import sys 20 | import plyara 21 | import math 22 | from plyara import utils as plutils 23 | import yara 24 | import traceback 25 | import time 26 | from rich.progress import track 27 | from rich.progress import Progress 28 | from rich.console import Console 29 | import gc 30 | #import pprint 31 | 32 | SAMPLE_SET = ['./samples'] 33 | CALIBRATION_RULE_HUGE = './baseline.yar' 34 | 35 | CALIBRATION_RULE_ONE = """ 36 | rule Calibration_Rule { 37 | strings: 38 | $ = "Fubar, you lose again!" 39 | $ = "A strange game. The only winning move is not to play." 40 | $ = "I'm sorry Dave, I'm afraid I can't do that." 41 | condition: 42 | 1 of them 43 | } 44 | """ 45 | 46 | with open(CALIBRATION_RULE_HUGE, 'r') as f: 47 | yr = f.read() 48 | 49 | CALIBRATION_RULES = yr 50 | 51 | samples_data=[] 52 | rule_num=0 53 | 54 | count = 0 55 | for s in SAMPLE_SET: 56 | if not os.path.exists(s): 57 | print("[E] Error: sample directory '%s' doesn't exist" % s) 58 | else: 59 | for (dirpath, dirnames, filenames) in os.walk(s): 60 | for filename in filenames: 61 | count += 1 62 | sample_file = os.path.join(dirpath, filename) 63 | with open(sample_file, 'rb') as fh: 64 | fdata = fh.read() 65 | samples_data.append(fdata) 66 | 67 | warnings_list =[] 68 | warning_rules_file = 'warning_rules.yar' 69 | warning_rules = open(warning_rules_file, 'w') 70 | warning_rules.write("// New panopticon run at " + time.strftime("%Y-%m-%d %H:%M:%S") + '\n\n' ) 71 | 72 | def log_warning_rich(msg, rule): 73 | Log.warning(msg) 74 | progress.console.print("[red]"+"[WARNING] "+"[/red]" + msg) 75 | progress.update(task2, advance=1) 76 | warnings_list.append(msg) 77 | warning_rules.write("// " + msg + "\n") 78 | warning_rules.write(rule) 79 | warning_rules.write("\n") 80 | 81 | def measure(yara_rule_string, cycles, progress, show_score=True, c_duration=0, rule_name="", alert_diff=0, single_rule=""): 82 | """ 83 | Measure rule performance 84 | :param yara_rule_string: the YARA rule to test 85 | :param cycles: number of iterations over the sample set 86 | :param progress: progress indicator 87 | :param show_score: show the performance score 88 | :param c_duration: duration of the calibration run 89 | :param alert_diff: difference to alert on 90 | :param single_rule: the separate rule that is going to be tested as string 91 | :return min_duration: minimal duration in seconds 92 | :return count: count of samples in the given samples folders 93 | :return diff_perc: the difference in percent 94 | """ 95 | try: 96 | y = yara.compile(source=yara_rule_string, externals={ 97 | 'filename': "", 98 | 'filepath': "", 99 | 'extension': "", 100 | 'filetype': "", 101 | 'md5': "", 102 | }) 103 | except Exception as e: 104 | Log.error("Error compiling YARA rule '%s' : %s" % ( rule_name, e )) 105 | return 0,0,0 106 | #Log.info("Scanning sample set %d times with rule: %s" % (cycles, rule_name )) 107 | min_duration=9999999999999999 108 | max_duration=0 109 | diff_perc = 0 110 | count_mega_slow = 0 111 | 112 | for _ in range(cycles): 113 | # do garbage collection to avoid that it happens during benchmarking 114 | gc.collect() 115 | 116 | start = time.time() 117 | for sample in samples_data: 118 | try: 119 | matches = y.match(data=sample, externals={ 120 | 'filename': "", 121 | 'filepath': "", 122 | 'extension': "", 123 | 'filetype': "", 124 | 'md5': "", 125 | }) 126 | except Exception as e: 127 | Log.error("Error matching YARA rule '%s' : %s" % ( rule_name, e)) 128 | traceback.print_exc() 129 | # TODO: sys.exit or not??? 130 | #sys.exit(1) 131 | end = time.time() 132 | duration = end - start 133 | 134 | if duration < min_duration: 135 | min_duration = duration 136 | #print("New min: ", duration) 137 | if duration > max_duration: 138 | max_duration = duration 139 | #print("New max: ", duration) 140 | 141 | # If a calibration duration has been evaluated 142 | if c_duration > 0: 143 | # print("%s - %s" % (c_duration, min_duration)) 144 | diff_perc = ( (min_duration / c_duration -1)*100 ) 145 | # print("Diff Percentage: %0.4f" % diff_perc) 146 | 147 | # skip test if this scan was too fast 148 | if not slow_mode and diff_perc < alert_diff: 149 | progress.console.print("[INFO ] Rule \"%s\" is fast enough, not measuring any further due to fast mode, diff %0.4f %% below alerting level: %0.4f %%" % (rule_name, diff_perc, alert_diff )) 150 | return 0,0,0 151 | 152 | # stop test if this scan was mega slow for 10th time => warning because alert_diff is high enough 153 | if not slow_mode and diff_perc > 2 * alert_diff: 154 | count_mega_slow += 1 155 | if count_mega_slow > 10: 156 | break 157 | 158 | if c_duration and not rule_name == "Baseline": 159 | if diff_perc > alert_diff: 160 | log_warning_rich("Rule \"%s\" slows down a search with %d rules by %0.4f %% (Measured by best of %d runs)" % (rule_name, num_calib_rules, diff_perc , cycles ), single_rule) 161 | else: 162 | if show_score: 163 | progress.console.print("[INFO ] Rule: \"%s\" - Best of %d - duration: %.4f s (%0.4f s, %0.4f %%)" % (rule_name, cycles, min_duration, (min_duration-c_duration), diff_perc )) 164 | else: 165 | progress.console.print("[INFO ] Rule: \"%s\" - best of %d - duration: %.4f s" % (rule_name, cycles, min_duration)) 166 | return min_duration, count, diff_perc 167 | 168 | 169 | if __name__ == '__main__': 170 | 171 | print(" ___ __ _ ") 172 | print(" / _ \\___ ____ ___ ___ / /_(_)______ ___ ") 173 | print(" / ___/ _ `/ _ \\/ _ \\/ _ \\/ __/ / __/ _ \\/ _ \\ ") 174 | print(" /_/ \\_,_/_//_/\\___/ .__/\\__/_/\\__/\\___/_//_/ ") 175 | print(" by Florian Roth /_/ v%s " % __version__) 176 | print(" and Arnim Rupp") 177 | print(" ") 178 | print(" YARA Rule Performance Testing") 179 | 180 | # Parse Arguments 181 | parser = argparse.ArgumentParser(description='YARA RULE PERFORMANCE TESTER') 182 | parser.add_argument('-f', action='append', nargs='+', help='Path to input files (YARA rules, separated by space)', 183 | metavar='yara files') 184 | parser.add_argument('-d', action='append', nargs='+', 185 | help='Path to input directory (YARA rules folders, separated by space)', metavar='yara files') 186 | parser.add_argument('-l', help='Log file (default: panopticon.log)', metavar='logfile', default=r'panopticon.log') 187 | parser.add_argument('-i', help='Number of iterations (default: auto)', metavar='iterations') 188 | parser.add_argument('-s', help='Number of seconds to spend for each rule\'s measurement', metavar='seconds', default=30) 189 | parser.add_argument('-c', help='How often to run the iterations on the calibration rule', metavar='baseline_calib_times', default=5) 190 | parser.add_argument('-m', help='Number of normal runs to measure accuracy of calibration rule', metavar='baseline_test_times', default=5) 191 | parser.add_argument('-S', help='Slow mode, don\'t skip rule on first quick scan', action='store_true', default=False) 192 | parser.add_argument('-a', help='Alert bonus in percent: Only alert rules which slow down scans by this much percent (+ measured inaccuracy)', default=3) 193 | args = parser.parse_args() 194 | 195 | # don't to fast mode during calibration, it's set later by param 196 | slow_mode = True 197 | alert_bonus = int(args.a) 198 | 199 | # Logging 200 | logFormatter = logging.Formatter("[%(levelname)-7.7s] %(message)s") 201 | logFormatterRemote = logging.Formatter("{0} [%(levelname)-7.7s] %(message)s".format(platform.uname()[1])) 202 | Log = logging.getLogger(__name__) 203 | Log.setLevel(logging.INFO) 204 | # File Handler 205 | fileHandler = logging.FileHandler(args.l) 206 | fileHandler.setFormatter(logFormatter) 207 | Log.addHandler(fileHandler) 208 | # Console Handler 209 | consoleHandler = logging.StreamHandler() 210 | consoleHandler.setFormatter(logFormatter) 211 | Log.addHandler(consoleHandler) 212 | 213 | Log.info("Starting measurement at: " + time.strftime("%Y-%m-%d %H:%M:%S") ) 214 | 215 | # Check the YARA rule input files and directories 216 | input_files = [] 217 | if args.f or args.d: 218 | # File list 219 | if args.f: 220 | for f in args.f[0]: 221 | if not os.path.exists(f): 222 | Log.error("[E] Error: input file '%s' doesn't exist" % f) 223 | sys.exit(1) 224 | else: 225 | input_files.append(f) 226 | # Directory list 227 | if args.d: 228 | for d in args.d[0]: 229 | if not os.path.exists(d): 230 | Log.error("[E] Error: input directory '%s' doesn't exist" % d) 231 | sys.exit(1) 232 | else: 233 | for f in (os.listdir(d)): 234 | if ".yar" in f: 235 | input_files.append(os.path.join(d, f)) 236 | 237 | # Loop over input files 238 | rules_list = [] 239 | rules_all = "" 240 | for f in input_files: 241 | # Parse YARA rules to Dictionary 242 | if not os.path.exists(f): 243 | Log.error("Cannot find input file '%s'" % f) 244 | sys.exit(1) 245 | try: 246 | Log.info("Processing rules from %s ..." % f) 247 | p = plyara.Plyara() 248 | file_data = "" 249 | # Read file 250 | with open(f, 'r') as fh: 251 | file_data = fh.read() 252 | # Skip files without rule 253 | if 'rule' not in file_data: 254 | continue 255 | rules_all += file_data 256 | except Exception as e: 257 | Log.error("Can't process YARA rule file '%s'" % f) 258 | traceback.print_exc() 259 | 260 | # Now parse the YARA rules 261 | try: 262 | rules_list += p.parse_string(file_data) 263 | Log.info("Parsed %d rules from %s" % (len(rules_list), f)) 264 | # input_file_names.append(os.path.basename(f)) 265 | except Exception as e: 266 | Log.error("Error parsing YARA rule file '%s'" % f) 267 | traceback.print_exc() 268 | sys.exit(1) 269 | 270 | # Check the imports used by the rules to be tested 271 | used_imports = [] 272 | for rule in rules_list: 273 | if "imports" in rule: 274 | for i in rule["imports"]: 275 | if i not in used_imports: 276 | used_imports.append(i) 277 | Log.info("Imports used by the rules to test (will be prepended to the calibration set): %s" % ' '.join(used_imports)) 278 | 279 | # Preparing the calibration rules 280 | p = plyara.Plyara() 281 | # Appending the imports used in the test rules 282 | prepend_imports = "" 283 | for i in used_imports: 284 | prepend_imports += 'import "%s"' % i 285 | calibration_rule_set = prepend_imports + CALIBRATION_RULES 286 | # Parse the calibration rule set 287 | calibration_rules = p.parse_string(calibration_rule_set) 288 | num_calib_rules = len(calibration_rules) 289 | Log.info("Number of calibration rules: " + str(num_calib_rules)) 290 | 291 | # Now start the measurements 292 | with Progress(transient=True) as progress: 293 | # Calibration 294 | if not args.i: 295 | # Evaluate an optimal amount of cycles if nothing has been set manually 296 | calib_duration, sample_count, diff_perc = measure(calibration_rule_set, 1, progress, show_score=False, rule_name='Baseline') 297 | # One measurement should take 5 seconds 298 | auto_cycles = math.ceil(int(args.s) / calib_duration) 299 | cycles = auto_cycles 300 | else: 301 | # When cycle setting(option -i), occur error "sample_count not setting error" 302 | # set measure function same, error fix! 303 | # Evaluate an optimal amount of cycles if nothing has been set manually 304 | calib_duration, sample_count, diff_perc = measure(calibration_rule_set, 1, progress, show_score=False, rule_name='Baseline') 305 | cycles = int(args.i) 306 | 307 | # Startup 308 | Log.info("Auto-evaluation calculated that the defined %d seconds per rule can be accomplished by %d cycles per " 309 | "rule over the given sample set of %d samples" % (int(args.s), cycles, sample_count)) 310 | Log.info("Running %d cycles over the sample set" % cycles) 311 | Log.info("Now the benchmarking begins ... (try not cause any load on the system during benchmarking)") 312 | 313 | # Calibration Score 314 | baseline_calib_times = int(args.c) 315 | baseline_test_times = int(args.m) 316 | 317 | Log.info("Running 1st baseline measurement %s times with %s cycles (dropping the worst run) to get an average duration" % (baseline_test_times, cycles)) 318 | crule_duration_total=0 319 | crule_duration_max=0 320 | for x in range(baseline_calib_times): 321 | crule_duration_tmp, count, diff_perc = measure(calibration_rule_set, cycles, progress, show_score=True, rule_name='Baseline') 322 | crule_duration_total += crule_duration_tmp 323 | if crule_duration_tmp > crule_duration_max: 324 | crule_duration_max = crule_duration_tmp 325 | 326 | # drop worst result 327 | if baseline_calib_times > 1: 328 | crule_duration= ( crule_duration_total - crule_duration_max ) / ( baseline_calib_times -1 ) 329 | else: 330 | crule_duration= crule_duration_total 331 | Log.info("Calibrate average baseline duration: " + str(crule_duration)) 332 | 333 | if baseline_test_times: 334 | Log.info("Running 2nd baseline measurement %s times with %s cycles (dropping the worst run) to measure inaccuracy level" % (baseline_test_times, cycles)) 335 | min_diff_perc = 9999999999999999 336 | max_diff_perc = 0 337 | max_diff_perc_2nd = 0 338 | for x in range(baseline_test_times): 339 | crule_duration_tmp, count, diff_perc = measure(calibration_rule_set, cycles, progress, c_duration=crule_duration, show_score=True, rule_name='Baseline') 340 | if crule_duration_tmp < crule_duration: 341 | crule_duration_new = crule_duration_tmp 342 | if diff_perc < min_diff_perc: 343 | min_diff_perc = diff_perc 344 | if diff_perc > max_diff_perc: 345 | max_diff_perc_2nd = max_diff_perc 346 | max_diff_perc = diff_perc 347 | Log.info("Min diff " + str(min_diff_perc) + " % -- max diff " + str(max_diff_perc_2nd) + " %") 348 | 349 | # we ignore results faster than baseline because that shouldn't happen anyway ;) 350 | # if it's slower, only alert it of it's at least 50% higher than the (imperfectly) measured inaccuracy + alert_bonus set by the user 351 | if max_diff_perc_2nd: 352 | alert_diff = max_diff_perc_2nd * 1.5 + alert_bonus 353 | else: 354 | alert_diff = max_diff_perc * 1.5 + alert_bonus 355 | Log.info("Setting warning diff to: " + str(alert_diff)) 356 | 357 | slow_mode = args.S 358 | 359 | # avoid mixing up output of rich and logging to console, only log to file from here on 360 | # yep, this mix of logging and rich is ugly 361 | Log.removeHandler(consoleHandler) 362 | msg ="Calibrations done, now checking the rules" 363 | Log.info(msg) 364 | progress.console.print("[INFO ][green] " + msg) 365 | 366 | rule_num=len(rules_list) 367 | task1 = progress.add_task("[green]Processing rules...", total=rule_num) 368 | if rule_num > 100: 369 | warning_bar_num = 100 370 | else: 371 | warning_bar_num = rule_num 372 | task2 = progress.add_task("[red]Warnings", total=warning_bar_num) 373 | 374 | # first test all at once (this might easily fail on multiple files with duplicate rulenames) 375 | #measure_rule = CALIBRATION_RULES + rules_all 376 | #rule_name = "All " + str(len(rules_list)) + " rules from all input files" 377 | #measure(measure_rule, cycles, progress, show_score=True, c_duration=crule_duration, rule_name=rule_name, alert_diff=alert_diff) 378 | 379 | # Scan files 380 | for r in rules_list: 381 | yara_rule_string = plutils.rebuild_yara_rule(r) 382 | rule_name = r['rule_name'] 383 | if len(yara_rule_string) > 20000: 384 | log_warning_rich("Big rule: " + rule_name + " has " + str(len(yara_rule_string)) + " bytes", yara_rule_string) 385 | 386 | measure_rule = calibration_rule_set + yara_rule_string 387 | 388 | measure(measure_rule, cycles, progress, show_score=True, c_duration=crule_duration, rule_name=rule_name, alert_diff=alert_diff, single_rule=yara_rule_string) 389 | progress.update(task1, advance=1) 390 | 391 | # rich console 392 | console = Console() 393 | console.print("") 394 | console.print("----------------------------------------------------------------------------------------------------------------") 395 | console.print("Done scanning " + str(rule_num) + " rules.") 396 | if warnings_list: 397 | console.print("Check the collected warnings below are look in " + args.l + " for \"WARNING\".") 398 | console.print("All offending rules written to \"" + warning_rules_file + "\" (hint: useful for rechecking)") 399 | for msg in warnings_list: 400 | console.print("[red]"+"[WARNING] "+"[/red]" + msg) 401 | else: 402 | console.print("Everything [green]ok[/green], log written to " + args.l) 403 | 404 | # reenable console logging because this should be on screen and in logfile 405 | Log.addHandler(consoleHandler) 406 | Log.info("Ending measurement at: " + time.strftime("%Y-%m-%d %H:%M:%S") ) 407 | warning_rules.write("// End of panopticon run at " + time.strftime("%Y-%m-%d %H:%M:%S") + '\n\n' ) 408 | -------------------------------------------------------------------------------- /baseline_50.yar: -------------------------------------------------------------------------------- 1 | 2 | // imports ^ will be prepeneded dynamically based on the imports found in the test rule set 3 | 4 | rule baseline_PowerShell_Case_Anomaly { 5 | meta: 6 | description = "Detects obfuscated PowerShell hacktools" 7 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 8 | author = "Florian Roth" 9 | reference = "https://twitter.com/danielhbohannon/status/905096106924761088" 10 | date = "2017-08-11" 11 | score = 70 12 | strings: 13 | // first detect 'powershell' keyword case insensitive 14 | $s1 = "powershell" fullword nocase ascii wide 15 | // define the normal cases 16 | $sr1 = /(powershell|Powershell|PowerShell|POWERSHELL|powerShell)/ fullword ascii wide 17 | // define the normal cases 18 | $sn1 = "powershell" fullword ascii wide 19 | $sn2 = "Powershell" fullword ascii wide 20 | $sn3 = "PowerShell" fullword ascii wide 21 | $sn4 = "POWERSHELL" fullword ascii wide 22 | $sn5 = "powerShell" fullword ascii wide 23 | 24 | // PowerShell with \x19\x00\x00 25 | $a1 = "wershell -e " nocase wide ascii 26 | // expected casing 27 | $an1 = "wershell -e " wide ascii 28 | $an2 = "werShell -e " wide ascii 29 | 30 | // adding a keyword with a sufficent length and relevancy 31 | $k1 = "-noprofile" fullword nocase ascii wide 32 | // define normal cases 33 | $kn1 = "-noprofile" ascii wide 34 | $kn2 = "-NoProfile" ascii wide 35 | $kn3 = "-noProfile" ascii wide 36 | $kn4 = "-NOPROFILE" ascii wide 37 | $kn5 = "-Noprofile" ascii wide 38 | 39 | $fp1 = "Microsoft Code Signing" ascii fullword 40 | $fp2 = "Microsoft Corporation" ascii 41 | condition: 42 | filesize < 800KB and ( 43 | // find all 'powershell' occurances and ignore the expected cases 44 | ( #s1 < 3 and #sr1 > 0 and #s1 > #sr1 ) or 45 | ( $s1 and not 1 of ($sn*) ) or 46 | ( $a1 and not 1 of ($an*) ) or 47 | // find all '-norpofile' occurances and ignore the expected cases 48 | ( $k1 and not 1 of ($kn*) ) 49 | ) and not 1 of ($fp*) 50 | } 51 | 52 | rule baseline_WScriptShell_Case_Anomaly { 53 | meta: 54 | description = "Detects obfuscated wscript.shell commands" 55 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 56 | author = "Florian Roth" 57 | reference = "Internal Research" 58 | date = "2017-09-11" 59 | score = 60 60 | strings: 61 | // first detect powershell keyword case insensitive 62 | $s1 = "WScript.Shell\").Run" nocase ascii wide 63 | // define the normal cases 64 | $sn1 = "WScript.Shell\").Run" ascii wide 65 | $sn2 = "wscript.shell\").run" ascii wide 66 | $sn3 = "WSCRIPT.SHELL\").RUN" ascii wide 67 | $sn4 = "Wscript.Shell\").Run" ascii wide 68 | $sn5 = "WScript.Shell\").Run" ascii wide 69 | $sn6 = "WScript.shell\").Run" ascii wide 70 | condition: 71 | filesize < 800KB and 72 | ( $s1 and not 1 of ($sn*) ) 73 | } 74 | 75 | rule baseline_SUSP_BAT_Aux_Jan20_1 { 76 | meta: 77 | description = "Detects BAT file often dropped to cleanup temp dirs during infection" 78 | author = "Florian Roth" 79 | reference = "https://medium.com/@quoscient/the-chicken-keeps-laying-new-eggs-uncovering-new-gc-maas-tools-used-by-top-tier-threat-actors-531d80a6b4e9" 80 | date = "2020-01-29" 81 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 82 | score = 65 83 | hash1 = "f5d558ec505b635b1e37557350562ad6f79b3da5cf2cf74db6e6e648b7a47127" 84 | strings: 85 | $s1 = "if exist \"C:\\Users\\" ascii 86 | $s2 = "\\AppData\\Local\\Temp\\" ascii 87 | $s3 = "del \"C:\\Users\\" ascii 88 | $s4 = ".bat\"" ascii 89 | $s5 = ".exe\" goto" ascii 90 | condition: 91 | uint8(0) == 0x3a and filesize <= 1KB and all of them 92 | } 93 | 94 | rule baseline_MAL_Trickbot_Oct19_1 { 95 | meta: 96 | description = "Detects Trickbot malware" 97 | author = "Florian Roth" 98 | reference = "Internal Research" 99 | date = "2019-10-02" 100 | hash1 = "58852140a2dc30e799b7d50519c56e2fd3bb506691918dbf5d4244cc1f4558a2" 101 | hash2 = "aabf54eb27de3d72078bbe8d99a92f5bcc1e43ff86774eb5321ed25fba5d27d4" 102 | hash3 = "9d6e4ad7f84d025bbe9f95e74542e7d9f79e054f6dcd7b37296f01e7edd2abae" 103 | strings: 104 | $s1 = "Celestor@hotmail.com" fullword ascii 105 | $s2 = "\\txtPassword" fullword ascii 106 | $s14 = "Invalid Password, try again!" fullword wide 107 | 108 | $op1 = { 78 c4 40 00 ff ff ff ff b4 47 41 } 109 | $op2 = { 9b 68 b2 34 46 00 eb 14 8d 55 e4 8d 45 e8 52 50 } 110 | condition: 111 | uint16(0) == 0x5a4d and filesize <= 2000KB and 3 of them 112 | } 113 | 114 | 115 | rule baseline_MAL_Nitol_Malware_Jan19_1 { 116 | meta: 117 | description = "Detects Nitol Malware" 118 | author = "Florian Roth" 119 | reference = "https://twitter.com/shotgunner101/status/1084602413691166721" 120 | date = "2019-01-14" 121 | hash1 = "fe65f6a79528802cb61effc064476f7b48233fb0f245ddb7de5b7cc8bb45362e" 122 | strings: 123 | $xc1 = { 00 25 75 20 25 73 00 00 00 30 2E 30 2E 30 2E 30 124 | 00 25 75 20 4D 42 00 00 00 25 64 2A 25 75 25 73 125 | 00 7E 4D 48 7A } 126 | $xc2 = "GET ^&&%$%$^" ascii 127 | 128 | $n1 = ".htmGET " ascii 129 | 130 | $s1 = "User-Agent:Mozilla/4.0 (compatible; MSIE %d.00; Windows NT %d.0; MyIE 3.01)" fullword ascii 131 | $s2 = "User-Agent:Mozilla/4.0 (compatible; MSIE %d.0; Windows NT %d.1; SV1)" fullword ascii 132 | $s3 = "User-Agent:Mozilla/5.0 (X11; U; Linux i686; en-US; re:1.4.0) Gecko/20080808 Firefox/%d.0" fullword ascii 133 | $s4 = "User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" fullword ascii 134 | condition: 135 | uint16(0) == 0x5a4d and filesize < 1000KB and ( 136 | 1 of ($x*) or 137 | #n1 > 4 or 138 | 4 of them 139 | ) 140 | } 141 | 142 | rule baseline_MAL_RANSOM_RobinHood_May19_1 { 143 | meta: 144 | description = "Detects RobinHood Ransomware" 145 | author = "Florian Roth" 146 | reference = "https://twitter.com/BThurstonCPTECH/status/1128489465327030277" 147 | date = "2019-05-15" 148 | hash1 = "21cb84fc7b33e8e31364ff0e58b078db8f47494a239dc3ccbea8017ff60807e3" 149 | strings: 150 | $s1 = ".enc_robbinhood" ascii 151 | $s2 = "c:\\windows\\temp\\pub.key" ascii fullword 152 | $s3 = "cmd.exe /c net use * /DELETE /Y" ascii 153 | $s4 = "sc.exe stop SQLAgent$SQLEXPRESS" nocase 154 | $s5 = "main.EnableShadowFucks" nocase 155 | $s6 = "main.EnableRecoveryFCK" nocase 156 | $s7 = "main.EnableLogLaunders" nocase 157 | $s8 = "main.EnableServiceFuck" nocase 158 | condition: 159 | uint16(0) == 0x5a4d and filesize < 8000KB and 1 of them 160 | } 161 | 162 | rule baseline_PoS_Malware_MalumPOS 163 | { 164 | meta: 165 | author = "Trend Micro, Inc." 166 | date = "2015-05-25" 167 | description = "Used to detect MalumPOS memory dumper" 168 | sample_filtype = "exe" 169 | strings: 170 | $string1 = "SOFTWARE\\Borland\\Delphi\\RTL" 171 | $string2 = "B)[0-9]{13,19}\\" 172 | $string3 = "[A-Za-z\\s]{0,30}\\/[A-Za-z\\s]{0,30}\\" 173 | $string4 = "TRegExpr(exec): ExecNext Without Exec[Pos]" 174 | $string5 = /Y:\\PROGRAMS\\.{20,300}\.pas/ 175 | condition: 176 | all of ($string*) 177 | } 178 | 179 | rule baseline_APT_Sandworm_Keywords_May20_1 { 180 | meta: 181 | description = "Detects commands used by Sandworm group to exploit critical vulernability CVE-2019-10149 in Exim" 182 | author = "Florian Roth" 183 | reference = "https://media.defense.gov/2020/May/28/2002306626/-1/-1/0/CSA%20Sandworm%20Actors%20Exploiting%20Vulnerability%20in%20Exim%20Transfer%20Agent%2020200528.pdf" 184 | date = "2020-05-28" 185 | strings: 186 | $x1 = "MAIL FROM:<$(run(" 187 | $x2 = "exec\\x20\\x2Fusr\\x2Fbin\\x2Fwget\\x20\\x2DO\\x20\\x2D\\x20http" 188 | condition: 189 | filesize < 8000KB and 190 | 1 of them 191 | } 192 | 193 | rule baseline_APT_Sandworm_SSH_Key_May20_1 { 194 | meta: 195 | description = "Detects SSH key used by Sandworm on exploited machines" 196 | author = "Florian Roth" 197 | reference = "https://media.defense.gov/2020/May/28/2002306626/-1/-1/0/CSA%20Sandworm%20Actors%20Exploiting%20Vulnerability%20in%20Exim%20Transfer%20Agent%2020200528.pdf" 198 | date = "2020-05-28" 199 | hash1 = "dc074464e50502459038ac127b50b8c68ed52817a61c2f97f0add33447c8f730" 200 | hash2 = "538d713cb47a6b5ec6a3416404e0fc1ebcbc219a127315529f519f936420c80e" 201 | strings: 202 | $x1 = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2q/NGN/brzNfJiIp2zswtL33tr74pIAjMeWtXN1p5Hqp5fTp058U1EN4NmgmjX0KzNjjV" 203 | condition: 204 | filesize < 1000KB and 205 | 1 of them 206 | } 207 | 208 | rule baseline_APT_Sandworm_SSHD_Config_Modification_May20_1 { 209 | meta: 210 | description = "Detects ssh config entry inserted by Sandworm on compromised machines" 211 | author = "Florian Roth" 212 | reference = "https://media.defense.gov/2020/May/28/2002306626/-1/-1/0/CSA%20Sandworm%20Actors%20Exploiting%20Vulnerability%20in%20Exim%20Transfer%20Agent%2020200528.pdf" 213 | date = "2020-05-28" 214 | hash1 = "dc074464e50502459038ac127b50b8c68ed52817a61c2f97f0add33447c8f730" 215 | hash2 = "538d713cb47a6b5ec6a3416404e0fc1ebcbc219a127315529f519f936420c80e" 216 | strings: 217 | $x1 = "AllowUsers mysql_db" ascii 218 | 219 | $a1 = "ListenAddress" ascii fullword 220 | condition: 221 | filesize < 10KB and 222 | all of them 223 | } 224 | 225 | 226 | rule baseline_SUSP_ZIP_NtdsDIT : T1003_003 { 227 | meta: 228 | description = "Detects ntds.dit files in ZIP archives that could be a left over of administrative activity or traces of data exfiltration" 229 | author = "Florian Roth" 230 | score = 50 231 | reference = "https://pentestlab.blog/2018/07/04/dumping-domain-password-hashes/" 232 | date = "2020-08-10" 233 | strings: 234 | $s1 = "ntds.dit" ascii 235 | condition: 236 | uint16(0) == 0x4b50 and 237 | $s1 in (0..256) 238 | } 239 | 240 | rule baseline_MAL_BackNet_Nov18_1 { 241 | meta: 242 | description = "Detects BackNet samples" 243 | author = "Florian Roth" 244 | reference = "https://github.com/valsov/BackNet" 245 | date = "2018-11-02" 246 | hash1 = "4ce82644eaa1a00cdb6e2f363743553f2e4bd1eddb8bc84e45eda7c0699d9adc" 247 | strings: 248 | $s1 = "ProcessedByFody" fullword ascii 249 | $s2 = "SELECT * FROM AntivirusProduct" fullword wide 250 | $s3 = "/C netsh wlan show profile" wide 251 | $s4 = "browsertornado" fullword wide 252 | $s5 = "Current user is administrator" fullword wide 253 | $s6 = "/C choice /C Y /N /D Y /T 4 & Del" wide 254 | $s7 = "ThisIsMyMutex-2JUY34DE8E23D7" wide 255 | condition: 256 | uint16(0) == 0x5a4d and filesize < 2000KB and 2 of them 257 | } 258 | 259 | rule baseline_remsec_executable_blob_32 { 260 | meta: 261 | copyright = "Symantec" 262 | description = "Detects malware from Symantec's Strider APT report" 263 | score = 80 264 | date = "2016/08/08" 265 | reference = "http://www.symantec.com/connect/blogs/strider-cyberespionage-group-turns-eye-sauron-targets" 266 | strings: 267 | $code = { 31 06 83 C6 04 D1 E8 73 05 35 01 00 00 D0 E2 F0 } 268 | condition: 269 | all of them 270 | } 271 | 272 | rule baseline_remsec_executable_blob_64 { 273 | meta: 274 | copyright = "Symantec" 275 | description = "Detects malware from Symantec's Strider APT report" 276 | score = 80 277 | date = "2016/08/08" 278 | reference = "http://www.symantec.com/connect/blogs/strider-cyberespionage-group-turns-eye-sauron-targets" 279 | strings: 280 | $code = { 31 06 48 83 C6 04 D1 E8 73 05 35 01 00 00 D0 E2 EF } 281 | condition: 282 | all of them 283 | } 284 | 285 | rule baseline_remsec_executable_blob_parser { 286 | meta: 287 | copyright = "Symantec" 288 | description = "Detects malware from Symantec's Strider APT report" 289 | score = 80 290 | date = "2016/08/08" 291 | reference = "http://www.symantec.com/connect/blogs/strider-cyberespionage-group-turns-eye-sauron-targets" 292 | strings: 293 | $code = { ( 0F 82 ?? ?? 00 00 | 72 ?? ) ( 80 | 41 80 ) ( 7? | 7C 24 ) 04 02 ( 0F 85 ?? ?? 00 00 | 75 ?? ) ( 81 | 41 81 ) ( 3? | 3C 24 | 7D 00 ) 02 AA 02 C1 ( 0F 85 ?? ?? 00 00 | 75 ?? ) ( 8B | 41 8B | 44 8B | 45 8B ) ( 4? | 5? | 6? | 7? | ?4 24 | ?C 24 ) 06 } 294 | condition: 295 | all of them 296 | } 297 | 298 | rule baseline_remsec_encrypted_api { 299 | meta: 300 | copyright = "Symantec" 301 | description = "Detects malware from Symantec's Strider APT report" 302 | score = 80 303 | date = "2016/08/08" 304 | reference = "http://www.symantec.com/connect/blogs/strider-cyberespionage-group-turns-eye-sauron-targets" 305 | strings: 306 | $open_process = { 91 9A 8F B0 9C 90 8D AF 8C 8C 9A FF } 307 | condition: 308 | all of them 309 | } 310 | 311 | 312 | rule baseline_TA17_318A_rc4_stack_key_fallchill { 313 | meta: 314 | description = "HiddenCobra FallChill - rc4_stack_key" 315 | author = "US CERT" 316 | reference = "https://www.us-cert.gov/ncas/alerts/TA17-318B" 317 | date = "2017-11-15" 318 | strings: 319 | $stack_key = { 0d 06 09 2a ?? ?? ?? ?? 86 48 86 f7 ?? ?? ?? ?? 0d 01 01 01 ?? ?? ?? ?? 05 00 03 82 41 8b c9 41 8b d1 49 8b 40 08 48 ff c2 88 4c 02 ff ff c1 81 f9 00 01 00 00 7c eb } 320 | condition: 321 | (uint16(0) == 0x5A4D and uint16(uint32(0x3c)) == 0x4550) and $stack_key 322 | } 323 | 324 | rule baseline_TA17_318A_success_fail_codes_fallchill { 325 | meta: 326 | description = "HiddenCobra FallChill - success_fail_codes" 327 | author = "US CERT" 328 | reference = "https://www.us-cert.gov/ncas/alerts/TA17-318B" 329 | date = "2017-11-15" 330 | strings: 331 | $s0 = { 68 7a 34 12 00 } 332 | $s1 = { ba 7a 34 12 00 } 333 | $f0 = { 68 5c 34 12 00 } 334 | $f1 = { ba 5c 34 12 00 } 335 | condition: 336 | (uint16(0) == 0x5A4D and uint16(uint32(0x3c)) == 0x4550) and (($s0 and $f0) or ($s1 and $f1)) 337 | } 338 | 339 | rule baseline_Backdoor_Naikon_APT_Sample1 { 340 | meta: 341 | description = "Detects backdoors related to the Naikon APT" 342 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 343 | author = "Florian Roth" 344 | reference = "https://goo.gl/7vHyvh" 345 | date = "2015-05-14" 346 | hash = "d5716c80cba8554eb79eecfb4aa3d99faf0435a1833ec5ef51f528146c758eba" 347 | hash = "f5ab8e49c0778fa208baad660fe4fa40fc8a114f5f71614afbd6dcc09625cb96" 348 | strings: 349 | $x0 = "GET http://%s:%d/aspxabcdef.asp?%s HTTP/1.1" fullword ascii 350 | $x1 = "POST http://%s:%d/aspxabcdefg.asp?%s HTTP/1.1" fullword ascii 351 | $x2 = "greensky27.vicp.net" fullword ascii 352 | $x3 = "\\tempvxd.vxd.dll" fullword wide 353 | $x4 = "otna.vicp.net" fullword ascii 354 | $x5 = "smithking19.gicp.net" fullword ascii 355 | 356 | $s1 = "User-Agent: webclient" fullword ascii 357 | $s2 = "\\User.ini" fullword ascii 358 | $s3 = "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-EN; rv:1.7.12) Gecko/200" ascii 359 | $s4 = "\\UserProfile.dll" fullword wide 360 | $s5 = "Connection:Keep-Alive: %d" fullword ascii 361 | $s6 = "Referer: http://%s:%d/" fullword ascii 362 | $s7 = "%s %s %s %d %d %d " fullword ascii 363 | $s8 = "%s--%s" fullword wide 364 | $s9 = "Run File Success!" fullword wide 365 | $s10 = "DRIVE_REMOTE" fullword wide 366 | $s11 = "ProxyEnable" fullword wide 367 | $s12 = "\\cmd.exe" fullword wide 368 | condition: 369 | uint16(0) == 0x5a4d and filesize < 1000KB and 370 | ( 371 | 1 of ($x*) or 7 of ($s*) 372 | ) 373 | } 374 | 375 | 376 | rule baseline_Trojan_Win32_Plainst2 : Platinum 377 | { 378 | meta: 379 | author = "Microsoft" 380 | description = "Zc tool" 381 | original_sample_sha1 = "3f2ce812c38ff5ac3d813394291a5867e2cddcf2" 382 | unpacked_sample_sha1 = "88ff852b1b8077ad5a19cc438afb2402462fbd1a" 383 | activity_group = "Platinum" 384 | version = "1.0" 385 | last_modified = "2016-04-12" 386 | 387 | strings: 388 | $str1 = "Connected [%s:%d]..." 389 | $str2 = "reuse possible: %c" 390 | $str3 = "] => %d%%\x0a" 391 | 392 | condition: 393 | $str1 and $str2 and $str3 394 | } 395 | 396 | rule baseline_Trojan_Win32_Plakpeer : Platinum 397 | { 398 | meta: 399 | author = "Microsoft" 400 | description = "Zc tool v2" 401 | original_sample_sha1 = "2155c20483528377b5e3fde004bb604198463d29" 402 | unpacked_sample_sha1 = "dc991ef598825daabd9e70bac92c79154363bab2" 403 | activity_group = "Platinum" 404 | version = "1.0" 405 | last_modified = "2016-04-12" 406 | 407 | strings: 408 | $str1 = "@@E0020(%d)" wide 409 | $str2 = /exit.{0,3}@exit.{0,3}new.{0,3}query.{0,3}rcz.{0,3}scz/ wide 410 | $str3 = "---###---" wide 411 | $str4 = "---@@@---" wide 412 | 413 | condition: 414 | $str1 and $str2 and $str3 and $str4 415 | } 416 | 417 | rule baseline_HttpBrowser_RAT_dropper_Gen1 { 418 | meta: 419 | description = "Threat Group 3390 APT Sample - HttpBrowser RAT Dropper" 420 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 421 | author = "Florian Roth" 422 | reference = "http://snip.ly/giNB" 423 | date = "2015-08-06" 424 | score = 70 425 | hash1 = "808de72f1eae29e3c1b2c32be1b84c5064865a235866edf5e790d2a7ba709907" 426 | hash2 = "f6f966d605c5e79de462a65df437ddfca0ad4eb5faba94fc875aba51a4b894a7" 427 | hash3 = "f424965a35477d822bbadb821125995616dc980d3d4f94a68c87d0cd9b291df9" 428 | hash4 = "01441546fbd20487cb2525a0e34e635eff2abe5c3afc131c7182113220f02753" 429 | hash5 = "8cd8159f6e4689f572e2087394452e80e62297af02ca55fe221fe5d7570ad47b" 430 | hash6 = "10de38419c9a02b80ab7bf2f1f1f15f57dbb0fbc9df14b9171dc93879c5a0c53" 431 | hash7 = "c2fa67e970d00279cec341f71577953d49e10fe497dae4f298c2e9abdd3a48cc" 432 | strings: 433 | $x1 = "1001=cmd.exe" fullword ascii 434 | $x2 = "1003=ShellExecuteA" fullword ascii 435 | $x3 = "1002=/c del /q %s" fullword ascii 436 | $x4 = "1004=SetThreadPriority" fullword ascii 437 | 438 | /* $s1 = "pnipcn.dllUT" fullword ascii 439 | $s2 = "ssonsvr.exeUT" fullword ascii 440 | $s3 = "navlu.dllUT" fullword ascii 441 | $s4 = "@CONOUT$" fullword wide 442 | $s5 = "VPDN_LU.exeUT" fullword ascii 443 | $s6 = "msi.dll.urlUT" fullword ascii 444 | $s7 = "setup.exeUT" fullword ascii 445 | $s8 = "pnipcn.dll.urlUT" fullword ascii 446 | $s9 = "ldvpreg.exeUT" fullword ascii */ 447 | 448 | $op0 = { e8 71 11 00 00 83 c4 10 ff 4d e4 8b f0 78 07 8b } /* Opcode */ 449 | $op1 = { e8 85 34 00 00 59 59 8b 86 b4 } /* Opcode */ 450 | $op2 = { 8b 45 0c 83 38 00 0f 84 97 } /* Opcode */ 451 | $op3 = { 8b 45 0c 83 38 00 0f 84 98 } /* Opcode */ 452 | $op4 = { 89 7e 0c ff 15 a0 50 40 00 59 8b d8 6a 20 59 8d } /* Opcode */ 453 | $op5 = { 56 8d 85 cd fc ff ff 53 50 88 9d cc fc ff ff e8 } /* Opcode */ 454 | condition: 455 | uint16(0) == 0x5a4d and filesize < 400KB and all of ($x*) and 1 of ($op*) 456 | } 457 | 458 | rule baseline_HttpBrowser_RAT_Sample1 { 459 | meta: 460 | description = "Threat Group 3390 APT Sample - HttpBrowser RAT Sample update.hancominc.com" 461 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 462 | author = "Florian Roth" 463 | reference = "http://snip.ly/giNB" 464 | date = "2015-08-06" 465 | score = 80 466 | hash1 = "be334d1f8fa65a723af65200a166c2bbdb06690c8b30fafe772600e4662fc68b" 467 | hash2 = "1052ad7f4d49542e4da07fa8ea59c15c40bc09a4d726fad023daafdf05866ebb" 468 | strings: 469 | $s0 = "update.hancominc.com" fullword wide 470 | condition: 471 | uint16(0) == 0x5a4d and filesize < 100KB and $s0 472 | } 473 | 474 | rule baseline_HttpBrowser_RAT_Sample2 { 475 | meta: 476 | description = "Threat Group 3390 APT Sample - HttpBrowser RAT Sample" 477 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 478 | author = "Florian Roth" 479 | reference = "http://snip.ly/giNB" 480 | date = "2015-08-06" 481 | score = 80 482 | hash1 = "c57c5a2c322af2835ae136b75283eaaeeaa6aa911340470182a9983ae47b8992" 483 | strings: 484 | $s0 = "nKERNEL32.DLL" fullword wide 485 | $s1 = "WUSER32.DLL" fullword wide 486 | $s2 = "mscoree.dll" fullword wide 487 | $s3 = "VPDN_LU.exeUT" fullword ascii 488 | condition: 489 | uint16(0) == 0x5a4d and filesize < 250KB and all of them 490 | } 491 | 492 | rule baseline_HttpBrowser_RAT_Gen { 493 | meta: 494 | description = "Threat Group 3390 APT Sample - HttpBrowser RAT Generic" 495 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 496 | author = "Florian Roth" 497 | reference = "http://snip.ly/giNB" 498 | date = "2015-08-06" 499 | score = 90 500 | hash1 = "0299493ccb175d452866f5e21d023d3e92cd8d28452517d1d19c0f05f2c5ca27" 501 | hash2 = "065d055a90da59b4bdc88b97e537d6489602cb5dc894c5c16aff94d05c09abc7" 502 | hash3 = "05c7291db880f94c675eea336ecd66338bd0b1d49ad239cc17f9df08106e6684" 503 | hash4 = "07133f291fe022cd14346cd1f0a649aa2704ec9ccadfab809ca9c48b91a7d81b" 504 | hash5 = "0f8893e87ddec3d98e39a57f7cd530c28e36d596ea0a1d9d1e993dc2cae0a64d" 505 | hash6 = "108e6633744da6efe773eb78bd0ac804920add81c3dde4b26e953056ac1b26c5" 506 | hash7 = "1052ad7f4d49542e4da07fa8ea59c15c40bc09a4d726fad023daafdf05866ebb" 507 | hash8 = "1277ede988438d4168bb5b135135dd3b9ae7d9badcdf1421132ca4692dd18386" 508 | hash9 = "19be90c152f7a174835fd05a0b6f722e29c648969579ed7587ae036679e66a7b" 509 | hash10 = "1e7133bf5a9fe5e462321aafc2b7770b8e4183a66c7fef14364a0c3f698a29af" 510 | hash11 = "2264e5e8fcbdcb29027798b200939ecd8d1d3ad1ef0aef2b8ce7687103a3c113" 511 | hash12 = "2a1bdeb0a021fb0bdbb328bd4b65167d1f954c871fc33359cb5ea472bad6e13e" 512 | hash13 = "259a2e0508832d0cf3f4f5d9e9e1adde17102d2804541a9587a9a4b6f6f86669" 513 | hash14 = "240d9ce148091e72d8f501dbfbc7963997d5c2e881b4da59a62975ddcbb77ca2" 514 | hash15 = "211a1b195cf2cc70a2caf8f1aafb8426eb0e4bae955e85266490b12b5322aa16" 515 | hash16 = "2d25c6868c16085c77c58829d538b8f3dbec67485f79a059f24e0dce1e804438" 516 | hash17 = "2d932d764dd9b91166361d8c023d64a4480b5b587a6087b0ce3d2ac92ead8a7d" 517 | hash18 = "3556722d9aa37beadfa6ba248a66576f767e04b09b239d3fb0479fa93e0ba3fd" 518 | hash19 = "365e1d4180e93d7b87ba28ce4369312cbae191151ac23ff4a35f45440cb9be48" 519 | hash20 = "36c49f18ce3c205152eef82887eb3070e9b111d35a42b534b2fb2ee535b543c0" 520 | hash21 = "3eeb1fd1f0d8ab33f34183893c7346ddbbf3c19b94ba3602d377fa2e84aaad81" 521 | hash22 = "3fa8d13b337671323e7fe8b882763ec29b6786c528fa37da773d95a057a69d9a" 522 | strings: 523 | $s0 = "%d|%s|%04d/%02d/%02d %02d:%02d:%02d|%ld|%d" fullword wide 524 | $s1 = "HttpBrowser/1.0" fullword wide 525 | $s2 = "set cmd : %s" ascii fullword 526 | $s3 = "\\config.ini" wide fullword 527 | condition: 528 | uint16(0) == 0x5a4d and filesize < 45KB and filesize > 20KB and all of them 529 | } 530 | 531 | rule baseline_PlugX_NvSmartMax_Gen { 532 | meta: 533 | description = "Threat Group 3390 APT Sample - PlugX NvSmartMax Generic" 534 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 535 | author = "Florian Roth" 536 | reference = "http://snip.ly/giNB" 537 | date = "2015-08-06" 538 | score = 70 539 | hash1 = "718fc72942b9b706488575c0296017971170463f6f40fa19b08fc84b79bf0cef" 540 | hash2 = "1c0379481d17fc80b3330f148f1b87ff613cfd2a6601d97920a0bcd808c718d0" 541 | hash3 = "555952aa5bcca4fa5ad5a7269fece99b1a04816d104ecd8aefabaa1435f65fa5" 542 | hash4 = "71f7a9da99b5e3c9520bc2cc73e520598d469be6539b3c243fb435fe02e44338" 543 | hash5 = "65bbf0bd8c6e1ccdb60cf646d7084e1452cb111d97d21d6e8117b1944f3dc71e" 544 | strings: 545 | $s0 = "NvSmartMax.dll" fullword ascii 546 | $s1 = "NvSmartMax.dll.url" fullword ascii 547 | $s2 = "Nv.exe" fullword ascii 548 | $s4 = "CryptProtectMemory failed" fullword ascii 549 | $s5 = "CryptUnprotectMemory failed" fullword ascii 550 | $s7 = "r%.*s(%d)%s" fullword wide 551 | $s8 = " %s CRC " fullword wide 552 | 553 | $op0 = { c6 05 26 49 42 00 01 eb 4a 8d 85 00 f8 ff ff 50 } /* Opcode */ 554 | $op1 = { 8d 85 c8 fe ff ff 50 8d 45 c8 50 c6 45 47 00 e8 } /* Opcode */ 555 | $op2 = { e8 e6 65 00 00 50 68 10 43 41 00 e8 56 84 00 00 } /* Opcode */ 556 | condition: 557 | uint16(0) == 0x5a4d and filesize < 800KB and all of ($s*) and 1 of ($op*) 558 | } 559 | 560 | 561 | rule baseline_UACElevator { 562 | meta: 563 | description = "UACElevator bypassing UAC - file UACElevator.exe" 564 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 565 | author = "Florian Roth" 566 | reference = "https://github.com/MalwareTech/UACElevator" 567 | date = "2015-05-14" 568 | hash = "fd29d5a72d7a85b7e9565ed92b4d7a3884defba6" 569 | strings: 570 | $x1 = "\\UACElevator.pdb" ascii 571 | 572 | $s1 = "%userprofile%\\Downloads\\dwmapi.dll" fullword ascii 573 | $s2 = "%windir%\\system32\\dwmapi.dll" fullword ascii 574 | $s3 = "Infection module: %s" fullword ascii 575 | $s4 = "Could not save module to %s" fullword ascii 576 | $s5 = "%s%s%p%s%ld%s%d%s" fullword ascii 577 | $s6 = "Stack area around _alloca memory reserved by this function is corrupted" fullword ascii 578 | $s7 = "Stack around the variable '" fullword ascii 579 | $s8 = "MSVCR120D.dll" fullword wide 580 | $s9 = "Address: 0x" fullword ascii 581 | condition: 582 | uint16(0) == 0x5a4d and filesize < 172KB and 583 | ( $x1 or 8 of ($s*) ) 584 | } 585 | 586 | rule baseline_s4u { 587 | meta: 588 | description = "Detects s4u executable which allows the creation of a cmd.exe with the context of any user without requiring the password. - file s4u.exe" 589 | license = "https://creativecommons.org/licenses/by-nc/4.0/" 590 | author = "Florian Roth" 591 | reference = "https://github.com/aurel26/s-4-u-for-windows" 592 | date = "2015-06-05" 593 | hash = "cfc18f3d5306df208461459a8e667d89ce44ed77" 594 | score = 50 595 | strings: 596 | // Specific strings (may change) 597 | $x0 = "s4u.exe Domain\\Username [Extra SID]" fullword ascii 598 | $x1 = "\\Release\\s4u.pdb" ascii 599 | 600 | // Less specific strings 601 | $s0 = "CreateProcessAsUser failed (error %u)." fullword ascii 602 | $s1 = "GetTokenInformation failed (error: %u)." fullword ascii 603 | $s2 = "LsaLogonUser failed (error 0x%x)." fullword ascii 604 | $s3 = "LsaLogonUser: OK, LogonId: 0x%x-0x%x" fullword ascii 605 | $s4 = "LookupPrivilegeValue failed (error: %u)." fullword ascii 606 | $s5 = "The token does not have the specified privilege (%S)." fullword ascii 607 | $s6 = "Unable to parse command line." fullword ascii 608 | $s7 = "Unable to find logon SID." fullword ascii 609 | $s8 = "AdjustTokenPrivileges failed (error: %u)." fullword ascii 610 | $s9 = "AdjustTokenPrivileges (%S): OK" fullword ascii 611 | 612 | // Generic 613 | $g1 = "%systemroot%\\system32\\cmd.exe" wide 614 | $g2 = "SeTcbPrivilege" wide 615 | $g3 = "winsta0\\default" wide 616 | $g4 = ".rsrc" 617 | $g5 = "HeapAlloc" 618 | $g6 = "GetCurrentProcess" 619 | $g7 = "HeapFree" 620 | $g8 = "GetProcessHeap" 621 | $g9 = "ExpandEnvironmentStrings" 622 | $g10 = "ConvertStringSidToSid" 623 | $g11 = "LookupPrivilegeValue" 624 | $g12 = "AllocateLocallyUniqueId" 625 | $g13 = "ADVAPI32.dll" 626 | $g14 = "LsaLookupAuthenticationPackage" 627 | $g15 = "Secur32.dll" 628 | $g16 = "MSVCR120.dll" 629 | 630 | condition: 631 | uint16(0) == 0x5a4d and filesize < 60KB and ( 1 of ($x*) or all of ($s*) or all of ($g*) ) 632 | } 633 | 634 | rule baseline_gen_python_reverse_shell 635 | { 636 | meta: 637 | description = "Python Base64 encoded reverse shell" 638 | author = "John Lambert @JohnLaTwC" 639 | reference = "https://www.virustotal.com/en/file/9ec5102bcbabc45f2aa7775464f33019cfbe9d766b1332ee675957c923a17efd/analysis/" 640 | date = "2018-02-24" 641 | hash1 = "9ec5102bcbabc45f2aa7775464f33019cfbe9d766b1332ee675957c923a17efd" 642 | hash2 = "bfb5c622a3352bb71b86df81c45ccefaa68b9f7cc0a3577e8013aad951308f12" 643 | strings: 644 | $h1 = "import base64" fullword ascii 645 | 646 | $s1 = "b64decode" fullword ascii 647 | $s2 = "lambda" fullword ascii 648 | $s3 = "version_info" fullword ascii 649 | 650 | //Base64 encoded versions of these strings 651 | // socket.SOCK_STREAM 652 | $enc_x0 = /(AG8AYwBrAGUAdAAuAFMATwBDAEsAXwBTAFQAUgBFAEEATQ|b2NrZXQuU09DS19TVFJFQU|c29ja2V0LlNPQ0tfU1RSRUFN|cwBvAGMAawBlAHQALgBTAE8AQwBLAF8AUwBUAFIARQBBAE0A|MAbwBjAGsAZQB0AC4AUwBPAEMASwBfAFMAVABSAEUAQQBNA|NvY2tldC5TT0NLX1NUUkVBT)/ ascii 653 | 654 | //.connect(( 655 | $enc_x1 = /(4AYwBvAG4AbgBlAGMAdAAoACgA|5jb25uZWN0KC|AGMAbwBuAG4AZQBjAHQAKAAoA|LgBjAG8AbgBuAGUAYwB0ACgAKA|LmNvbm5lY3QoK|Y29ubmVjdCgo)/ 656 | 657 | //time.sleep 658 | $enc_x2 = /(AGkAbQBlAC4AcwBsAGUAZQBwA|aW1lLnNsZWVw|dABpAG0AZQAuAHMAbABlAGUAcA|dGltZS5zbGVlc|QAaQBtAGUALgBzAGwAZQBlAHAA|RpbWUuc2xlZX)/ 659 | 660 | //.recv 661 | $enc_x3 = /(4AcgBlAGMAdg|5yZWN2|AHIAZQBjAHYA|cmVjd|LgByAGUAYwB2A|LnJlY3)/ 662 | condition: 663 | uint32be(0) == 0x696d706f 664 | and $h1 at 0 665 | and filesize < 40KB 666 | and all of ($s*) 667 | and all of ($enc_x*) 668 | } 669 | 670 | 671 | rule baseline_PrikormkaModule 672 | { 673 | strings: 674 | // binary 675 | $str1 = {6D 70 2E 64 6C 6C 00 53 74 61 72 74 69 6E 67 00} 676 | $str2 = {68 6C 70 75 63 74 66 2E 64 6C 6C 00 43 79 63 6C 65} 677 | $str3 = {00 6B 6C 2E 64 6C 6C 00 53 74 61 72 74 69 6E 67 00} 678 | $str4 = {69 6F 6D 75 73 2E 64 6C 6C 00 53 74 61 72 74 69 6E 67} 679 | $str5 = {61 74 69 6D 6C 2E 64 6C 6C 00 4B 69 63 6B 49 6E 50 6F 69 6E 74} 680 | $str6 = {73 6E 6D 2E 64 6C 6C 00 47 65 74 52 65 61 64 79 46 6F 72 44 65 61 64} 681 | $str7 = {73 63 72 73 68 2E 64 6C 6C 00 47 65 74 52 65 61 64 79 46 6F 72 44 65 61 64} 682 | 683 | // encrypted 684 | $str8 = {50 52 55 5C 17 51 58 17 5E 4A} 685 | $str9 = {60 4A 55 55 4E 53 58 4B 17 52 57 17 5E 4A} 686 | $str10 = {55 52 5D 4E 5B 4A 5D 17 51 58 17 5E 4A} 687 | $str11 = {60 4A 55 55 4E 61 17 51 58 17 5E 4A} 688 | $str12 = {39 5D 17 1D 1C 0A 3C 57 59 3B 1C 1E 57 58 4C 54 0F} 689 | 690 | // mutex 691 | $str13 = "ZxWinDeffContex" ascii wide 692 | $str14 = "Paramore756Contex43" wide 693 | $str15 = "Zw_&one@ldrContext43" wide 694 | 695 | // other 696 | $str16 = "A95BL765MNG2GPRS" 697 | 698 | // dll names 699 | $str17 = "helpldr.dll" wide fullword 700 | $str18 = "swma.dll" wide fullword 701 | $str19 = "iomus.dll" wide fullword 702 | $str20 = "atiml.dll" wide fullword 703 | $str21 = "hlpuctf.dll" wide fullword 704 | $str22 = "hauthuid.dll" ascii wide fullword 705 | 706 | // rbcon 707 | $str23 = "[roboconid][%s]" ascii fullword 708 | $str24 = "[objectset][%s]" ascii fullword 709 | $str25 = "rbcon.ini" wide fullword 710 | 711 | // files and logs 712 | $str26 = "%s%02d.%02d.%02d_%02d.%02d.%02d.skw" ascii fullword 713 | $str27 = "%02d.%02d.%02d_%02d.%02d.%02d.%02d.rem" wide fullword 714 | 715 | // pdb strings 716 | $str28 = ":\\!PROJECTS!\\Mina\\2015\\" ascii 717 | $str29 = "\\PZZ\\RMO\\" ascii 718 | $str30 = ":\\work\\PZZ" ascii 719 | $str31 = "C:\\Users\\mlk\\" ascii 720 | $str32 = ":\\W o r k S p a c e\\" ascii 721 | $str33 = "D:\\My\\Projects_All\\2015\\" ascii 722 | $str34 = "\\TOOLS PZZ\\Bezzahod\\" ascii 723 | 724 | condition: 725 | uint16(0) == 0x5a4d and (any of ($str*)) 726 | } 727 | 728 | rule baseline_PrikormkaEarlyVersion 729 | { 730 | strings: 731 | $str1 = "IntelRestore" ascii fullword 732 | $str2 = "Resent" wide fullword 733 | $str3 = "ocp8.1" wide fullword 734 | $str4 = "rsfvxd.dat" ascii fullword 735 | $str5 = "tsb386.dat" ascii fullword 736 | $str6 = "frmmlg.dat" ascii fullword 737 | $str7 = "smdhost.dll" ascii fullword 738 | $str8 = "KDLLCFX" wide fullword 739 | $str9 = "KDLLRUNDRV" wide fullword 740 | condition: 741 | uint16(0) == 0x5a4d and (2 of ($str*)) 742 | } 743 | 744 | 745 | rule baseline_SUSP_MalDoc_ExcelMacro { 746 | meta: 747 | description = "Detects malicious Excel macro Artifacts" 748 | author = "James Quinn" 749 | date = "2020-11-03" 750 | reference = "YARA Exchange - Undisclosed Macro Builder" 751 | strings: 752 | $artifact1 = {5c 00 ?? 00 ?? 00 ?? 00 ?? 00 ?? 00 ?? 00 ?? 00 2e 00 ?? 00 ?? 00} 753 | $url1 = "http://" wide 754 | $url2 = "https://" wide 755 | $import1 = "URLDownloadToFileA" wide ascii 756 | $macro = "xl/macrosheets/" 757 | condition: 758 | uint16(0) == 0x4b50 and 759 | filesize < 2000KB and 760 | $artifact1 and $macro and $import1 and 1 of ($url*) 761 | } 762 | 763 | rule baseline_plist_macos { 764 | meta: 765 | hashes = "76eb97aba93979be06dbf0a872518f9514d0bb20b680c887d6fd5cc79dce3681" 766 | strings: 767 | $sr1 = "PropertyList-1.0.dtd" fullword 768 | $sr2 = "[\/|\w]{0,20}\+[\/|\+|=|\w]{59,80}\<\/string\>/ 797 | //see 0541fc6a11f4226d52ae3d4158deb8f50ed61b25bb5f889d446102e1ee57b76d 798 | $v1 = "curl " fullword 799 | // see 9a3fd0d2b0bca7d2f7e3c70cb15a7005a1afa1ce78371fd3fa9c526a288b64ce 800 | $v2 = "PAYLOAD_DATA" 801 | $v3 = "base64" 802 | // see 9a3fd0d2b0bca7d2f7e3c70cb15a7005a1afa1ce78371fd3fa9c526a288b64ce 803 | //PAYLOAD_BASE64 804 | $vb640 = /(AAQQBZAEwATwBBAEQAXwBCAEEAUwBFADYANA|AEEAWQBMAE8AQQBEAF8AQgBBAFMARQA2ADQA|BBWUxPQURfQkFTRTY0|QVlMT0FEX0JBU0U2N|UABBAFkATABPAEEARABfAEIAQQBTAEUANgA0A|UEFZTE9BRF9CQVNFNj)/ 805 | //subprocess 806 | $vb641 = /(AHUAYgBwAHIAbwBjAGUAcwBzA|c3VicHJvY2Vzc|cwB1AGIAcAByAG8AYwBlAHMAcw|dWJwcm9jZXNz|MAdQBiAHAAcgBvAGMAZQBzAHMA|N1YnByb2Nlc3)/ 807 | // #!/usr 808 | $vb642 = "IyEvdXNy" 809 | // # -*- 810 | $vb643 = "IyAtKi0" 811 | //add_header 812 | $vb644 = /(AGQAZABfAGgAZQBhAGQAZQByA|EAZABkAF8AaABlAGEAZABlAHIA|FkZF9oZWFkZX|YQBkAGQAXwBoAGUAYQBkAGUAcg|YWRkX2hlYWRlc|ZGRfaGVhZGVy)/ 813 | 814 | $fp1 = " do echo" // Shells.plist 815 | $fp2 = "com.cisco.base64" // Webex 816 | $fp3 = "video/mp4;base64" 817 | $fp4 = "Content-Length" 818 | condition: 819 | baseline_plist_macos and ( 1 of ($v*) or all of ($p*) ) 820 | and not 1 of ($fp*) 821 | } 822 | 823 | 824 | rule baseline_APT_HackTool_MSIL_GPOHUNT_1 825 | { 826 | meta: 827 | description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the 'gpohunt' project." 828 | md5 = "dd8805d0e470e59b829d98397507d8c2" 829 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 830 | author = "FireEye" 831 | strings: 832 | $typelibguid0 = "751a9270-2de0-4c81-9e29-872cd6378303" ascii nocase wide 833 | condition: 834 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and any of them 835 | } 836 | 837 | rule baseline_APT_HackTool_MSIL_JUSTASK_1 838 | { 839 | meta: 840 | description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the 'justask' project." 841 | md5 = "dd8805d0e470e59b829d98397507d8c2" 842 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 843 | author = "FireEye" 844 | strings: 845 | $typelibguid0 = "aa59be52-7845-4fed-9ea5-1ea49085d67a" ascii nocase wide 846 | condition: 847 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and any of them 848 | } 849 | 850 | rule baseline_APT_Trojan_Win_REDFLARE_4 851 | { 852 | meta: 853 | date = "2020-12-01" 854 | modified = "2020-12-01" 855 | md5 = "a8b5dcfea5e87bf0e95176daa243943d, 9dcb6424662941d746576e62712220aa" 856 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 857 | author = "FireEye" 858 | strings: 859 | $s1 = "LogonUserW" fullword 860 | $s2 = "ImpersonateLoggedOnUser" fullword 861 | $s3 = "runCommand" fullword 862 | $user_logon = { 22 02 00 00 [1-10] 02 02 00 00 [0-4] E8 [4-40] ( 09 00 00 00 [1-10] 03 00 00 00 | 6A 03 6A 09 ) [4-30] FF 15 [4] 85 C0 7? } 863 | condition: 864 | (uint16(0) == 0x5A4D) and (uint32(uint32(0x3C)) == 0x00004550) and all of them 865 | } 866 | 867 | rule baseline_APT_HackTool_MSIL_TITOSPECIAL_1 868 | { 869 | meta: 870 | date = "2020-11-25" 871 | modified = "2020-11-25" 872 | md5 = "4bf96a7040a683bd34c618431e571e26" 873 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 874 | author = "FireEye" 875 | strings: 876 | $ind_dump = { 1F 10 16 28 [2] 00 0A 6F [2] 00 0A [50-200] 18 19 18 73 [2] 00 0A 13 [1-4] 06 07 11 ?? 6F [2] 00 0A 18 7E [2] 00 0A 7E [2] 00 0A 7E [2] 00 0A 28 [2] 00 06 } 877 | $ind_s1 = "NtReadVirtualMemory" fullword wide 878 | $ind_s2 = "WriteProcessMemory" fullword 879 | $shellcode_x64 = { 4C 8B D1 B8 3C 00 00 00 0F 05 C3 } 880 | $shellcode_x86 = { B8 3C 00 00 00 33 C9 8D 54 24 04 64 FF 15 C0 00 00 00 83 C4 04 C2 14 00 } 881 | condition: 882 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and all of ($ind*) and any of ($shellcode* ) 883 | } 884 | 885 | rule baseline_Dropper_LNK_LNKSmasher_1 886 | { 887 | meta: 888 | description = "The LNKSmasher project contains a prebuilt LNK file that has pieces added based on various configuration items. Because of this, several artifacts are present in every single LNK file generated by LNKSmasher, including the Drive Serial #, the File Droid GUID, and the GUID CLSID." 889 | md5 = "0a86d64c3b25aa45428e94b6e0be3e08" 890 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 891 | author = "FireEye" 892 | strings: 893 | $drive_serial = { 12 F7 26 BE } 894 | $file_droid_guid = { BC 96 28 4F 0A 46 54 42 81 B8 9F 48 64 D7 E9 A5 } 895 | $guid_clsid = { E0 4F D0 20 EA 3A 69 10 A2 D8 08 00 2B 30 30 9D } 896 | $header = { 4C 00 00 00 01 14 02 } 897 | condition: 898 | $header at 0 and all of them 899 | } 900 | 901 | rule baseline_CredTheft_MSIL_TitoSpecial_1 902 | { 903 | meta: 904 | description = "This rule looks for .NET PE files that have the strings of various method names in the TitoSpecial code." 905 | md5 = "4bf96a7040a683bd34c618431e571e26" 906 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 907 | author = "FireEye" 908 | strings: 909 | $str1 = "Minidump" ascii wide 910 | $str2 = "dumpType" ascii wide 911 | $str3 = "WriteProcessMemory" ascii wide 912 | $str4 = "bInheritHandle" ascii wide 913 | $str5 = "GetProcessById" ascii wide 914 | $str6 = "SafeHandle" ascii wide 915 | $str7 = "BeginInvoke" ascii wide 916 | $str8 = "EndInvoke" ascii wide 917 | $str9 = "ConsoleApplication1" ascii wide 918 | $str10 = "getOSInfo" ascii wide 919 | $str11 = "OpenProcess" ascii wide 920 | $str12 = "LoadLibrary" ascii wide 921 | $str13 = "GetProcAddress" ascii wide 922 | condition: 923 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and all of ($str*) 924 | } 925 | 926 | rule baseline_Builder_MSIL_G2JS_1 927 | { 928 | meta: 929 | description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the Gadget2JScript project." 930 | md5 = "fa255fdc88ab656ad9bc383f9b322a76" 931 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 932 | author = "FireEye" 933 | strings: 934 | $typelibguid1 = "AF9C62A1-F8D2-4BE0-B019-0A7873E81EA9" ascii nocase wide 935 | condition: 936 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and $typelibguid1 937 | } 938 | 939 | rule baseline_APT_Loader_Win32_DShell_2 940 | { 941 | meta: 942 | date = "2020-11-27" 943 | modified = "2020-11-27" 944 | md5 = "590d98bb74879b52b97d8a158af912af" 945 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 946 | author = "FireEye" 947 | strings: 948 | $sb1 = { 6A 40 68 00 30 00 00 [4-32] E8 [4-8] 50 [0-16] E8 [4-150] 6A FF [1-32] 6A 00 6A 00 5? 6A 00 6A 00 [0-32] E8 [4] 50 } 949 | $ss1 = "\x00CreateThread\x00" 950 | $ss2 = "base64.d" fullword 951 | $ss3 = "core.sys.windows" fullword 952 | $ss4 = "C:\\Users\\config.ini" fullword 953 | $ss5 = "Invalid config file" fullword 954 | condition: 955 | (uint16(0) == 0x5A4D) and (uint32(uint32(0x3C)) == 0x00004550) and (uint16(uint32(0x3C)+0x18) == 0x010B) and all of them 956 | } 957 | 958 | 959 | rule baseline_CredTheft_Win_EXCAVATOR_2 960 | { 961 | meta: 962 | description = "This rule looks for the binary signature of the routine that calls PssFreeSnapshot found in the Excavator-Reflector DLL." 963 | md5 = "6a9a114928554c26675884eeb40cc01b" 964 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 965 | author = "FireEye" 966 | strings: 967 | $bytes1 = { 4C 89 74 24 20 55 48 8D AC 24 60 FF FF FF 48 81 EC A0 01 00 00 48 8B 05 4C 4A 01 00 48 33 C4 48 89 85 90 00 00 00 BA 50 00 00 00 C7 05 CB 65 01 00 43 00 3A 00 66 89 15 EC 65 01 00 4C 8D 44 24 68 48 8D 15 D8 68 01 00 C7 05 B2 65 01 00 5C 00 57 00 33 C9 C7 05 AA 65 01 00 69 00 6E 00 C7 05 A4 65 01 00 64 00 6F 00 C7 05 9E 65 01 00 77 00 73 00 C7 05 98 65 01 00 5C 00 4D 00 C7 05 92 65 01 00 45 00 4D 00 C7 05 8C 65 01 00 4F 00 52 00 C7 05 86 65 01 00 59 00 2E 00 C7 05 80 65 01 00 44 00 4D 00 C7 05 72 68 01 00 53 00 65 00 C7 05 6C 68 01 00 44 00 65 00 C7 05 66 68 01 00 42 00 75 00 C7 05 60 68 01 00 47 00 50 00 C7 05 5A 68 01 00 72 00 69 00 C7 05 54 68 01 00 56 00 69 00 C7 05 4E 68 01 00 4C 00 45 00 C7 05 48 68 01 00 67 00 65 00 C7 05 12 67 01 00 6C 73 61 73 C7 05 0C 67 01 00 73 2E 65 78 C6 05 09 67 01 00 65 FF 15 63 B9 00 00 45 33 F6 85 C0 74 66 48 8B 44 24 68 48 89 44 24 74 C7 44 24 70 01 00 00 00 C7 44 24 7C 02 00 00 00 FF 15 A4 B9 00 00 48 8B C8 4C 8D 44 24 48 41 8D 56 20 FF 15 1A B9 00 00 85 C0 74 30 48 8B 4C 24 48 4C 8D 44 24 70 4C 89 74 24 28 45 33 C9 33 D2 4C 89 74 24 20 FF 15 EF B8 00 00 FF 15 11 B9 00 00 48 8B 4C 24 48 FF 15 16 B9 00 00 48 89 9C 24 B0 01 00 00 48 8D 0D BF 2E 01 00 48 89 B4 24 B8 01 00 00 4C 89 74 24 40 FF 15 1C B9 00 00 48 85 C0 0F 84 B0 00 00 00 48 8D 15 AC 2E 01 00 48 8B C8 FF 15 1B B9 00 00 48 8B D8 48 85 C0 0F 84 94 00 00 00 33 D2 48 8D 4D 80 41 B8 04 01 00 00 E8 06 15 00 00 48 8B 4C 24 40 48 8D 44 24 40 45 33 C9 48 89 44 24 20 45 33 C0 BA 00 00 00 02 FF D3 85 C0 75 63 66 0F 1F 44 00 00 48 8B 4C 24 40 4C 8D 45 80 41 B9 04 01 00 00 33 D2 FF 15 89 B8 00 00 48 8D 15 F2 65 01 00 48 8D 4D 80 E8 49 0F 00 00 48 85 C0 75 38 33 D2 48 8D 4D 80 41 B8 04 01 00 00 E8 A3 14 00 00 48 8B 4C 24 40 48 8D 44 24 40 45 33 C9 48 89 44 24 20 45 33 C0 BA 00 00 00 02 FF D3 85 C0 74 A3 33 C0 E9 F5 00 00 00 48 8B 5C 24 40 48 8B CB FF 15 5E B8 00 00 8B F0 48 85 DB 74 E4 85 C0 74 E0 4C 8D 4C 24 50 48 89 BC 24 C0 01 00 00 BA FD 03 00 AC 41 B8 1F 00 10 00 48 8B CB FF 15 12 B8 00 00 85 C0 0F 85 A0 00 00 00 48 8D 05 43 FD FF FF 4C 89 74 24 30 C7 44 24 28 80 00 00 00 48 8D 0D 3F 63 01 00 45 33 C9 48 89 44 24 58 45 33 C0 C7 44 24 20 01 00 00 00 BA 00 00 00 10 4C 89 74 24 60 FF 15 E4 B7 00 00 48 8B F8 48 83 F8 FF 74 59 48 8B 4C 24 50 48 8D 44 24 58 48 89 44 24 30 41 B9 02 00 00 00 4C 89 74 24 28 4C 8B C7 8B D6 4C 89 74 24 20 FF 15 B1 B9 00 00 48 8B CB FF 15 78 B7 00 00 48 8B CF FF 15 6F B7 00 00 FF 15 B1 B7 00 00 48 8B 54 24 50 48 8B C8 FF 15 53 B7 00 00 33 C9 FF 15 63 B7 00 00 CC 48 8B CB FF 15 49 B7 00 00 48 8B BC 24 C0 01 00 00 33 C0 48 8B B4 24 B8 01 00 00 48 8B 9C 24 B0 01 00 00 48 8B 8D 90 00 00 00 48 33 CC E8 28 00 00 00 4C 8B B4 24 C8 01 00 00 48 81 C4 A0 01 00 00 5D C3 } 968 | $bytes2 = { 4C 89 74 24 20 55 48 8D AC 24 60 FF FF FF 48 81 EC A? ?1 ?? ?? 48 ?? ?? ?? ?? ?? ?? 48 33 C4 48 89 85 9? ?? ?? ?0 BA ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? 66 89 ?? ?? ?? ?? ?? 4C 8D 44 24 68 48 ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? 33 C9 C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C6 ?? ?? ?? ?? ?? ?? FF ?? ?? ?? ?? ?? 45 33 F6 85 C0 74 ?? 48 8B 44 24 68 48 89 44 24 74 C7 44 24 7? ?1 ?? ?? ?? C7 44 24 7C 02 ?? ?? ?? FF ?? ?? ?? ?? ?? 48 8B C8 4C 8D 44 24 48 41 8D 56 20 FF ?? ?? ?? ?? ?? 85 C0 74 ?? 48 8B 4C 24 48 4C 8D 44 24 70 4C 89 74 24 28 45 33 C9 33 D2 4C 89 74 24 20 FF ?? ?? ?? ?? ?? FF ?? ?? ?? ?? ?? 48 8B 4C 24 48 FF ?? ?? ?? ?? ?? 48 89 9C 24 B? ?1 ?? ?? 48 ?? ?? ?? ?? ?? ?? 48 89 B4 24 B8 01 ?? ?? 4C 89 74 24 40 FF ?? ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? 48 8B C8 FF ?? ?? ?? ?? ?? 48 8B D8 48 85 C0 0F 84 ?? ?? ?? ?? 33 D2 48 8D 4D 80 41 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 4C 24 40 48 8D 44 24 40 45 33 C9 48 89 44 24 20 45 33 C0 BA ?? ?? ?? ?? FF D3 85 C0 75 ?? 66 0F 1F 44 ?? ?? 48 8B 4C 24 40 4C 8D 45 80 41 ?? ?? ?? ?? ?? 33 D2 FF ?? ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? 48 8D 4D 80 E8 ?? ?? ?? ?? 48 85 C0 75 ?? 33 D2 48 8D 4D 80 41 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 4C 24 40 48 8D 44 24 40 45 33 C9 48 89 44 24 20 45 33 C0 BA ?? ?? ?? ?? FF D3 85 C0 74 ?? 33 C0 E9 ?? ?? ?? ?? 48 8B 5C 24 40 48 8B CB FF ?? ?? ?? ?? ?? 8B F0 48 85 DB 74 ?? 85 C0 74 ?? 4C 8D 4C 24 50 48 89 BC 24 C? ?1 ?? ?? BA ?? ?? ?? ?? 41 ?? ?? ?? ?? ?? 48 8B CB FF ?? ?? ?? ?? ?? 85 C0 0F 85 ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? 4C 89 74 24 30 C7 ?? ?? ?? ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? 45 33 C9 48 89 44 24 58 45 33 C0 C7 44 24 2? ?1 ?? ?? ?? BA ?? ?? ?? ?? 4C 89 74 24 60 FF ?? ?? ?? ?? ?? 48 8B F8 48 83 F8 FF 74 ?? 48 8B 4C 24 50 48 8D 44 24 58 48 89 44 24 30 41 B9 02 ?? ?? ?? 4C 89 74 24 28 4C 8B C7 8B D6 4C 89 74 24 20 FF ?? ?? ?? ?? ?? 48 8B CB FF ?? ?? ?? ?? ?? 48 8B CF FF ?? ?? ?? ?? ?? FF ?? ?? ?? ?? ?? 48 8B 54 24 50 48 8B C8 FF ?? ?? ?? ?? ?? 33 C9 FF ?? ?? ?? ?? ?? 48 8B CB FF ?? ?? ?? ?? ?? 48 8B BC 24 C? ?1 ?? ?? 33 C0 48 8B B4 24 B8 01 ?? ?? 48 8B 9C 24 B? ?1 ?? ?? 48 8B 8D 9? ?? ?? ?0 48 33 CC E8 ?? ?? ?? ?? 4C 8B B4 24 C8 01 ?? ?? 48 81 C4 A? ?1 ?? ?? 5D C3 } 969 | $bytes3 = { 4C 89 74 24 20 55 48 8D AC 24 60 FF FF FF 48 81 EC A? ?1 ?? ?? 48 ?? ?? ?? ?? ?? ?? 48 33 C4 48 89 85 9? ?? ?? ?0 BA ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? 66 89 ?? ?? ?? ?? ?? 4C 8D 44 24 68 48 ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? 33 C9 C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C7 ?? ?? ?? ?? ?? ?? ?? ?? ?? C6 ?? ?? ?? ?? ?? ?? FF ?? ?? ?? ?? ?? 45 33 F6 85 C0 74 ?? 48 8B 44 24 68 48 89 44 24 74 C7 44 24 7? ?1 ?? ?? ?? C7 44 24 7C 02 ?? ?? ?? FF ?? ?? ?? ?? ?? 48 8B C8 4C 8D 44 24 48 41 8D 56 20 FF ?? ?? ?? ?? ?? 85 C0 74 ?? 48 8B 4C 24 48 4C 8D 44 24 70 4C 89 74 24 28 45 33 C9 33 D2 4C 89 74 24 20 FF ?? ?? ?? ?? ?? FF ?? ?? ?? ?? ?? 48 8B 4C 24 48 FF ?? ?? ?? ?? ?? 48 89 9C 24 B? ?1 ?? ?? 48 ?? ?? ?? ?? ?? ?? 48 89 B4 24 B8 01 ?? ?? 4C 89 74 24 40 FF ?? ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? 48 8B C8 FF ?? ?? ?? ?? ?? 48 8B D8 48 85 C0 0F 84 ?? ?? ?? ?? 33 D2 48 8D 4D 80 41 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 4C 24 40 48 8D 44 24 40 45 33 C9 48 89 44 24 20 45 33 C0 BA ?? ?? ?? ?? FF D3 85 C0 75 ?? 66 0F 1F 44 ?? ?? 48 8B 4C 24 40 4C 8D 45 80 41 ?? ?? ?? ?? ?? 33 D2 FF ?? ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? 48 8D 4D 80 E8 ?? ?? ?? ?? 48 85 C0 75 ?? 33 D2 48 8D 4D 80 41 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 4C 24 40 48 8D 44 24 40 45 33 C9 48 89 44 24 20 45 33 C0 BA ?? ?? ?? ?? FF D3 85 C0 74 ?? 33 C0 E9 ?? ?? ?? ?? 48 8B 5C 24 40 48 8B CB FF ?? ?? ?? ?? ?? 8B F0 48 85 DB 74 ?? 85 C0 74 ?? 4C 8D 4C 24 50 48 89 BC 24 C? ?1 ?? ?? BA ?? ?? ?? ?? 41 ?? ?? ?? ?? ?? 48 8B CB FF ?? ?? ?? ?? ?? 85 C0 0F 85 ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? 4C 89 74 24 30 C7 ?? ?? ?? ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? 45 33 C9 48 89 44 24 58 45 33 C0 C7 44 24 2? ?1 ?? ?? ?? BA ?? ?? ?? ?? 4C 89 74 24 60 FF ?? ?? ?? ?? ?? 48 8B F8 48 83 F8 FF 74 ?? 48 8B 4C 24 50 48 8D 44 24 58 48 89 44 24 30 41 B9 02 ?? ?? ?? 4C 89 74 24 28 4C 8B C7 8B D6 4C 89 74 24 20 FF ?? ?? ?? ?? ?? 48 8B CB FF ?? ?? ?? ?? ?? 48 8B CF FF ?? ?? ?? ?? ?? FF ?? ?? ?? ?? ?? 48 8B 54 24 50 48 8B C8 FF ?? ?? ?? ?? ?? 33 C9 FF ?? ?? ?? ?? ?? 48 8B CB FF ?? ?? ?? ?? ?? 48 8B BC 24 C? ?1 ?? ?? 33 C0 48 8B B4 24 B8 01 ?? ?? 48 8B 9C 24 B? ?1 ?? ?? 48 8B 8D 9? ?? ?? ?0 48 33 CC E8 ?? ?? ?? ?? 4C 8B B4 24 C8 01 ?? ?? 48 81 C4 A? ?1 ?? ?? 5D C3 } 970 | $bytes4 = { 4C 89 74 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC A0 01 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? BA 50 00 00 00 C7 05 ?? ?? ?? ?? 43 00 3A 00 66 89 15 ?? ?? 01 00 4C 8D 44 24 ?? 48 8D 15 ?? ?? ?? ?? C7 05 ?? ?? ?? ?? 5C 00 57 00 33 C9 C7 05 ?? ?? ?? ?? 69 00 6E 00 C7 05 ?? ?? ?? ?? 64 00 6F 00 C7 05 ?? ?? ?? ?? 77 00 73 00 C7 05 ?? ?? ?? ?? 5C 00 4D 00 C7 05 ?? ?? ?? ?? 45 00 4D 00 C7 05 ?? ?? ?? ?? 4F 00 52 00 C7 05 ?? ?? ?? ?? 59 00 2E 00 C7 05 ?? ?? ?? ?? 44 00 4D 00 C7 05 ?? ?? ?? ?? 53 00 65 00 C7 05 ?? ?? ?? ?? 44 00 65 00 C7 05 ?? ?? ?? ?? 42 00 75 00 C7 05 ?? ?? ?? ?? 47 00 50 00 C7 05 ?? ?? ?? ?? 72 00 69 00 C7 05 ?? ?? ?? ?? 56 00 69 00 C7 05 ?? ?? ?? ?? 4C 00 45 00 C7 05 ?? ?? ?? ?? 67 00 65 00 C7 05 ?? ?? ?? ?? 6C 73 61 73 C7 05 ?? ?? ?? ?? 73 2E 65 78 C6 05 ?? ?? ?? ?? 65 FF 15 ?? ?? ?? ?? 45 33 F6 85 C0 74 ?? 48 8B 44 24 ?? 48 89 44 24 ?? C7 44 24 ?? 01 00 00 00 C7 44 24 ?? 02 00 00 00 FF 15 ?? ?? ?? ?? 48 8B C8 4C 8D 44 24 ?? 41 8D 56 ?? FF 15 ?? ?? ?? ?? 85 C0 74 ?? 48 8B 4C 24 ?? 4C 8D 44 24 ?? 4C 89 74 24 ?? 45 33 C9 33 D2 4C 89 74 24 ?? FF 15 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8B 4C 24 ?? FF 15 ?? ?? ?? ?? 48 89 9C 24 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? 48 89 B4 24 ?? ?? ?? ?? 4C 89 74 24 ?? FF 15 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8B C8 FF 15 ?? ?? ?? ?? 48 8B D8 48 85 C0 0F 84 ?? ?? ?? ?? 33 D2 48 8D 4D ?? 41 B8 04 01 00 00 E8 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 8D 44 24 ?? 45 33 C9 48 89 44 24 ?? 45 33 C0 BA 00 00 00 02 FF D3 85 C0 75 ?? 66 0F 1F 44 00 ?? 48 8B 4C 24 ?? 4C 8D 45 ?? 41 B9 04 01 00 00 33 D2 FF 15 ?? ?? ?? ?? 48 8D 15 ?? ?? ?? ?? 48 8D 4D ?? E8 ?? ?? ?? ?? 48 85 C0 75 ?? 33 D2 48 8D 4D ?? 41 B8 04 01 00 00 E8 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 8D 44 24 ?? 45 33 C9 48 89 44 24 ?? 45 33 C0 BA 00 00 00 02 FF D3 85 C0 74 ?? 33 C0 E9 ?? ?? ?? ?? 48 8B 5C 24 ?? 48 8B CB FF 15 ?? ?? ?? ?? 8B F0 48 85 DB 74 ?? 85 C0 74 ?? 4C 8D 4C 24 ?? 48 89 BC 24 ?? ?? ?? ?? BA FD 03 00 AC 41 B8 1F 00 10 00 48 8B CB FF 15 ?? ?? ?? ?? 85 C0 0F 85 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 4C 89 74 24 ?? C7 44 24 ?? 80 00 00 00 48 8D 0D ?? ?? ?? ?? 45 33 C9 48 89 44 24 ?? 45 33 C0 C7 44 24 ?? 01 00 00 00 BA 00 00 00 10 4C 89 74 24 ?? FF 15 ?? ?? ?? ?? 48 8B F8 48 83 F8 FF 74 ?? 48 8B 4C 24 ?? 48 8D 44 24 ?? 48 89 44 24 ?? 41 B9 02 00 00 00 4C 89 74 24 ?? 4C 8B C7 8B D6 4C 89 74 24 ?? FF 15 ?? ?? ?? ?? 48 8B CB FF 15 ?? ?? ?? ?? 48 8B CF FF 15 ?? ?? ?? ?? FF 15 ?? ?? ?? ?? 48 8B 54 24 ?? 48 8B C8 FF 15 ?? ?? ?? ?? 33 C9 FF 15 ?? ?? ?? ?? CC 48 8B CB FF 15 ?? ?? ?? ?? 48 8B BC 24 ?? ?? ?? ?? 33 C0 48 8B B4 24 ?? ?? ?? ?? 48 8B 9C 24 ?? ?? ?? ?? 48 8B 8D ?? ?? ?? ?? 48 33 CC E8 ?? ?? ?? ?? 4C 8B B4 24 ?? ?? ?? ?? 48 81 C4 A0 01 00 00 5D C3 } 971 | condition: 972 | uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550 and any of ($bytes*) 973 | } 974 | 975 | rule baseline_Builder_MSIL_SharpGenerator_1 976 | { 977 | meta: 978 | description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the 'SharpGenerator' project." 979 | md5 = "dd8805d0e470e59b829d98397507d8c2" 980 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 981 | author = "FireEye" 982 | strings: 983 | $typelibguid0 = "3f450977-d796-4016-bb78-c9e91c6a0f08" ascii nocase wide 984 | condition: 985 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and any of them 986 | } 987 | 988 | rule baseline_APT_Loader_Win_PGF_2 989 | { 990 | meta: 991 | description = "PE rich header matches PGF backdoor" 992 | md5 = "226b1ac427eb5a4dc2a00cc72c163214" 993 | md5_2 = "2398ed2d5b830d226af26dedaf30f64a" 994 | md5_3 = "24a7c99da9eef1c58f09cf09b9744d7b" 995 | md5_4 = "aeb0e1d0e71ce2a08db9b1e5fb98e0aa" 996 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 997 | author = "FireEye" 998 | strings: 999 | $rich1 = { A8 B7 17 3A EC D6 79 69 EC D6 79 69 EC D6 79 69 2F D9 24 69 E8 D6 79 69 E5 AE EC 69 EA D6 79 69 EC D6 78 69 A8 D6 79 69 E5 AE EA 69 EF D6 79 69 E5 AE FA 69 D0 D6 79 69 E5 AE EB 69 ED D6 79 69 E5 AE FD 69 E2 D6 79 69 CB 10 07 69 ED D6 79 69 E5 AE E8 69 ED D6 79 69 } 1000 | $rich2 = { C1 CF 75 A4 85 AE 1B F7 85 AE 1B F7 85 AE 1B F7 8C D6 88 F7 83 AE 1B F7 0D C9 1A F6 87 AE 1B F7 0D C9 1E F6 8F AE 1B F7 0D C9 1F F6 8F AE 1B F7 0D C9 18 F6 84 AE 1B F7 DE C6 1A F6 86 AE 1B F7 85 AE 1A F7 BF AE 1B F7 84 C3 12 F6 81 AE 1B F7 84 C3 E4 F7 84 AE 1B F7 84 C3 19 F6 84 AE 1B F7 } 1001 | $rich3 = { D6 60 82 B8 92 01 EC EB 92 01 EC EB 92 01 EC EB 9B 79 7F EB 94 01 EC EB 1A 66 ED EA 90 01 EC EB 1A 66 E9 EA 98 01 EC EB 1A 66 E8 EA 9A 01 EC EB 1A 66 EF EA 90 01 EC EB C9 69 ED EA 91 01 EC EB 92 01 ED EB AF 01 EC EB 93 6C E5 EA 96 01 EC EB 93 6C 13 EB 93 01 EC EB 93 6C EE EA 93 01 EC EB } 1002 | $rich4 = { 41 36 64 33 05 57 0A 60 05 57 0A 60 05 57 0A 60 73 CA 71 60 01 57 0A 60 0C 2F 9F 60 04 57 0A 60 0C 2F 89 60 3D 57 0A 60 0C 2F 8E 60 0A 57 0A 60 05 57 0B 60 4A 57 0A 60 0C 2F 99 60 06 57 0A 60 73 CA 67 60 04 57 0A 60 0C 2F 98 60 04 57 0A 60 0C 2F 80 60 04 57 0A 60 22 91 74 60 04 57 0A 60 0C 2F 9B 60 04 57 0A 60 } 1003 | condition: 1004 | (uint16(0) == 0x5A4D) and (uint32(uint32(0x3C)) == 0x00004550) and filesize < 15MB and (($rich1 at 128) or ($rich2 at 128) or ($rich3 at 128) or ($rich4 at 128)) 1005 | } 1006 | 1007 | rule baseline_Trojan_Win_Generic_101 1008 | { 1009 | meta: 1010 | description = "Detects FireEye Windows trojan" 1011 | date = "2020-11-25" 1012 | modified = "2020-11-25" 1013 | md5 = "2e67c62bd0307c04af469ee8dcb220f2" 1014 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 1015 | author = "FireEye" 1016 | strings: 1017 | $s0 = { 2A [1-16] 17 [1-16] 02 04 00 00 [1-16] FF 15 } 1018 | $s1 = { 81 7? [1-3] 02 04 00 00 7? [1-3] 83 7? [1-3] 17 7? [1-3] 83 7? [1-3] 2A 7? } 1019 | $s2 = { FF 15 [4-16] FF D? [1-16] 3D [1-24] 89 [1-8] E8 [4-16] 89 [1-8] F3 A4 [1-24] E8 } 1020 | $si1 = "PeekMessageA" fullword 1021 | $si2 = "PostThreadMessageA" fullword 1022 | condition: 1023 | (uint16(0) == 0x5A4D) and (uint32(uint32(0x3C)) == 0x00004550) and @s0[1] < @s1[1] and @s1[1] < @s2[1] and all of them 1024 | } 1025 | 1026 | rule baseline_Loader_MSIL_CSharpSectionInjection_1 1027 | { 1028 | meta: 1029 | description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the 'C_Sharp_SectionInjection' project." 1030 | md5 = "dd8805d0e470e59b829d98397507d8c2" 1031 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 1032 | author = "FireEye" 1033 | strings: 1034 | $typelibguid0 = "d77135da-0496-4b5c-9afe-e1590a4c136a" ascii nocase wide 1035 | condition: 1036 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and any of them 1037 | } 1038 | 1039 | rule baseline_APT_HackTool_MSIL_SHARPWEBCRAWLER_1 1040 | { 1041 | meta: 1042 | description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the 'sharpwebcrawler' project." 1043 | md5 = "dd8805d0e470e59b829d98397507d8c2" 1044 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 1045 | author = "FireEye" 1046 | strings: 1047 | $typelibguid0 = "cf27abf4-ef35-46cd-8d0c-756630c686f1" ascii nocase wide 1048 | condition: 1049 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and any of them 1050 | } 1051 | 1052 | rule baseline_Trojan_Win64_Generic_22 1053 | { 1054 | meta: 1055 | description = "Detects FireEye's Windows Trojan" 1056 | date = "2020-11-26" 1057 | modified = "2020-11-26" 1058 | md5 = "f7d9961463b5110a3d70ee2e97842ed3" 1059 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 1060 | author = "FireEye" 1061 | strings: 1062 | $api1 = "VirtualAllocEx" fullword 1063 | $api2 = "UpdateProcThreadAttribute" fullword 1064 | $api3 = "DuplicateTokenEx" fullword 1065 | $api4 = "CreateProcessAsUserA" fullword 1066 | $inject = { C7 44 24 20 40 00 00 00 33 D2 41 B9 00 30 00 00 41 B8 [4] 48 8B CB FF 15 [4] 48 8B F0 48 85 C0 74 ?? 4C 89 74 24 20 41 B9 [4] 4C 8D 05 [4] 48 8B D6 48 8B CB FF 15 [4] 85 C0 75 [5-10] 4C 8D 0C 3E 48 8D 44 24 ?? 48 89 44 24 30 44 89 74 24 28 4C 89 74 24 20 33 D2 41 B8 [4] 48 8B CB FF 15 } 1067 | $process = { 89 74 24 30 ?? 8D 4C 24 [2] 89 74 24 28 33 D2 41 B8 00 00 02 00 48 C7 44 24 20 08 00 00 00 48 8B CF FF 15 [4] 85 C0 0F 84 [4] 48 8B [2-3] 48 8D 45 ?? 48 89 44 24 50 4C 8D 05 [4] 48 8D 45 ?? 48 89 7D 08 48 89 44 24 48 45 33 C9 ?? 89 74 24 40 33 D2 ?? 89 74 24 38 C7 44 24 30 04 00 08 00 [0-1] 89 74 24 28 ?? 89 74 24 20 FF 15 } 1068 | $token = { FF 15 [4] 4C 8D 44 24 ?? BA 0A 00 00 00 48 8B C8 FF 15 [4] 85 C0 0F 84 [4] 48 8B 4C 24 ?? 48 8D [2-3] 41 B9 02 00 00 00 48 89 44 24 28 45 33 C0 C7 44 24 20 02 00 00 00 41 8D 51 09 FF 15 [4] 85 C0 0F 84 [4] 45 33 C0 4C 8D 4C 24 ?? 33 C9 41 8D 50 01 FF 15 } 1069 | condition: 1070 | ((uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and (uint16(uint32(0x3C)+0x18) == 0x020B)) and all of them 1071 | } 1072 | 1073 | rule baseline_Trojan_MSIL_GORAT_Module_PowerShell_1 1074 | { 1075 | meta: 1076 | description = "The TypeLibGUID present in a .NET binary maps directly to the ProjectGuid found in the '.csproj' file of a .NET project. This rule looks for .NET PE files that contain the ProjectGuid found in the 'RedFlare - Module - PowerShell' project." 1077 | md5 = "dd8805d0e470e59b829d98397507d8c2" 1078 | reference = "https://www.fireeye.com/blog/products-and-services/2020/12/fireeye-shares-details-of-recent-cyber-attack-actions-to-protect-community.html" 1079 | author = "FireEye" 1080 | strings: 1081 | $typelibguid0 = "38d89034-2dd9-4367-8a6e-5409827a243a" ascii nocase wide 1082 | $typelibguid1 = "845ee9dc-97c9-4c48-834e-dc31ee007c25" ascii nocase wide 1083 | condition: 1084 | (uint16(0) == 0x5A4D and uint32(uint32(0x3C)) == 0x00004550) and any of them 1085 | } 1086 | 1087 | --------------------------------------------------------------------------------