| name | roth-detection-engineering |
| description | Apply Florian Roth's detection engineering methodology with YARA and Sigma rules. Emphasizes portable detection logic, community sharing, and signature quality. Use when creating detection rules that work across platforms. |
| tags | yara, sigma, detection-rules, malware, ioc, scanning, community, open-source, threat-detection |
Florian Roth — Detection Engineering
Overview
Florian Roth is CTO of Nextron Systems and creator of some of the most influential detection tools in security: the Sigma rule format, extensive YARA rule sets, and the THOR APT scanner. His work has made detection logic portable, shareable, and accessible to the community.
References
Core Philosophy
"Detection should be shareable."
"One rule format to rule them all."
"The community is stronger together."
Roth's insight: detection logic shouldn't be locked to a single SIEM. Sigma allows defenders to write once and deploy everywhere, accelerating community-wide defense.
Key Contributions
Sigma Rules
Generic signature format for SIEM systems. Write detection logic once, convert to any SIEM query language.
YARA Rules
Binary pattern matching for malware detection. Extensive rule sets for threat hunting.
THOR Scanner
APT scanner that brings professional detection capabilities to incident response.
When Implementing
Always
- Write Sigma rules for portability
- Include MITRE ATT&CK tags
- Document false positives
- Test rules before deployment
- Share rules with the community
- Version control your rules
Never
- Hard-code SIEM-specific syntax in shared rules
- Deploy untested rules to production
- Ignore false positive rates
- Keep effective rules private
- Skip metadata (author, date, references)
Prefer
- Sigma over SIEM-specific queries
- Behavioral patterns over exact strings
- Community rules as starting points
- Layered detection (multiple rules per technique)
- Regular rule review and tuning
Implementation Patterns
Sigma Rule Anatomy
title: Suspicious PowerShell Download Cradle
id: 3b6ab547-8ec2-4991-b9d2-2b06702a48d7
status: experimental
description: |
Detects PowerShell download cradle patterns commonly used
for malware delivery and living-off-the-land attacks.
references:
- https://attack.mitre.org/techniques/T1059/001/
- https://lolbas-project.github.io/
author: Florian Roth (Nextron Systems)
date: 2024/01/15
modified: 2024/02/20
tags:
- attack.execution
- attack.t1059.001
- attack.defense_evasion
- attack.t1140
logsource:
category: process_creation
product: windows
detection:
selection_img:
- Image|endswith: '\powershell.exe'
-
YARA Rule Anatomy
/*
YARA Rule: Cobalt Strike Beacon
Author: Florian Roth
Date: 2024-01-15
Reference: https://www.cobaltstrike.com/
Detects Cobalt Strike beacon payloads in memory or files
*/
rule CobaltStrike_Beacon_Encoded
{
meta:
description = "Detects Cobalt Strike beacon"
author = "Florian Roth"
date = "2024-01-15"
reference = "https://attack.mitre.org/software/S0154/"
hash1 = "abc123..."
score = 80
strings:
// XOR encoded config
$config_start = { 00 01 00 01 00 02 }
// Sleep mask
$sleep_mask = { 48 89 5C 24 08 48 89 6C 24 10 }
// Named pipe pattern
$pipe = "\\\\.\\pipe\\msagent_" ascii wide
// Default watermark (change for specific actors)
$watermark = { 01 00 00 00 [4] 00 00 00 00 }
// Reflective loader
$reflective = "ReflectiveLoader" ascii
condition:
uint16(0) == 0x5A4D and // MZ header
filesize < 1MB and
(
($config_start and $watermark) or
($sleep_mask and $pipe) or
($reflective and $pipe)
)
}
rule Mimikatz_Memory_Strings
{
meta:
description = "Detects Mimikatz in memory"
author = "Florian Roth"
reference = "https://attack.mitre.org/software/S0002/"
strings:
$s1 = "sekurlsa::logonpasswords" ascii wide nocase
$s2 = "sekurlsa::wdigest" ascii wide nocase
$s3 = "sekurlsa::kerberos" ascii wide nocase
$s4 = "lsadump::sam" ascii wide nocase
$s5 = "lsadump::dcsync" ascii wide nocase
$s6 = "privilege::debug" ascii wide nocase
$author = "gentilkiwi" ascii wide
$tool = "mimikatz" ascii wide nocase
condition:
3 of ($s*) or
($author and $tool)
}
Sigma to SIEM Conversion
from dataclasses import dataclass
from typing import Dict, List, Optional
import yaml
import re
@dataclass
class SigmaRule:
"""Parsed Sigma rule"""
title: str
id: str
status: str
description: str
logsource: Dict
detection: Dict
level: str
tags: List[str]
falsepositives: List[str]
@classmethod
def from_yaml(cls, yaml_content: str) -> 'SigmaRule':
data = yaml.safe_load(yaml_content)
return cls(
title=data.get('title', ''),
id=data.get('id', ''),
status=data.get('status', 'experimental'),
description=data.get('description', ''),
logsource=data.get('logsource', {}),
detection=data.get('detection', {}),
level=data.get('level', 'medium'),
tags=data.get('tags', []),
falsepositives=data.get(, [])
)
:
():
.rule = rule
() -> :
query_parts = []
.rule.logsource.get() == :
query_parts.append()
detection = .rule.detection
condition = detection.get(, )
key, value detection.items():
key == :
(value, ):
field_queries = ._convert_selection_splunk(value)
query_parts.append()
query = .join(query_parts)
query +=
query
() -> []:
queries = []
field, value selection.items():
field:
field_name, modifier = field.split()
modifier == :
(value, ):
queries.extend([ v value])
:
queries.append()
modifier == :
(value, ):
queries.extend([ v value])
:
queries.append()
modifier == :
(value, ):
queries.extend([ v value])
:
queries.append()
:
(value, ):
queries.extend([ v value])
:
queries.append()
queries
() -> :
query_parts = []
detection = .rule.detection
key, value detection.items():
key == :
(value, ):
field_queries = ._convert_selection_elastic(value)
query_parts.append()
.join(query_parts)
() -> []:
queries = []
field, value selection.items():
field_name = field.split()[] field field
field_map = {
: ,
: ,
: ,
: ,
:
}
field_name = field_map.get(field_name, field_name)
field:
modifier = field.split()[]
modifier == :
(value, ):
queries.extend([ v value])
:
queries.append()
:
(value, ):
queries.extend([ v value])
:
queries.append()
queries
() -> :
() -> :
query_parts = []
.rule.logsource.get() == :
query_parts.append()
query_parts.append()
detection = .rule.detection
key, value detection.items():
key == :
(value, ):
field, val value.items():
field_name = field.split()[]
sentinel_fields = {
: ,
: ,
:
}
sentinel_field = sentinel_fields.get(field_name, field_name)
field:
(val, ):
conditions = .join(
[ v val]
)
query_parts.append()
.join(query_parts)
Rule Quality Framework
from dataclasses import dataclass
from typing import List, Dict, Optional
from enum import Enum
class QualityDimension(Enum):
ACCURACY = "accuracy"
COVERAGE = "coverage"
PERFORMANCE = "performance"
MAINTAINABILITY = "maintainability"
DOCUMENTATION = "documentation"
@dataclass
class RuleQualityScore:
"""Quality assessment for a detection rule"""
rule_id: str
accuracy_score: int
coverage_score: int
performance_score: int
maintainability_score: int
documentation_score: int
false_positive_rate: float
true_positive_samples: int
avg_query_time_ms: float
improvements: List[str]
@property
def overall_score(self) -> :
weights = {
: ,
: ,
: ,
: ,
:
}
(
.accuracy_score * weights[] +
.coverage_score * weights[] +
.performance_score * weights[] +
.maintainability_score * weights[] +
.documentation_score * weights[]
)
() -> :
score = .overall_score
score >= :
score >= :
score >= :
score >= :
:
() -> RuleQualityScore:
yaml
rule = yaml.safe_load(rule_content)
improvements = []
doc_score =
rule.get():
doc_score -=
improvements.append()
rule.get():
doc_score -=
improvements.append()
rule.get():
doc_score -=
improvements.append()
rule.get():
doc_score -=
improvements.append()
rule.get():
doc_score -=
improvements.append()
maint_score =
detection = rule.get(, {})
key, value detection.items():
(value, ):
field, val value.items():
(val, ) (val) > :
maint_score -=
improvements.append(
)
( k k detection.keys()):
maint_score -=
improvements.append()
RuleQualityScore(
rule_id=rule.get(, ),
accuracy_score=,
coverage_score=,
performance_score=,
maintainability_score=maint_score,
documentation_score=doc_score,
false_positive_rate=,
true_positive_samples=,
avg_query_time_ms=,
improvements=improvements
)
Mental Model
Roth approaches detection engineering by asking:
- Is this portable? Can it work across SIEMs?
- Is this documented? Metadata, references, false positives
- Is this testable? Can we validate it works?
- Is this shareable? Can the community benefit?
- Is this maintainable? Will it survive updates?
Signature Roth Moves
- Sigma rules for portable detection
- YARA rules for binary/memory patterns
- Comprehensive rule metadata
- Community sharing and collaboration
- Detection-as-code in version control
- Continuous rule quality improvement