From 4aaa22fd6d0ef45cbf90be1364bf4e0eddd7a9db Mon Sep 17 00:00:00 2001 From: Thomas Patzke Date: Sat, 4 Mar 2017 23:36:46 +0100 Subject: [PATCH 01/27] Made not implemented sigmac features obvious * added notes to help message * error if not implemented option is used --- tools/sigmac.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tools/sigmac.py b/tools/sigmac.py index 33d70b6f..bbc326fe 100755 --- a/tools/sigmac.py +++ b/tools/sigmac.py @@ -17,11 +17,11 @@ def print_debug(*args, **kwargs): print(*args, **kwargs) argparser = argparse.ArgumentParser(description="Convert Sigma rules into SIEM signatures.") -argparser.add_argument("--recurse", "-r", help="Recurse into subdirectories") +argparser.add_argument("--recurse", "-r", help="Recurse into subdirectories (not yet implemented)") argparser.add_argument("--target", "-t", default="es-qs", choices=backends.getBackendDict().keys(), help="Output target format") argparser.add_argument("--target-list", "-l", action="store_true", help="List available output target formats") -argparser.add_argument("--config", "-c", help="Configuration with field name and index mapping for target environment") -argparser.add_argument("--output", "-o", help="Output file or filename prefix if multiple files are generated") +argparser.add_argument("--config", "-c", help="Configuration with field name and index mapping for target environment (not yet implemented)") +argparser.add_argument("--output", "-o", help="Output file or filename prefix if multiple files are generated (not yet implemented)") argparser.add_argument("--verbose", "-v", action="store_true", help="Be verbose") argparser.add_argument("--debug", "-d", action="store_true", help="Debugging output") argparser.add_argument("inputs", nargs="*", help="Sigma input files") @@ -32,6 +32,16 @@ if cmdargs.target_list: print("%10s: %s" % (backend.identifier, backend.__doc__)) sys.exit(0) +if cmdargs.recurse: + print("--recurse/-r not yet implemented", file=sys.stderr) + sys.exit(99) +if cmdargs.output: + print("--output/-o not yet implemented", file=sys.stderr) + sys.exit(99) +if cmdargs.config: + print("--config/-c not yet implemented", file=sys.stderr) + sys.exit(99) + try: backend = backends.getBackend(cmdargs.target)() except LookupError as e: From f092333bb4d9f9fe92aa71a412f381772b612eab Mon Sep 17 00:00:00 2001 From: Thomas Patzke Date: Sun, 5 Mar 2017 00:37:28 +0100 Subject: [PATCH 02/27] Sigmac configuration parsing --- tools/sigma.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tools/sigma.py b/tools/sigma.py index ee05f74c..d0215ad0 100644 --- a/tools/sigma.py +++ b/tools/sigma.py @@ -192,7 +192,7 @@ class SigmaConditionTokenizer: def index(self, item): return self.tokens.index(item) - + class SigmaParseError(Exception): pass @@ -349,3 +349,30 @@ class SigmaConditionParser: def getParseTree(self): return(self.parsedSearch[0]) + +# Configuration +class SigmaConfiguration: + def __init__(self, configyaml): + config = yaml.safe_load(configyaml) + + self.fieldmappings = config['fieldmappings'] + if type(self.fieldmappings) != dict: + raise SigmaConfigParseError("Fieldmappings must be a map") + + self.logsources = config['logsources'] + if type(self.logsources) != dict: + raise SigmaConfigParseError("Logsources must be a map") + for name, logsource in self.logsources.items(): + if type(logsource) != dict: + raise SigmaConfigParseError("Logsource definitions must be maps") + if type(logsource.category) != str or type(logsource.product) != str or type(logsource.service) != str: + raise SigmaConfigParseError("Logsource category, product or service must be a string") + if type(logsource.index) not in (str, list): + raise SigmaConfigParseError("Logsource index must be string or list of strings") + if type(logsource.index) == list and set([type(index) for index in logsource.index]).issubset({str}): + raise SigmaConfigParseError("Logsource index patterns must be strings") + if type(self.conditions) != dict: + raise SigmaConfigParseError("Logsource conditions must be a map") + +class SigmaConfigParseError(Exception): + pass From 15373d86f513d88b7ff0c8216345bba9c3f9cc9e Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 01:06:36 +0100 Subject: [PATCH 03/27] Set theme jekyll-theme-hacker --- _config.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 _config.yml diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..fc24e7a6 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-hacker \ No newline at end of file From d397ee9f68a2ee42207e94774f5ae503adc3811a Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 01:47:25 +0100 Subject: [PATCH 04/27] First PowerShell Ruleset --- .../powershell/powershell_exe_calling_ps.yml | 21 +++ .../powershell_malicious_commandlets.yml | 120 ++++++++++++++++++ .../powershell_malicious_keywords.yml | 41 ++++++ .../powershell/powershell_psattack.yml | 16 +++ 4 files changed, 198 insertions(+) create mode 100644 rules/windows/powershell/powershell_exe_calling_ps.yml create mode 100644 rules/windows/powershell/powershell_malicious_commandlets.yml create mode 100644 rules/windows/powershell/powershell_malicious_keywords.yml create mode 100644 rules/windows/powershell/powershell_psattack.yml diff --git a/rules/windows/powershell/powershell_exe_calling_ps.yml b/rules/windows/powershell/powershell_exe_calling_ps.yml new file mode 100644 index 00000000..f78e6ab7 --- /dev/null +++ b/rules/windows/powershell/powershell_exe_calling_ps.yml @@ -0,0 +1,21 @@ +title: PowerShell called from an Executable +status: experimental +description: Detects PowerShell called from an executable by the version mismatch method +reference: https://adsecurity.org/?p=2921 +author: Sean Metcalf (source), Florian Roth (rule) +logsource: + platform: windows + product: powershell + description: 'It is recommanded to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277' +detection: + seletcion: + EventID: 400 + EngineVersion: '2.*' + HostVersion: + - '3.*' + - '4.*' + - '5.*' + condition: keywords +falsepositives: + - Pentesters +level: high diff --git a/rules/windows/powershell/powershell_malicious_commandlets.yml b/rules/windows/powershell/powershell_malicious_commandlets.yml new file mode 100644 index 00000000..672b5dae --- /dev/null +++ b/rules/windows/powershell/powershell_malicious_commandlets.yml @@ -0,0 +1,120 @@ +title: Malicious PowerShell Commandlets +status: experimental +description: Detects Commandlet names from well-known PowerShell exploitation frameworks +reference: https://adsecurity.org/?p=2921 +author: Sean Metcalf (source), Florian Roth (rule) +logsource: + platform: windows + product: powershell + description: 'It is recommanded to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277' +detection: + keywords: + - Invoke-DllInjection + - Invoke-Shellcode + - Invoke-WmiCommand + - Get-GPPPassword + - Get-Keystrokes + - Get-TimedScreenshot + - Get-VaultCredential + - Invoke-CredentialInjection + - Invoke-Mimikatz + - Invoke-NinjaCopy + - Invoke-TokenManipulation + - Out-Minidump + - VolumeShadowCopyTools + - Invoke-ReflectivePEInjection + - Invoke-UserHunter + - Find-GPOLocation + - Invoke-ACLScanner + - Invoke-DowngradeAccount + - Get-ServiceUnquoted + - Get-ServiceFilePermission + - Get-ServicePermission + - Invoke-ServiceAbuse + - Install-ServiceBinary + - Get-RegAutoLogon + - Get-VulnAutoRun + - Get-VulnSchTask + - Get-UnattendedInstallFile + - Get-WebConfig + - Get-ApplicationHost + - Get-RegAlwaysInstallElevated + - Get-Unconstrained + - Add-RegBackdoor + - Add-ScrnSaveBackdoor + - Gupt-Backdoor + - Invoke-ADSBackdoor + - Enabled-DuplicateToken + - Invoke-PsUaCme + - Remove-Update + - Check-VM + - Get-LSASecret + - Get-PassHashes + - Invoke-Mimikatz + - Show-TargetScreen + - Port-Scan + - Invoke-PoshRatHttp + - Invoke-PowerShellTCP + - Invoke-PowerShellWMI + - Add-Exfiltration + - Add-Persistence + - Do-Exfiltration + - Start-CaptureServer + - Invoke-DllInjection + - Invoke-ReflectivePEInjection + - Invoke-ShellCode + - Get-ChromeDump + - Get-ClipboardContents + - Get-FoxDump + - Get-IndexedItem + - Get-Keystrokes + - Get-Screenshot + - Invoke-Inveigh + - Invoke-NetRipper + - Invoke-NinjaCopy + - Out-Minidump + - Invoke-EgressCheck + - Invoke-PostExfil + - Invoke-PSInject + - Invoke-RunAs + - MailRaider + - New-HoneyHash + - Set-MacAttribute + - Get-VaultCredential + - Invoke-DCSync + - Invoke-Mimikatz + - Invoke-PowerDump + - Invoke-TokenManipulation + - Exploit-Jboss + - Invoke-ThunderStruck + - Invoke-VoiceTroll + - Set-Wallpaper + - Invoke-InveighRelay + - Invoke-PsExec + - Invoke-SSHCommand + - Get-SecurityPackages + - Install-SSP + - Invoke-BackdoorLNK + - PowerBreach + - Get-GPPPassword + - Get-SiteListPassword + - Get-System + - Invoke-BypassUAC + - Invoke-Tater + - Invoke-WScriptBypassUAC + - PowerUp + - PowerView + - Get-RickAstley + - Find-Fruit + - HTTP-Login + - Find-TrustedDocuments + - Invoke-Paranoia + - Invoke-WinEnum + - Invoke-ARPScan + - Invoke-PortScan + - Invoke-ReverseDNSLookup + - Invoke-SMBScanner + condition: keywords +falsepositives: + - Penetration testing +level: high diff --git a/rules/windows/powershell/powershell_malicious_keywords.yml b/rules/windows/powershell/powershell_malicious_keywords.yml new file mode 100644 index 00000000..b477c927 --- /dev/null +++ b/rules/windows/powershell/powershell_malicious_keywords.yml @@ -0,0 +1,41 @@ +title: Malicious PowerShell Commandlets +status: experimental +description: Detects Commandlet names from well-known PowerShell exploitation frameworks +reference: https://adsecurity.org/?p=2921 +author: Sean Metcalf (source), Florian Roth (rule) +logsource: + platform: windows + product: powershell + description: 'It is recommanded to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277' +detection: + keywords: + - AdjustTokenPrivileges + - IMAGE_NT_OPTIONAL_HDR64_MAGIC + - Management.Automation.RuntimeException + - Microsoft.Win32.UnsafeNativeMethods + - ReadProcessMemory.Invoke + - Runtime.InteropServices + - SE_PRIVILEGE_ENABLED + - System.Security.Cryptography + - System.Runtime.InteropServices + - LSA_UNICODE_STRING + - MiniDumpWriteDump + - PAGE_EXECUTE_READ + - Net.Sockets.SocketFlags + - Reflection.Assembly + - SECURITY_DELEGATION + - TOKEN_ADJUST_PRIVILEGES + - TOKEN_ALL_ACCESS + - TOKEN_ASSIGN_PRIMARY + - TOKEN_DUPLICATE + - TOKEN_ELEVATION + - TOKEN_IMPERSONATE + - TOKEN_INFORMATION_CLASS + - TOKEN_PRIVILEGES + - TOKEN_QUERY + - Metasploit + - Mimikatz + condition: keywords +falsepositives: + - Penetration tests +level: high diff --git a/rules/windows/powershell/powershell_psattack.yml b/rules/windows/powershell/powershell_psattack.yml new file mode 100644 index 00000000..5fb3e892 --- /dev/null +++ b/rules/windows/powershell/powershell_psattack.yml @@ -0,0 +1,16 @@ +title: PowerShell PSAttack +status: experimental +description: Detects the use of PSAttack PowerShell hack tool +reference: https://adsecurity.org/?p=2921 +author: Sean Metcalf (source), Florian Roth (rule) +logsource: + platform: windows + product: powershell + description: 'It is recommanded to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277' +detection: + keywords: + - 'PS ATTACK!!!' + condition: keywords +falsepositives: + - Pentesters +level: high From 12535417d9a3214cb83908e5062707081a6fcc8e Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 01:47:37 +0100 Subject: [PATCH 05/27] Typo --- rules/windows/sysmon/sysmon_mshta_spawn_shell.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/windows/sysmon/sysmon_mshta_spawn_shell.yml b/rules/windows/sysmon/sysmon_mshta_spawn_shell.yml index aa83953d..1880213d 100644 --- a/rules/windows/sysmon/sysmon_mshta_spawn_shell.yml +++ b/rules/windows/sysmon/sysmon_mshta_spawn_shell.yml @@ -1,4 +1,4 @@ -title: MSHTA Spawning Windows Shell +title: MSHTA Spawning Windows Shell status: experimental description: Detects a Windows command line executable started from MSHTA. reference: https://www.trustedsec.com/july-2015/malicious-htas/ From 97b4078d018d09118aaaaa3705cfa9a88e2f71c5 Mon Sep 17 00:00:00 2001 From: Omer Yampel Date: Sat, 4 Mar 2017 20:26:39 -0500 Subject: [PATCH 06/27] Update powershell_malicious_commandlets.yml Added https://github.com/putterpanda/mimikittenz reference --- rules/windows/powershell/powershell_malicious_commandlets.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/windows/powershell/powershell_malicious_commandlets.yml b/rules/windows/powershell/powershell_malicious_commandlets.yml index 672b5dae..d78ba91a 100644 --- a/rules/windows/powershell/powershell_malicious_commandlets.yml +++ b/rules/windows/powershell/powershell_malicious_commandlets.yml @@ -114,6 +114,7 @@ detection: - Invoke-PortScan - Invoke-ReverseDNSLookup - Invoke-SMBScanner + - Invoke-Mimikittenz condition: keywords falsepositives: - Penetration testing From 1e1cf9cb9e92e3c6e7c627c4b83f673caa7d3f76 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 14:14:31 +0100 Subject: [PATCH 07/27] PowerShell Rules Revision --- .../windows/powershell/powershell_exe_calling_ps.yml | 12 ++++++------ rules/windows/powershell/powershell_psattack.yml | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/rules/windows/powershell/powershell_exe_calling_ps.yml b/rules/windows/powershell/powershell_exe_calling_ps.yml index f78e6ab7..32c4d3dd 100644 --- a/rules/windows/powershell/powershell_exe_calling_ps.yml +++ b/rules/windows/powershell/powershell_exe_calling_ps.yml @@ -1,4 +1,4 @@ -title: PowerShell called from an Executable +title: PowerShell called from an Executable Version Mismatch status: experimental description: Detects PowerShell called from an executable by the version mismatch method reference: https://adsecurity.org/?p=2921 @@ -8,14 +8,14 @@ logsource: product: powershell description: 'It is recommanded to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277' detection: - seletcion: + selection1: EventID: 400 - EngineVersion: '2.*' - HostVersion: - - '3.*' + EngineVersion: + - '2.*' - '4.*' - '5.*' - condition: keywords + HostVersion: '3.*' + condition: selection1 falsepositives: - Pentesters level: high diff --git a/rules/windows/powershell/powershell_psattack.yml b/rules/windows/powershell/powershell_psattack.yml index 5fb3e892..3f7a9a23 100644 --- a/rules/windows/powershell/powershell_psattack.yml +++ b/rules/windows/powershell/powershell_psattack.yml @@ -8,6 +8,7 @@ logsource: product: powershell description: 'It is recommanded to use the new "Script Block Logging" of PowerShell v5 https://adsecurity.org/?p=2277' detection: + EventID: 4103 keywords: - 'PS ATTACK!!!' condition: keywords From 7fae49b183a05d393bab76926645506e84895b31 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 15:01:51 +0100 Subject: [PATCH 08/27] More PowerShell rules --- .../powershell_suspicious_download.yml | 15 +++++++++++++++ .../powershell_suspicious_invocation.yml | 18 ++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 rules/windows/powershell/powershell_suspicious_download.yml create mode 100644 rules/windows/powershell/powershell_suspicious_invocation.yml diff --git a/rules/windows/powershell/powershell_suspicious_download.yml b/rules/windows/powershell/powershell_suspicious_download.yml new file mode 100644 index 00000000..1fa32002 --- /dev/null +++ b/rules/windows/powershell/powershell_suspicious_download.yml @@ -0,0 +1,15 @@ +title: Suspicious PowerShell Download +status: experimental +description: Detects suspicious PowerShell download command +author: Florian Roth +logsource: + platform: windows + product: powershell +detection: + keywords: + - 'System.Net.WebClient).DownloadString(' + - 'system.net.webclient).downloadfile(' + condition: keywords +falsepositives: + - PowerShell scripts that download content from the Internet +level: medium diff --git a/rules/windows/powershell/powershell_suspicious_invocation.yml b/rules/windows/powershell/powershell_suspicious_invocation.yml new file mode 100644 index 00000000..d72d070a --- /dev/null +++ b/rules/windows/powershell/powershell_suspicious_invocation.yml @@ -0,0 +1,18 @@ +title: Suspicious PowerShell Invocations +status: experimental +description: Detects suspicious PowerShell invocation command parameters +author: Florian Roth (rule) +logsource: + platform: windows + product: powershell +detection: + keywords: + - ' -nop -w hidden -c * [Convert]::FromBase64String' + - ' -w hidden -noni -nop -c "iex(New-Object' + - ' -w hidden -ep bypass -Enc' + - 'powershell.exe reg add HKCU\software\microsoft\windows\currentversion\run' + - 'bypass -noprofile -windowstyle hidden (new-object system.net.webclient).download' + condition: keywords +falsepositives: + - Penetration tests +level: high From 294df21c566d8e1eba7b4f2af3a8b0ca8c2f3031 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 22:45:54 +0100 Subject: [PATCH 09/27] Added expression --- rules/windows/powershell/powershell_suspicious_invocation.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/windows/powershell/powershell_suspicious_invocation.yml b/rules/windows/powershell/powershell_suspicious_invocation.yml index d72d070a..158e77e1 100644 --- a/rules/windows/powershell/powershell_suspicious_invocation.yml +++ b/rules/windows/powershell/powershell_suspicious_invocation.yml @@ -12,6 +12,7 @@ detection: - ' -w hidden -ep bypass -Enc' - 'powershell.exe reg add HKCU\software\microsoft\windows\currentversion\run' - 'bypass -noprofile -windowstyle hidden (new-object system.net.webclient).download' + - 'iex(New-Object Net.WebClient).Download' condition: keywords falsepositives: - Penetration tests From 8864647e0483b5daaaccfac5746bf3c55ce47df0 Mon Sep 17 00:00:00 2001 From: Thomas Patzke Date: Sun, 5 Mar 2017 23:44:52 +0100 Subject: [PATCH 10/27] Parsing of sigmac configuration files * field mappings * log sources --- tools/sigma.py | 27 +++++++++++++++++++-------- tools/sigmac.py | 14 +++++++++++--- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/tools/sigma.py b/tools/sigma.py index d0215ad0..f956900d 100644 --- a/tools/sigma.py +++ b/tools/sigma.py @@ -352,26 +352,37 @@ class SigmaConditionParser: # Configuration class SigmaConfiguration: + """Sigma converter configuration. Contains field mappings and logsource descriptions""" def __init__(self, configyaml): config = yaml.safe_load(configyaml) - self.fieldmappings = config['fieldmappings'] + try: + self.fieldmappings = config['fieldmappings'] + except KeyError: + self.fieldmappings = dict() if type(self.fieldmappings) != dict: raise SigmaConfigParseError("Fieldmappings must be a map") - self.logsources = config['logsources'] + try: + self.logsources = config['logsources'] + except KeyError: + self.logsources = dict() + if type(self.logsources) != dict: raise SigmaConfigParseError("Logsources must be a map") for name, logsource in self.logsources.items(): if type(logsource) != dict: raise SigmaConfigParseError("Logsource definitions must be maps") - if type(logsource.category) != str or type(logsource.product) != str or type(logsource.service) != str: + if 'category' in logsource and type(logsource['category']) != str \ + or 'product' in logsource and type(logsource['product']) != str \ + or 'service' in logsource and type(logsource['service']) != str: raise SigmaConfigParseError("Logsource category, product or service must be a string") - if type(logsource.index) not in (str, list): - raise SigmaConfigParseError("Logsource index must be string or list of strings") - if type(logsource.index) == list and set([type(index) for index in logsource.index]).issubset({str}): - raise SigmaConfigParseError("Logsource index patterns must be strings") - if type(self.conditions) != dict: + if 'index' in logsource: + if type(logsource['index']) not in (str, list): + raise SigmaConfigParseError("Logsource index must be string or list of strings") + if type(logsource['index']) == list and not set([type(index) for index in logsource['index']]).issubset({str}): + raise SigmaConfigParseError("Logsource index patterns must be strings") + if 'conditions' in logsource and type(logsource['conditions']) != dict: raise SigmaConfigParseError("Logsource conditions must be a map") class SigmaConfigParseError(Exception): diff --git a/tools/sigmac.py b/tools/sigmac.py index bbc326fe..e04ce875 100755 --- a/tools/sigmac.py +++ b/tools/sigmac.py @@ -5,7 +5,7 @@ import sys import argparse import yaml import json -from sigma import SigmaParser, SigmaParseError +from sigma import SigmaParser, SigmaParseError, SigmaConfiguration, SigmaConfigParseError import backends def print_verbose(*args, **kwargs): @@ -39,8 +39,16 @@ if cmdargs.output: print("--output/-o not yet implemented", file=sys.stderr) sys.exit(99) if cmdargs.config: - print("--config/-c not yet implemented", file=sys.stderr) - sys.exit(99) + try: + conffile = cmdargs.config + f = open(conffile) + config = SigmaConfiguration(f) + except OSError as e: + print("Failed to open Sigma configuration file %s: %s" % (conffile, str(e))) + except yaml.parser.ParserError as e: + print("Sigma configuration file %s is no valid YAML: %s" % (conffile, str(e))) + except SigmaParseError as e: + print("Sigma configuration parse error in %s: %s" % (conffile, str(e))) try: backend = backends.getBackend(cmdargs.target)() From 7b815ef3e511de0b63b3e557ac4dd07bbc4d86a6 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 23:51:39 +0100 Subject: [PATCH 11/27] Sysmon PowerShell - Suspicious Param Combination --- ...n_powershell_suspicious_parameter_combo.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 rules/windows/sysmon/sysmon_powershell_suspicious_parameter_combo.yml diff --git a/rules/windows/sysmon/sysmon_powershell_suspicious_parameter_combo.yml b/rules/windows/sysmon/sysmon_powershell_suspicious_parameter_combo.yml new file mode 100644 index 00000000..c9466ed7 --- /dev/null +++ b/rules/windows/sysmon/sysmon_powershell_suspicious_parameter_combo.yml @@ -0,0 +1,18 @@ +title: Suspicious PowerShell Parameter Combination +status: experimental +description: Detects suspicious PowerShell invocation command parameters +author: Florian Roth +logsource: + product: sysmon +detection: + keywords: + - 'powershell' + - ' -nop ' + - ' -w hidden ' + - ' -exec bypass ' + - ' -enc ' + condition: all of keywords +falsepositives: + - Penetration tests + - Very special / sneaky PowerShell scripts +level: high From 16c5192ee9ed101061b803e9ac329af10dc05f86 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 23:52:02 +0100 Subject: [PATCH 12/27] Windows Malicious Password Dumper Service Installs --- .../builtin/win_malicious_service_install.yml | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 rules/windows/builtin/win_malicious_service_install.yml diff --git a/rules/windows/builtin/win_malicious_service_install.yml b/rules/windows/builtin/win_malicious_service_install.yml new file mode 100644 index 00000000..3a0faf20 --- /dev/null +++ b/rules/windows/builtin/win_malicious_service_install.yml @@ -0,0 +1,22 @@ +title: Malicious Service Install +description: This method detects well-known keywords of malicious services in the Windows System Eventlog +author: Florian Roth +logsource: + product: windows + service: system +detection: + selection: + EventID: + - 7045 + - 4697 + keywords: + - 'WCE SERVICE' + - 'WCESERVICE' + - 'DumpSvc' + quarkspwdump: + EventID: 16 + HiveName: '*\AppData\Local\Temp\SAM*.dmp' + condition: ( selection and keywords ) or quarkspwdump +falsepositives: + - Unlikely +level: high From aad892c834315c079140135e531d238118c980b2 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Sun, 5 Mar 2017 23:55:52 +0100 Subject: [PATCH 13/27] Windows Built-In rules > LogSource definition --- rules/windows/builtin/win_alert_mimikatz_keywords.yml | 8 +------- rules/windows/builtin/win_av_relevant_match.yml | 5 ++--- rules/windows/builtin/win_susp_add_sid_history.yml | 2 +- rules/windows/builtin/win_susp_dsrm_password_change.yml | 2 +- rules/windows/builtin/win_susp_eventlog_cleared.yml | 2 +- rules/windows/builtin/win_susp_failed_logon_reasons.yml | 2 +- .../builtin/win_susp_failed_logons_single_source.yml | 2 +- rules/windows/builtin/win_susp_kerberos_manipulation.yml | 2 +- rules/windows/builtin/win_susp_lsass_dump.yml | 2 +- rules/windows/builtin/win_susp_rc4_kerberos.yml | 2 +- .../builtin/win_susp_security_eventlog_cleared.yml | 2 +- 11 files changed, 12 insertions(+), 19 deletions(-) diff --git a/rules/windows/builtin/win_alert_mimikatz_keywords.yml b/rules/windows/builtin/win_alert_mimikatz_keywords.yml index ffbb86a5..20a9ecdc 100644 --- a/rules/windows/builtin/win_alert_mimikatz_keywords.yml +++ b/rules/windows/builtin/win_alert_mimikatz_keywords.yml @@ -4,12 +4,6 @@ author: Florian Roth logsource: product: windows detection: - selection: - EventLog: - - Security - - System - - Application - - Microsoft-Windows-Sysmon/Operational keywords: - mimikatz - mimilib @@ -19,7 +13,7 @@ detection: - sekurlsa::logonpasswords - lsadump::sam - mimidrv.sys - condition: selection and 1 of keywords + condition: keywords falsepositives: - Naughty administrators - Penetration test diff --git a/rules/windows/builtin/win_av_relevant_match.yml b/rules/windows/builtin/win_av_relevant_match.yml index 62c0338a..b3027045 100644 --- a/rules/windows/builtin/win_av_relevant_match.yml +++ b/rules/windows/builtin/win_av_relevant_match.yml @@ -3,9 +3,8 @@ description: This detection method points out highly relevant Antivirus events author: Florian Roth logsource: product: windows + service: application detection: - selection: - EventLog: Application keywords: - HTool - Hacktool @@ -30,7 +29,7 @@ detection: filters: - Keygen - Crack - condition: selection and 1 of keywords and not 1 of filters + condition: keywords and not 1 of filters falsepositives: - Some software piracy tools (key generators, cracks) are classified as hack tools level: high diff --git a/rules/windows/builtin/win_susp_add_sid_history.yml b/rules/windows/builtin/win_susp_add_sid_history.yml index 8837b72b..42169e99 100644 --- a/rules/windows/builtin/win_susp_add_sid_history.yml +++ b/rules/windows/builtin/win_susp_add_sid_history.yml @@ -5,9 +5,9 @@ reference: https://adsecurity.org/?p=1772 author: Thomas Patzke logsource: product: windows + service: security detection: selection: - EventLog: Security EventID: - 4765 - 4766 diff --git a/rules/windows/builtin/win_susp_dsrm_password_change.yml b/rules/windows/builtin/win_susp_dsrm_password_change.yml index 0c8cacfc..2390881b 100644 --- a/rules/windows/builtin/win_susp_dsrm_password_change.yml +++ b/rules/windows/builtin/win_susp_dsrm_password_change.yml @@ -5,9 +5,9 @@ reference: https://adsecurity.org/?p=1714 author: Thomas Patzke logsource: product: windows + service: security detection: selection: - EventLog: Security EventID: 4794 condition: selection falsepositives: diff --git a/rules/windows/builtin/win_susp_eventlog_cleared.yml b/rules/windows/builtin/win_susp_eventlog_cleared.yml index 0e6e8ee3..67623568 100644 --- a/rules/windows/builtin/win_susp_eventlog_cleared.yml +++ b/rules/windows/builtin/win_susp_eventlog_cleared.yml @@ -4,9 +4,9 @@ reference: https://twitter.com/deviouspolack/status/832535435960209408 author: Florian Roth logsource: product: windows + service: system detection: selection: - EventLog: System EventID: 104 condition: selection falsepositives: diff --git a/rules/windows/builtin/win_susp_failed_logon_reasons.yml b/rules/windows/builtin/win_susp_failed_logon_reasons.yml index 97f2a83d..392f8ec1 100644 --- a/rules/windows/builtin/win_susp_failed_logon_reasons.yml +++ b/rules/windows/builtin/win_susp_failed_logon_reasons.yml @@ -3,9 +3,9 @@ description: This method uses uncommon error codes on failed logons to determine author: Florian Roth logsource: product: windows + service: security detection: selection: - EventLog: Security EventID: - 4625 - 4776 diff --git a/rules/windows/builtin/win_susp_failed_logons_single_source.yml b/rules/windows/builtin/win_susp_failed_logons_single_source.yml index 3ec13ac0..5233903f 100644 --- a/rules/windows/builtin/win_susp_failed_logons_single_source.yml +++ b/rules/windows/builtin/win_susp_failed_logons_single_source.yml @@ -3,9 +3,9 @@ description: Detects suspicious failed logins with different user accounts from author: Florian Roth logsource: product: windows + service: security detection: selection: - EventLog: Security EventID: - 529 - 4625 diff --git a/rules/windows/builtin/win_susp_kerberos_manipulation.yml b/rules/windows/builtin/win_susp_kerberos_manipulation.yml index df2c7f07..c6087da2 100644 --- a/rules/windows/builtin/win_susp_kerberos_manipulation.yml +++ b/rules/windows/builtin/win_susp_kerberos_manipulation.yml @@ -3,9 +3,9 @@ description: This method triggers on rare Kerberos Failure Codes caused by manip author: Florian Roth logsource: product: windows + service: security detection: selection: - EventLog: Security EventID: - 675 - 4768 diff --git a/rules/windows/builtin/win_susp_lsass_dump.yml b/rules/windows/builtin/win_susp_lsass_dump.yml index b14d5f18..0cb86bcd 100644 --- a/rules/windows/builtin/win_susp_lsass_dump.yml +++ b/rules/windows/builtin/win_susp_lsass_dump.yml @@ -4,9 +4,9 @@ status: experimental reference: https://twitter.com/jackcr/status/807385668833968128 logsource: product: windows + service: security detection: selection: - EventLog: Security EventID: 4656 ProcessName: 'C:\Windows\System32\lsass.exe' AccessMask: '0x705' diff --git a/rules/windows/builtin/win_susp_rc4_kerberos.yml b/rules/windows/builtin/win_susp_rc4_kerberos.yml index 0525bf6f..3fa722b5 100644 --- a/rules/windows/builtin/win_susp_rc4_kerberos.yml +++ b/rules/windows/builtin/win_susp_rc4_kerberos.yml @@ -4,9 +4,9 @@ reference: https://adsecurity.org/?p=3458 description: Detects logons using RC4 encryption type logsource: product: windows + service: security detection: selection: - EventLog: Security EventID: 4769 TicketOptions: '0x40810000' TicketEncryption: '0x17' diff --git a/rules/windows/builtin/win_susp_security_eventlog_cleared.yml b/rules/windows/builtin/win_susp_security_eventlog_cleared.yml index e7e4924f..853b8f63 100644 --- a/rules/windows/builtin/win_susp_security_eventlog_cleared.yml +++ b/rules/windows/builtin/win_susp_security_eventlog_cleared.yml @@ -3,9 +3,9 @@ description: Some threat groups tend to delete the local 'Security' Eventlog usi author: Florian Roth logsource: product: windows + service: security detection: selection: - EventLog: Security EventID: - 517 - 1102 From df39dee702372392df9b1719bdbf11ef95462fbf Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Mon, 6 Mar 2017 09:36:10 +0100 Subject: [PATCH 14/27] Sigmac recursive feature --- tools/sigmac.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tools/sigmac.py b/tools/sigmac.py index 33d70b6f..9e97207e 100755 --- a/tools/sigmac.py +++ b/tools/sigmac.py @@ -5,6 +5,8 @@ import sys import argparse import yaml import json +import pathlib +import itertools from sigma import SigmaParser, SigmaParseError import backends @@ -16,8 +18,21 @@ def print_debug(*args, **kwargs): if cmdargs.debug: print(*args, **kwargs) +def alliter(path): + for sub in path.iterdir(): + if sub.is_dir(): + yield from alliter(sub) + else: + yield sub + +def get_inputs(paths, recursive): + if recursive: + return list(itertools.chain.from_iterable([list(alliter(pathlib.Path(p))) for p in paths])) + else: + return paths + argparser = argparse.ArgumentParser(description="Convert Sigma rules into SIEM signatures.") -argparser.add_argument("--recurse", "-r", help="Recurse into subdirectories") +argparser.add_argument("--recurse", "-r", action="store_true", help="Recurse into subdirectories (not yet implemented)") argparser.add_argument("--target", "-t", default="es-qs", choices=backends.getBackendDict().keys(), help="Output target format") argparser.add_argument("--target-list", "-l", action="store_true", help="List available output target formats") argparser.add_argument("--config", "-c", help="Configuration with field name and index mapping for target environment") @@ -32,13 +47,20 @@ if cmdargs.target_list: print("%10s: %s" % (backend.identifier, backend.__doc__)) sys.exit(0) +if cmdargs.output: + print("--output/-o not yet implemented", file=sys.stderr) + sys.exit(99) +if cmdargs.config: + print("--config/-c not yet implemented", file=sys.stderr) + sys.exit(99) + try: backend = backends.getBackend(cmdargs.target)() except LookupError as e: print("Backend not found!") sys.exit(1) -for sigmafile in cmdargs.inputs: +for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse): print_verbose("* Processing Sigma input %s" % (sigmafile)) try: f = open(sigmafile) From 362ff157ba8f4d282a937a18c7e95be2e6481c1d Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Mon, 6 Mar 2017 09:37:31 +0100 Subject: [PATCH 15/27] Update README.md --- tools/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/README.md b/tools/README.md index 80894af8..0d1e55ae 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,3 +1,3 @@ Tools in this section are currently in public beta status. -The ```--config``` and ```--recurse``` parameters are not usable yet. +The ```--output``` and ```--recurse``` parameters are not usable yet. From da6c5c19ae3bbabd80d9884ceeee61f78e3fad88 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Mon, 6 Mar 2017 09:37:44 +0100 Subject: [PATCH 16/27] Update README.md --- tools/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/README.md b/tools/README.md index 0d1e55ae..f416a98a 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,3 +1,3 @@ Tools in this section are currently in public beta status. -The ```--output``` and ```--recurse``` parameters are not usable yet. +The ```--output``` and ```--config``` parameters are not usable yet. From 66c46b2f44cda19772822f3271fb5f0e696ab70a Mon Sep 17 00:00:00 2001 From: Thomas Patzke Date: Mon, 6 Mar 2017 22:00:05 +0100 Subject: [PATCH 17/27] Removed NullBackend --- tools/backends.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tools/backends.py b/tools/backends.py index ccd27067..5ec1eadd 100644 --- a/tools/backends.py +++ b/tools/backends.py @@ -148,11 +148,3 @@ class SplunkBackend(BaseBackend): def generateValueNode(self, node): return "\"%s\"" % (self.cleanValue(str(node))) - -class NullBackend(BaseBackend): - """Does nothing, for debugging purposes.""" - identifier = "null" - active = False - - def generate(self, parsed): - pass From 05df298d45a262c4656613a4b8991eafe734c2a4 Mon Sep 17 00:00:00 2001 From: Thomas Patzke Date: Mon, 6 Mar 2017 22:07:04 +0100 Subject: [PATCH 18/27] Field mappings --- tools/backends.py | 11 +++++--- tools/sigma.py | 67 +++++++++++++++++++++++++++-------------------- tools/sigmac.py | 6 +++-- 3 files changed, 51 insertions(+), 33 deletions(-) diff --git a/tools/backends.py b/tools/backends.py index ccd27067..792c0c5c 100644 --- a/tools/backends.py +++ b/tools/backends.py @@ -22,6 +22,11 @@ class BaseBackend: identifier = "base" active = False + def __init__(self, sigmaconfig): + if not isinstance(sigmaconfig, (sigma.SigmaConfiguration, None)): + raise TypeError("SigmaConfiguration object expected") + self.sigmaconfig = sigmaconfig + def generate(self, parsed): return self.generateNode(parsed.getParseTree()) @@ -96,7 +101,7 @@ class ElasticsearchQuerystringBackend(BaseBackend): key, value = node if type(value) not in (str, int, list): raise TypeError("Map values must be strings, numbers or lists, not " + str(type(value))) - return "%s:%s" % (key, self.generateNode(value)) + return "%s:%s" % (self.sigmaconfig.get_fieldmapping(key), self.generateNode(value)) def generateValueNode(self, node): return "\"%s\"" % (self.cleanValue(str(node))) @@ -140,9 +145,9 @@ class SplunkBackend(BaseBackend): def generateMapItemNode(self, node): key, value = node if type(value) in (str, int): - return '%s=%s' % (key, self.generateNode(value)) + return '%s=%s' % (self.sigmaconfig.get_fieldmapping(key), self.generateNode(value)) elif type(value) == list: - return "(" + (" OR ".join(['%s=%s' % (key, self.generateValueNode(item)) for item in value])) + ")" + return "(" + (" OR ".join(['%s=%s' % (self.sigmaconfig.get_fieldmapping(key), self.generateValueNode(item)) for item in value])) + ")" else: raise TypeError("Map values must be strings, numbers or lists, not " + str(type(value))) diff --git a/tools/sigma.py b/tools/sigma.py index f956900d..6edecfd7 100644 --- a/tools/sigma.py +++ b/tools/sigma.py @@ -353,37 +353,48 @@ class SigmaConditionParser: # Configuration class SigmaConfiguration: """Sigma converter configuration. Contains field mappings and logsource descriptions""" - def __init__(self, configyaml): - config = yaml.safe_load(configyaml) - - try: - self.fieldmappings = config['fieldmappings'] - except KeyError: + def __init__(self, configyaml=None): + if configyaml == None: self.fieldmappings = dict() - if type(self.fieldmappings) != dict: - raise SigmaConfigParseError("Fieldmappings must be a map") - - try: - self.logsources = config['logsources'] - except KeyError: self.logsources = dict() + else: + config = yaml.safe_load(configyaml) - if type(self.logsources) != dict: - raise SigmaConfigParseError("Logsources must be a map") - for name, logsource in self.logsources.items(): - if type(logsource) != dict: - raise SigmaConfigParseError("Logsource definitions must be maps") - if 'category' in logsource and type(logsource['category']) != str \ - or 'product' in logsource and type(logsource['product']) != str \ - or 'service' in logsource and type(logsource['service']) != str: - raise SigmaConfigParseError("Logsource category, product or service must be a string") - if 'index' in logsource: - if type(logsource['index']) not in (str, list): - raise SigmaConfigParseError("Logsource index must be string or list of strings") - if type(logsource['index']) == list and not set([type(index) for index in logsource['index']]).issubset({str}): - raise SigmaConfigParseError("Logsource index patterns must be strings") - if 'conditions' in logsource and type(logsource['conditions']) != dict: - raise SigmaConfigParseError("Logsource conditions must be a map") + try: + self.fieldmappings = config['fieldmappings'] + except KeyError: + self.fieldmappings = dict() + if type(self.fieldmappings) != dict: + raise SigmaConfigParseError("Fieldmappings must be a map") + + try: + self.logsources = config['logsources'] + except KeyError: + self.logsources = dict() + + if type(self.logsources) != dict: + raise SigmaConfigParseError("Logsources must be a map") + for name, logsource in self.logsources.items(): + if type(logsource) != dict: + raise SigmaConfigParseError("Logsource definitions must be maps") + if 'category' in logsource and type(logsource['category']) != str \ + or 'product' in logsource and type(logsource['product']) != str \ + or 'service' in logsource and type(logsource['service']) != str: + raise SigmaConfigParseError("Logsource category, product or service must be a string") + if 'index' in logsource: + if type(logsource['index']) not in (str, list): + raise SigmaConfigParseError("Logsource index must be string or list of strings") + if type(logsource['index']) == list and not set([type(index) for index in logsource['index']]).issubset({str}): + raise SigmaConfigParseError("Logsource index patterns must be strings") + if 'conditions' in logsource and type(logsource['conditions']) != dict: + raise SigmaConfigParseError("Logsource conditions must be a map") + + def get_fieldmapping(self, fieldname): + """Return mapped fieldname if mapping defined or field name given in parameter value""" + try: + return self.fieldmappings[fieldname] + except KeyError: + return fieldname class SigmaConfigParseError(Exception): pass diff --git a/tools/sigmac.py b/tools/sigmac.py index 4bbdc605..28f8ef40 100755 --- a/tools/sigmac.py +++ b/tools/sigmac.py @@ -50,11 +50,13 @@ if cmdargs.target_list: if cmdargs.output: print("--output/-o not yet implemented", file=sys.stderr) sys.exit(99) + +sigmaconfig = SigmaConfiguration() if cmdargs.config: try: conffile = cmdargs.config f = open(conffile) - config = SigmaConfiguration(f) + sigmaconfig = SigmaConfiguration(f) except OSError as e: print("Failed to open Sigma configuration file %s: %s" % (conffile, str(e))) except yaml.parser.ParserError as e: @@ -63,7 +65,7 @@ if cmdargs.config: print("Sigma configuration parse error in %s: %s" % (conffile, str(e))) try: - backend = backends.getBackend(cmdargs.target)() + backend = backends.getBackend(cmdargs.target)(sigmaconfig) except LookupError as e: print("Backend not found!") sys.exit(1) From d1030ec0536dfcd678fb36546b16b09406cb9ae2 Mon Sep 17 00:00:00 2001 From: Thomas Patzke Date: Mon, 6 Mar 2017 22:47:30 +0100 Subject: [PATCH 19/27] Fieldlist backend Lists all fields used in given rules. --- tools/backends.py | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/tools/backends.py b/tools/backends.py index 792c0c5c..f7df815e 100644 --- a/tools/backends.py +++ b/tools/backends.py @@ -154,10 +154,44 @@ class SplunkBackend(BaseBackend): def generateValueNode(self, node): return "\"%s\"" % (self.cleanValue(str(node))) -class NullBackend(BaseBackend): - """Does nothing, for debugging purposes.""" - identifier = "null" - active = False +class FieldnameListBackend(BaseBackend): + """List all fieldnames from given Sigma rules for creation of a field mapping configuration.""" + identifier = "fieldlist" + active = True def generate(self, parsed): - pass + return "\n".join(sorted(set(list(flatten(self.generateNode(parsed.getParseTree())))))) + + def generateANDNode(self, node): + return [self.generateNode(val) for val in node] + + def generateORNode(self, node): + return self.generateANDNode(node) + + def generateNOTNode(self, node): + return self.generateNode(node.item) + + def generateSubexpressionNode(self, node): + return self.generateNode(node.items) + + def generateListNode(self, node): + if not set([type(value) for value in node]).issubset({str, int}): + raise TypeError("List values must be strings or numbers") + return [self.generateNode(value) for value in node] + + def generateMapItemNode(self, node): + key, value = node + if type(value) not in (str, int, list): + raise TypeError("Map values must be strings, numbers or lists, not " + str(type(value))) + return [self.sigmaconfig.get_fieldmapping(key)] + + def generateValueNode(self, node): + return [] + +# Helpers +def flatten(l): + for i in l: + if type(i) == list: + yield from flatten(i) + else: + yield i From dae88fbcfaf7e424bcd4ae128286d14a39cbed2c Mon Sep 17 00:00:00 2001 From: Thomas Patzke Date: Mon, 6 Mar 2017 23:01:33 +0100 Subject: [PATCH 20/27] Error and warning messages are printed to stderr --- tools/sigmac.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/sigmac.py b/tools/sigmac.py index 28f8ef40..c49140e1 100755 --- a/tools/sigmac.py +++ b/tools/sigmac.py @@ -58,16 +58,16 @@ if cmdargs.config: f = open(conffile) sigmaconfig = SigmaConfiguration(f) except OSError as e: - print("Failed to open Sigma configuration file %s: %s" % (conffile, str(e))) + print("Failed to open Sigma configuration file %s: %s" % (conffile, str(e)), file=sys.stderr) except yaml.parser.ParserError as e: - print("Sigma configuration file %s is no valid YAML: %s" % (conffile, str(e))) + print("Sigma configuration file %s is no valid YAML: %s" % (conffile, str(e)), file=sys.stderr) except SigmaParseError as e: - print("Sigma configuration parse error in %s: %s" % (conffile, str(e))) + print("Sigma configuration parse error in %s: %s" % (conffile, str(e)), file=sys.stderr) try: backend = backends.getBackend(cmdargs.target)(sigmaconfig) except LookupError as e: - print("Backend not found!") + print("Backend not found!", file=sys.stderr) sys.exit(1) for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse): @@ -83,14 +83,14 @@ for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse): print_debug("Condition Parse Tree:", condparsed) print(backend.generate(condparsed)) except OSError as e: - print("Failed to open Sigma file %s: %s" % (sigmafile, str(e))) + print("Failed to open Sigma file %s: %s" % (sigmafile, str(e)), file=sys.stderr) except yaml.parser.ParserError as e: - print("Sigma file %s is no valid YAML: %s" % (sigmafile, str(e))) + print("Sigma file %s is no valid YAML: %s" % (sigmafile, str(e)), file=sys.stderr) except SigmaParseError as e: - print("Sigma parse error in %s: %s" % (sigmafile, str(e))) + print("Sigma parse error in %s: %s" % (sigmafile, str(e)), file=sys.stderr) except NotImplementedError as e: - print("An unsupported feature is required for this Sigma rule: " + str(e)) - print("Feel free to contribute for fun and fame, this is open source :) -> https://github.com/Neo23x0/sigma") + print("An unsupported feature is required for this Sigma rule: " + str(e), file=sys.stderr) + print("Feel free to contribute for fun and fame, this is open source :) -> https://github.com/Neo23x0/sigma", file=sys.stderr) finally: f.close() print_debug() From 7113b3aed99031c3594d4f27b49cdf3a4dea40c6 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Tue, 7 Mar 2017 09:24:06 +0100 Subject: [PATCH 21/27] Rule: APT StoneDrill Service Install --- rules/apt/apt_stonedrill.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 rules/apt/apt_stonedrill.yml diff --git a/rules/apt/apt_stonedrill.yml b/rules/apt/apt_stonedrill.yml new file mode 100644 index 00000000..b3321ff2 --- /dev/null +++ b/rules/apt/apt_stonedrill.yml @@ -0,0 +1,15 @@ +title: StoneDrill Service Install +description: 'This method detects a service install of the malicious Microsoft Network Realtime Inspection Service service described in Kaspersky\'s StoneDrill report' +author: Florian Roth +reference: https://securelist.com/blog/research/77725/from-shamoon-to-stonedrill/ +logsource: + product: windows + service: system +detection: + selection: + EventID: 7045 + ServiceName: NtsSrv + condition: selection +falsepositives: + - Unlikely +level: high From cd445f8ae94283e3c9a6d7e54c117949aa090008 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Tue, 7 Mar 2017 09:41:46 +0100 Subject: [PATCH 22/27] Bugfix: non-recursive list not pathlib.Path elements but strings --- tools/sigmac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sigmac.py b/tools/sigmac.py index 28f8ef40..70f02821 100755 --- a/tools/sigmac.py +++ b/tools/sigmac.py @@ -29,7 +29,7 @@ def get_inputs(paths, recursive): if recursive: return list(itertools.chain.from_iterable([list(alliter(pathlib.Path(p))) for p in paths])) else: - return paths + return [pathlib.Path(p) for p in paths] argparser = argparse.ArgumentParser(description="Convert Sigma rules into SIEM signatures.") argparser.add_argument("--recurse", "-r", action="store_true", help="Recurse into subdirectories (not yet implemented)") From 5662bae40eccb59cb442f0e5d840cf58e13395a4 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Tue, 7 Mar 2017 09:24:06 +0100 Subject: [PATCH 23/27] Rule: APT StoneDrill Service Install --- rules/apt/apt_stonedrill.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 rules/apt/apt_stonedrill.yml diff --git a/rules/apt/apt_stonedrill.yml b/rules/apt/apt_stonedrill.yml new file mode 100644 index 00000000..b3321ff2 --- /dev/null +++ b/rules/apt/apt_stonedrill.yml @@ -0,0 +1,15 @@ +title: StoneDrill Service Install +description: 'This method detects a service install of the malicious Microsoft Network Realtime Inspection Service service described in Kaspersky\'s StoneDrill report' +author: Florian Roth +reference: https://securelist.com/blog/research/77725/from-shamoon-to-stonedrill/ +logsource: + product: windows + service: system +detection: + selection: + EventID: 7045 + ServiceName: NtsSrv + condition: selection +falsepositives: + - Unlikely +level: high From b34d1b75659b4c90ce8332a79e7b27eb2bbf2f0f Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Tue, 7 Mar 2017 10:22:14 +0100 Subject: [PATCH 24/27] Stonedrill rule enhancement --- rules/apt/apt_stonedrill.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rules/apt/apt_stonedrill.yml b/rules/apt/apt_stonedrill.yml index b3321ff2..96f6023a 100644 --- a/rules/apt/apt_stonedrill.yml +++ b/rules/apt/apt_stonedrill.yml @@ -1,5 +1,5 @@ title: StoneDrill Service Install -description: 'This method detects a service install of the malicious Microsoft Network Realtime Inspection Service service described in Kaspersky\'s StoneDrill report' +description: 'This method detects a service install of the malicious Microsoft Network Realtime Inspection Service service described in StoneDrill report by Kaspersky' author: Florian Roth reference: https://securelist.com/blog/research/77725/from-shamoon-to-stonedrill/ logsource: @@ -8,8 +8,10 @@ logsource: detection: selection: EventID: 7045 - ServiceName: NtsSrv - condition: selection + indicators: + - ServiceName: NtsSrv + - ServiceFileName: '* LocalService' + condition: selection and indicators falsepositives: - Unlikely level: high From fa6f76f2763ce97d950bff23fca49ff328493e23 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Tue, 7 Mar 2017 12:01:39 +0100 Subject: [PATCH 25/27] Rule: Windows - Recon Activity --- .../builtin/win_susp_recon_activity.yml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 rules/windows/builtin/win_susp_recon_activity.yml diff --git a/rules/windows/builtin/win_susp_recon_activity.yml b/rules/windows/builtin/win_susp_recon_activity.yml new file mode 100644 index 00000000..daeaf7b6 --- /dev/null +++ b/rules/windows/builtin/win_susp_recon_activity.yml @@ -0,0 +1,23 @@ +title: Detects Reconnaissance Activity +status: experimental +description: 'Detects activity as "net user administrator /domain" and "net group domain admins /domain"' +reference: https://findingbad.blogspot.de/2017/01/hunting-what-does-it-look-like.html +author: Florian Roth (rule), Jack Croock (method) +logsource: + product: windows + service: security +detection: + selection: + - EventID: 4661 + ObjectType: 'SAM_USER' + ObjectName: 'S-1-5-21-*-500' + AccessMask: '0x2d' + - EventID: 4661 + ObjectType: 'SAM_GROUP' + ObjectName: 'S-1-5-21-*-512' + AccessMask: '0x2d' + condition: selection +falsepositives: + - Administrator activity + - Penetration tests +level: high From 54848869323e1d498011a6d1c7bff5f81aee0d6a Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Tue, 7 Mar 2017 13:06:38 +0100 Subject: [PATCH 26/27] Rule: Windows - Recon Activity (improved) --- rules/windows/builtin/win_susp_recon_activity.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rules/windows/builtin/win_susp_recon_activity.yml b/rules/windows/builtin/win_susp_recon_activity.yml index daeaf7b6..6736cfdd 100644 --- a/rules/windows/builtin/win_susp_recon_activity.yml +++ b/rules/windows/builtin/win_susp_recon_activity.yml @@ -1,11 +1,12 @@ title: Detects Reconnaissance Activity status: experimental -description: 'Detects activity as "net user administrator /domain" and "net group domain admins /domain"' +description: 'Detects activity as "net user administrator /domain" and "net group domain admins /domain"' reference: https://findingbad.blogspot.de/2017/01/hunting-what-does-it-look-like.html author: Florian Roth (rule), Jack Croock (method) logsource: product: windows service: security + description: The volume of Event ID 4661 ist high on Domain Controllers and therefore "Audit SAM" and "Audit Kernel Object" advanced audit policy settings are not configured in the recommandations for server systems detection: selection: - EventID: 4661 From 3507a5e64463f3510e941fe0216ad7646c7becd7 Mon Sep 17 00:00:00 2001 From: Florian Roth Date: Wed, 8 Mar 2017 19:09:34 +0100 Subject: [PATCH 27/27] Rule: Rare Windows Service Installs --- .../builtin/win_rare_service_installs.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 rules/windows/builtin/win_rare_service_installs.yml diff --git a/rules/windows/builtin/win_rare_service_installs.yml b/rules/windows/builtin/win_rare_service_installs.yml new file mode 100644 index 00000000..32fdf910 --- /dev/null +++ b/rules/windows/builtin/win_rare_service_installs.yml @@ -0,0 +1,16 @@ +title: Rare Service Installs +description: Detects rare service installs that only appear a few times per timeframe and could reveal password dumpers, backdoor installs or other types of malicious services +status: experimental +author: Florian Roth +logsource: + product: windows + service: system +detection: + selection: + EventID: 7045 + timeframe: 7d + condition: selection | count(ServiceFileName) < 5 +falsepositives: + - Software installation + - Software updates +level: low