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 diff --git a/rules/apt/apt_stonedrill.yml b/rules/apt/apt_stonedrill.yml new file mode 100644 index 00000000..96f6023a --- /dev/null +++ b/rules/apt/apt_stonedrill.yml @@ -0,0 +1,17 @@ +title: StoneDrill Service Install +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: + product: windows + service: system +detection: + selection: + EventID: 7045 + indicators: + - ServiceName: NtsSrv + - ServiceFileName: '* LocalService' + condition: selection and indicators +falsepositives: + - Unlikely +level: high 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_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 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 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_recon_activity.yml b/rules/windows/builtin/win_susp_recon_activity.yml new file mode 100644 index 00000000..6736cfdd --- /dev/null +++ b/rules/windows/builtin/win_susp_recon_activity.yml @@ -0,0 +1,24 @@ +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 + 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 + 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 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 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..32c4d3dd --- /dev/null +++ b/rules/windows/powershell/powershell_exe_calling_ps.yml @@ -0,0 +1,21 @@ +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 +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: + selection1: + EventID: 400 + EngineVersion: + - '2.*' + - '4.*' + - '5.*' + HostVersion: '3.*' + condition: selection1 +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..d78ba91a --- /dev/null +++ b/rules/windows/powershell/powershell_malicious_commandlets.yml @@ -0,0 +1,121 @@ +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 + - Invoke-Mimikittenz + 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..3f7a9a23 --- /dev/null +++ b/rules/windows/powershell/powershell_psattack.yml @@ -0,0 +1,17 @@ +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: + EventID: 4103 + keywords: + - 'PS ATTACK!!!' + condition: keywords +falsepositives: + - Pentesters +level: high 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..158e77e1 --- /dev/null +++ b/rules/windows/powershell/powershell_suspicious_invocation.yml @@ -0,0 +1,19 @@ +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' + - 'iex(New-Object Net.WebClient).Download' + condition: keywords +falsepositives: + - Penetration tests +level: high 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/ 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 diff --git a/tools/README.md b/tools/README.md index 80894af8..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 ```--config``` and ```--recurse``` parameters are not usable yet. +The ```--output``` and ```--config``` parameters are not usable yet. diff --git a/tools/backends.py b/tools/backends.py index ccd27067..f7df815e 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,19 +145,53 @@ 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))) 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 diff --git a/tools/sigma.py b/tools/sigma.py index ee05f74c..6edecfd7 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,52 @@ class SigmaConditionParser: def getParseTree(self): return(self.parsedSearch[0]) + +# Configuration +class SigmaConfiguration: + """Sigma converter configuration. Contains field mappings and logsource descriptions""" + def __init__(self, configyaml=None): + if configyaml == None: + self.fieldmappings = dict() + self.logsources = dict() + else: + config = yaml.safe_load(configyaml) + + 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 33d70b6f..c96783f2 100755 --- a/tools/sigmac.py +++ b/tools/sigmac.py @@ -5,7 +5,9 @@ import sys import argparse import yaml import json -from sigma import SigmaParser, SigmaParseError +import pathlib +import itertools +from sigma import SigmaParser, SigmaParseError, SigmaConfiguration, SigmaConfigParseError import backends def print_verbose(*args, **kwargs): @@ -16,12 +18,25 @@ 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 [pathlib.Path(p) for p in 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") -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,16 +47,33 @@ 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) + +sigmaconfig = SigmaConfiguration() +if cmdargs.config: + try: + conffile = cmdargs.config + f = open(conffile) + sigmaconfig = SigmaConfiguration(f) + except OSError as 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)), file=sys.stderr) + except SigmaParseError as e: + print("Sigma configuration parse error in %s: %s" % (conffile, str(e)), file=sys.stderr) + try: - backend = backends.getBackend(cmdargs.target)() + 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 cmdargs.inputs: +for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse): print_verbose("* Processing Sigma input %s" % (sigmafile)) try: - f = open(sigmafile) + f = sigmafile.open() parser = SigmaParser(f) print_debug("Parsed YAML:\n", json.dumps(parser.parsedyaml, indent=2)) parser.parse_sigma() @@ -51,14 +83,14 @@ for sigmafile in cmdargs.inputs: 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()