Mastering YARA Rules - Complete Guide

Tools Advanced 📅 Published: 20/08/2025

YARA is the "pattern matching swiss knife for malware researchers." This comprehensive guide covers everything from basic rule writing to advanced ...

Mastering YARA Rules - Complete Guide

Mastering YARA Rules - Complete Guide

YARA is the "pattern matching swiss knife for malware researchers." This comprehensive guide covers everything from basic rule writing to advanced detection techniques, performance optimization, and integration with threat hunting platforms.

Table of Contents

Introduction to YARA

YARA is a powerful pattern matching engine designed to help malware researchers identify and classify malware samples. Created by Victor M. Alvarez, YARA allows analysts to create rules based on textual or binary patterns to detect specific malware families, behaviors, or code structures.

Why Use YARA?

  • Flexible Pattern Matching: Text strings, hex patterns, regular expressions
  • Rich Condition Logic: Complex boolean expressions and counting
  • Fast Performance: Optimized for large-scale scanning
  • Extensible: Modules for PE files, ELF, network protocols
  • Widely Adopted: Used by major security vendors and researchers
  • Open Source: Free to use and modify

YARA Use Cases

Use Case Description Examples
Malware Detection Identify known malware families Emotet, Cobalt Strike, APT campaigns
Threat Hunting Proactive threat detection Memory scans, file system hunting
Incident Response Rapid triage and classification Live response, forensic analysis
Automation Automated analysis pipelines Sandbox integration, SIEM rules

Installation and Setup

Installing YARA

Windows Installation:

# Method 1: Pre-compiled binaries
1. Download from https://github.com/VirusTotal/yara/releases
2. Extract yara.exe and yarac.exe to a directory in PATH
3. Test installation: yara --version

# Method 2: Python integration
pip install yara-python

# Method 3: Chocolatey
choco install yara

Linux Installation:

# Ubuntu/Debian
sudo apt-get update
sudo apt-get install yara

# CentOS/RHEL
sudo yum install epel-release
sudo yum install yara

# From source
git clone https://github.com/VirusTotal/yara.git
cd yara
./bootstrap.sh
./configure
make
sudo make install

Python Integration:

# Install yara-python for scripting
pip install yara-python

# Test installation
python -c "import yara; print('YARA Python binding installed successfully')"

Setting Up Development Environment

Editor Configuration:

# VS Code with YARA extension
1. Install "YARA" extension by infosec-intern
2. Configure syntax highlighting
3. Enable rule validation

# Vim configuration
1. Install yara.vim syntax file
2. Add to .vimrc:
   autocmd BufNewFile,BufRead *.yar,*.yara set filetype=yara

Directory Structure:

yara-rules/
├── rules/
│   ├── malware/
│   │   ├── emotet.yar
│   │   ├── cobalt_strike.yar
│   │   └── apt/
│   ├── tools/
│   │   ├── packers.yar
│   │   └── pentest_tools.yar
│   └── signatures/
├── modules/
├── tests/
└── scripts/

Basic Rule Syntax

Rule Structure

Anatomy of a YARA Rule:

rule RuleName
{
    meta:
        // Metadata about the rule
        author = "Analyst Name"
        description = "Detects specific malware family"
        date = "2025-10-07"
        version = "1.0"
        hash = "md5_hash_of_sample"
        
    strings:
        // String definitions
        $string1 = "malicious_string"
        $hex_pattern = { 4D 5A 90 00 }
        $regex = /http:\/\/[a-z0-9\.]+\/malware\.php/
        
    condition:
        // Logic that determines when rule matches
        $string1 or $hex_pattern or $regex
}

Metadata Section

Standard Metadata Fields:

meta:
    author = "Security Team"              // Rule author
    description = "Detects Emotet variant"  // What the rule detects
    date = "2025-10-07"                   // Creation date
    version = "1.2"                       // Rule version
    reference = "https://blog.example.com/analysis"  // External reference
    hash = "a1b2c3d4e5f6..."            // Sample hash
    family = "Emotet"                     // Malware family
    severity = "high"                     // Threat level
    tlp = "white"                         // Traffic Light Protocol
    yarahub_license = "CC0 1.0"          // License information
    yarahub_rule_matching_tlp = "TLP:WHITE"  // Sharing restrictions
    yarahub_rule_sharing_tlp = "TLP:WHITE"   // Distribution restrictions

Basic String Definitions

Text Strings:

strings:
    $plain_text = "This is a plain text string"
    $case_insensitive = "MALWARE" nocase
    $wide_string = "Unicode String" wide
    $ascii_string = "ASCII String" ascii
    $fullword = "cmd" fullword          // Match complete words only

Hex Patterns:

strings:
    $mz_header = { 4D 5A }              // MZ header
    $pe_signature = { 50 45 00 00 }     // PE signature
    $wildcard_pattern = { 4D 5A ?? ?? 03 }  // Wildcards with ??
    $variable_pattern = { 4D 5A [2-4] 03 }  // Variable length gaps
    $jump_pattern = { 4D 5A [2-4] 03 [10-20] 50 45 }  // Multiple gaps

Regular Expressions:

strings:
    $ip_address = /\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/
    $email_pattern = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/
    $url_pattern = /https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}[\/\w\.-]*\/?/
    $bitcoin_address = /[13][a-km-zA-HJ-NP-Z1-9]{25,34}/

String Types and Patterns

Advanced String Modifiers

String Modifiers:

strings:
    // Case sensitivity
    $case_sensitive = "CaseSensitive"
    $case_insensitive = "caseinsensitive" nocase
    
    // Character encoding
    $ascii_only = "ASCII" ascii
    $wide_only = "Wide" wide
    $both_encodings = "Both" ascii wide
    
    // Word boundaries  
    $full_word = "malware" fullword
    $partial_match = "malware"  // Matches "malware123"
    
    // Private strings (don't count in conditions)
    $private_string = "internal" private

Complex Hex Patterns:

strings:
    // Exact bytes
    $exact = { 4D 5A 90 00 03 00 00 00 04 00 00 00 FF FF }
    
    // Wildcards
    $wildcards = { 4D 5A ?? ?? ?? ?? 04 00 }
    
    // Alternatives
    $alternatives = { 4D 5A ( 90 00 | 89 00 | 87 00 ) }
    
    // Ranges
    $ranges = { 4D 5A [2-8] 04 00 }
    $unbounded = { 4D 5A [4-] 50 45 }  // 4 or more bytes
    
    // Complex pattern
    $complex = { 
        4D 5A                    // MZ header
        [58-62]                  // DOS stub (variable)
        50 45 00 00              // PE signature
        ( 4C 01 | 64 86 )        // Machine type (x86 or x64)
    }

Regular Expression Patterns

Common Regex Patterns:

strings:
    // Network indicators
    $ipv4 = /(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/
    $domain = /[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*\.[a-zA-Z]{2,}/
    $url = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/
    
    // File paths
    $windows_path = /[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*/
    $unix_path = /\/(?:[^\/\0]+\/)*[^\/\0]+/
    
    // Cryptocurrency addresses
    $bitcoin = /[13][a-km-zA-HJ-NP-Z1-9]{25,34}/
    $ethereum = /0x[a-fA-F0-9]{40}/
    $monero = /4[0-9AB][1-9A-HJ-NP-Za-km-z]{93}/
    
    // Encoded data
    $base64 = /[A-Za-z0-9+\/]{20,}={0,2}/
    $hex_encoded = /[0-9A-Fa-f]{32,}/

Regex Modifiers:

strings:
    $case_insensitive_regex = /malware/i
    $multiline_regex = /start.*end/s
    $global_match = /pattern/g

Advanced Conditions

Boolean Logic

Basic Operators:

condition:
    // AND operation
    $string1 and $string2
    
    // OR operation
    $string1 or $string2
    
    // NOT operation
    not $string1
    
    // Complex combinations
    ($string1 or $string2) and not $string3
    
    // Parentheses for precedence
    ($a and $b) or ($c and $d)

Counting Conditions:

condition:
    // Count specific strings
    #string1 > 5                  // String appears more than 5 times
    #string2 == 3                 // String appears exactly 3 times
    #string3 >= 1                 // String appears at least once
    
    // Count any/all strings
    any of ($string*)             // Any string starting with "string"
    all of ($http*)               // All strings starting with "http"
    2 of ($string*)               // At least 2 strings match
    3 of them                     // At least 3 of all defined strings
    
    // Percentage conditions
    90% of them                   // 90% of all strings must match

File Properties

File Size Conditions:

condition:
    filesize < 100KB              // File smaller than 100KB
    filesize > 1MB                // File larger than 1MB
    filesize == 1024              // Exact size in bytes
    
    // Size ranges
    filesize > 10KB and filesize < 1MB

String Positions:

condition:
    // Position-based conditions
    $mz_header at 0               // String at specific offset
    $string1 at entrypoint        // String at entry point
    $string2 in (0..1024)         // String in first 1024 bytes
    $string3 in (filesize-1024..filesize)  // String in last 1024 bytes
    
    // Multiple positions
    for any i in (0..10) : ( $pattern at i * 0x1000 )

PE Module Integration

PE File Analysis:

import "pe"

condition:
    // PE file validation
    pe.is_pe
    
    // Architecture checks
    pe.machine == pe.MACHINE_I386     // 32-bit
    pe.machine == pe.MACHINE_AMD64    // 64-bit
    
    // Compilation timestamp
    pe.timestamp > 1609459200        // After Jan 1, 2021
    
    // Section analysis
    pe.number_of_sections > 3
    pe.sections[0].name == ".text"
    
    // Import analysis
    pe.imports("kernel32.dll", "CreateFileA")
    pe.imports("ntdll.dll", "NtWriteVirtualMemory")
    
    // Resource analysis
    pe.number_of_resources > 0
    pe.version_info["CompanyName"] contains "Microsoft"

Advanced PE Conditions:

import "pe"

condition:
    pe.is_pe and
    
    // Entropy analysis (high entropy suggests packing)
    pe.sections[pe.section_index(".text")].raw_data_size > 0 and
    
    // Import table analysis
    pe.number_of_imports < 10 and  // Few imports (packed?)
    
    // Section characteristics
    for any section in pe.sections : (
        section.characteristics & pe.SECTION_MEM_EXECUTE and
        section.characteristics & pe.SECTION_MEM_WRITE
    ) and
    
    // Overlay detection
    pe.overlay.size > 0

ELF Module Integration

ELF File Analysis:

import "elf"

condition:
    elf.type == elf.ET_EXEC and        // Executable file
    elf.machine == elf.EM_X86_64 and   // x64 architecture
    elf.entry_point != 0 and           // Valid entry point
    
    // Section analysis
    for any section in elf.sections : (
        section.name == ".text" and
        section.size > 1000
    )

Performance Optimization

Writing Efficient Rules

String Optimization:

// GOOD: Specific, unique strings
strings:
    $unique_string = "very_specific_malware_identifier_12345"
    $specific_hex = { A1 B2 C3 D4 E5 F6 07 08 09 0A }

// BAD: Common, generic strings
strings:
    $common = "the"                    // Too common
    $short_hex = { 90 90 }            // Too short, very common

Condition Optimization:

// GOOD: Most restrictive conditions first
condition:
    filesize < 10MB and               // Quick file size check first
    pe.is_pe and                      // Fast PE validation
    $rare_string and                  // Unique string check
    any of ($common_string*)          // Broader checks last

// BAD: Expensive operations first
condition:
    for all section in pe.sections : ( // Expensive loop first
        section.size > 1000
    ) and
    filesize < 10MB                   // Should be first

Rule Compilation

Compiling Rules:

# Compile rules for better performance
yarac rules.yar compiled_rules.yarc

# Use compiled rules
yara compiled_rules.yarc target_file.exe

# Compile multiple rule files
yarac rule1.yar rule2.yar rule3.yar compiled.yarc

Performance Testing:

# Test rule performance
yara -p 4 rules.yar large_file.bin      # Use 4 threads
yara -r rules.yar directory/             # Recursive scanning
time yara rules.yar test_files/*         # Measure execution time

Scanning Optimization

Command Line Options:

# Performance optimization flags
yara -f                               # Fast matching (less accuracy)
yara -s                               # Print matching strings
yara -p 8                            # Use 8 threads
yara -l 100                          # Limit matches per rule
yara -t 30                           # 30 second timeout
yara --max-strings-per-rule=50       # Limit strings per rule

Detecting Malware Families

Emotet Detection Rule

rule Emotet_Banker_Variant
{
    meta:
        author = "Malware Analysis Team"
        description = "Detects Emotet banking trojan variants"
        date = "2024-10-07"
        family = "Emotet"
        severity = "high"
        reference = "https://any.run/malware-trends/emotet"
        
    strings:
        // API calls commonly used by Emotet
        $api1 = "CryptStringToBinaryA" ascii
        $api2 = "InternetOpenUrlA" ascii
        $api3 = "GetSystemDirectoryA" ascii
        
        // Base64 encoded strings (common in Emotet)
        $b64_1 = /[A-Za-z0-9+\/]{100,}={0,2}/ ascii
        
        // Registry persistence
        $reg1 = "Software\\Microsoft\\Windows\\CurrentVersion\\Run" ascii
        $reg2 = "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run" ascii
        
        // Network indicators
        $url_pattern = /https?:\/\/[a-zA-Z0-9.-]+\/[a-zA-Z0-9\/_-]+\.php/ ascii
        
        // Hex patterns from Emotet samples
        $hex1 = { 8B 45 ?? 83 C0 04 89 45 ?? 8B 4D ?? 3B 4D ?? 73 }
        $hex2 = { 6A 40 68 00 30 00 00 68 ?? ?? ?? ?? 6A 00 FF 15 }
        
    condition:
        pe.is_pe and
        filesize > 100KB and filesize < 5MB and
        
        // Must have API calls and either registry or network indicators
        2 of ($api*) and
        (
            any of ($reg*) or
            $url_pattern or
            $b64_1
        ) and
        
        // Hex patterns provide additional confidence
        any of ($hex*)
}

Cobalt Strike Detection

rule CobaltStrike_Beacon
{
    meta:
        author = "Threat Hunter Team"
        description = "Detects Cobalt Strike Beacon payloads"
        date = "2024-10-07"
        family = "Cobalt Strike"
        severity = "critical"
        
    strings:
        // Cobalt Strike strings
        $cs1 = "beacon.dll" ascii nocase
        $cs2 = "ReflectiveLoader" ascii
        $cs3 = "%02d/%02d/%02d %02d:%02d:%02d" ascii
        $cs4 = "StartServiceCtrlDispatcher" ascii
        
        // Malleable C2 default strings
        $mall1 = "__cfduid=" ascii
        $mall2 = "Mozilla/5.0 (compatible; MSIE" ascii
        $mall3 = "SESSIONID=" ascii
        
        // Common Beacon configuration markers
        $cfg1 = { 00 01 00 01 00 02 ?? ?? 00 02 ?? ?? }  // Config header
        $cfg2 = { 69 68 69 68 69 6B }                    // XOR key pattern
        
        // Shellcode patterns
        $shell1 = { FC 48 83 E4 F0 E8 C0 00 00 00 41 51 41 50 52 }  // x64 shellcode
        $shell2 = { FC E8 82 00 00 00 60 89 E5 31 C0 64 8B 50 30 }  // x86 shellcode
        
    condition:
        (
            // PE file with CS indicators
            pe.is_pe and (
                2 of ($cs*) or
                any of ($mall*) or
                any of ($cfg*)
            )
        ) or
        (
            // Shellcode detection
            any of ($shell*) and
            filesize < 1MB
        )
}

APT Campaign Detection

rule APT_Lazarus_Group
{
    meta:
        author = "APT Research Team"
        description = "Detects tools associated with Lazarus Group (APT38)"
        date = "2024-10-07"
        family = "Lazarus"
        severity = "critical"
        reference = "MITRE ATT&CK: G0032"
        
    strings:
        // Known Lazarus strings
        $laz1 = "Global\\DSSENH_" ascii
        $laz2 = "abcdefghijklmnopqrstuvwxyz012345" ascii
        $laz3 = "bigdata_hashing" ascii
        
        // File paths used by Lazarus
        $path1 = "\\System32\\IME\\" ascii
        $path2 = "\\Microsoft\\Windows\\IME\\" ascii
        $path3 = "\\Users\\Public\\Downloads\\" ascii
        
        // Network patterns
        $net1 = /http:\/\/[a-z0-9]{8,12}\.com\/[a-z0-9]{6,8}/ ascii
        $net2 = "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36" ascii
        
        // Cryptographic constants
        $crypto1 = { 67 45 23 01 EF CD AB 89 98 BA DC FE 10 32 54 76 }
        $crypto2 = { 01 23 45 67 89 AB CD EF FE DC BA 98 76 54 32 10 }
        
        // PE characteristics
        $pe_cert = "Wemade Entertainment co.,Ltd" ascii
        $pe_cert2 = "Neowiz" ascii
        
    condition:
        pe.is_pe and
        (
            // Multiple Lazarus indicators
            2 of ($laz*) or
            
            // File path patterns with network activity
            (any of ($path*) and any of ($net*)) or
            
            // Crypto patterns (custom encryption)
            any of ($crypto*) or
            
            // Known compromised certificates
            pe.signatures[0].issuer contains any of ($pe_cert*)
        ) and
        
        // Size constraints
        filesize > 50KB and filesize < 10MB
}

Tool Integration

Python Integration

Basic Python Usage:

import yara
import os

# Compile rules from string
rule_source = '''
rule TestRule {
    strings:
        $test = "malware"
    condition:
        $test
}
'''

rules = yara.compile(source=rule_source)

# Scan a file
matches = rules.match('suspicious_file.exe')
for match in matches:
    print(f"Rule matched: {match.rule}")
    for string in match.strings:
        print(f"  String: {string.identifier} at offset {string.instances[0].offset}")

# Compile rules from file
rules = yara.compile(filepath='rules.yar')

# Scan directory recursively
def scan_directory(path, rules):
    for root, dirs, files in os.walk(path):
        for file in files:
            file_path = os.path.join(root, file)
            try:
                matches = rules.match(file_path)
                if matches:
                    print(f"Matches found in {file_path}:")
                    for match in matches:
                        print(f"  - {match.rule}")
            except yara.Error as e:
                print(f"Error scanning {file_path}: {e}")

scan_directory('/path/to/scan', rules)

Advanced Python Features:

import yara
import hashlib

# Custom callback for matches
def match_callback(data):
    """Custom callback to process matches"""
    print(f"Match found: {data['rule']} in {data['filename']}")
    for string in data['strings']:
        print(f"  String: {string['identifier']} at {string['offset']}")
    return yara.CALLBACK_CONTINUE

# External variables
external_vars = {
    'filename': 'suspicious.exe',
    'extension': '.exe'
}

rules = yara.compile(source='''
rule ExternalVarExample {
    condition:
        filename contains "suspicious" and
        extension == ".exe"
}
''', externals=external_vars)

# Process memory scanning
import psutil

def scan_process_memory(pid):
    """Scan process memory with YARA"""
    try:
        process = psutil.Process(pid)
        # This is a simplified example
        # Real implementation would need to handle memory mapping
        memory_data = process.memory_info()
        # Scan memory regions...
        pass
    except psutil.NoSuchProcess:
        print(f"Process {pid} not found")

VirusTotal Integration

VT Intelligence Queries:

# Search VirusTotal with YARA rules
# Example query on VT Intelligence:

# Search for files matching your rule
yara:your_rule_name

# Combine with other metadata
yara:emotet_detection and type:peexe and size:500KB+

# Search for specific strings in files
content:"specific_malware_string" and type:peexe

# Find files with similar patterns
similar-to:hash_of_known_sample and yara:family_detection

SIEM Integration

Splunk Integration:

# Splunk app for YARA scanning
# Custom command example:

| inputlookup files_to_scan.csv 
| eval yara_scan=yara_command(file_path, "rules.yar")
| where match(yara_scan, "malware_family")
| stats count by yara_scan, file_path
| sort -count

ELK Stack Integration:

# Logstash configuration for YARA scanning
filter {
  if [file_path] {
    ruby {
      code => '
        require "yara"
        rules = Yara.compile(filepath: "/etc/yara/rules.yar")
        matches = rules.match(event.get("file_path"))
        if matches.any?
          event.set("yara_matches", matches.map(&:rule))
        end
      '
    }
  }
}

Best Practices

Rule Writing Guidelines

Naming Conventions:

// GOOD: Descriptive names
rule Emotet_Banking_Trojan_2024
rule APT29_Cozy_Bear_Loader
rule Ransomware_Ryuk_Variant_v3

// BAD: Generic names  
rule Malware1
rule BadStuff
rule Test

String Selection:

// GOOD: Unique, specific strings
strings:
    $unique_api = "SpecialMalwareFunction" ascii
    $error_msg = "Error: Cannot connect to C2 server xyz123" ascii
    $specific_hex = { A1 B2 C3 D4 E5 F6 [4-8] 09 0A 0B 0C }

// BAD: Common strings that cause false positives
strings:
    $common = "kernel32.dll" ascii       // Too common
    $generic = "error" ascii             // Too generic
    $short = { 90 90 }                   // Too short

Performance Guidelines

Condition Optimization:

// GOOD: Fast conditions first
condition:
    filesize < 5MB and                   // Quick check
    pe.is_pe and                         // Fast validation
    pe.number_of_sections < 10 and       // Simple comparison
    $specific_string and                 // Unique string
    2 of ($api_calls*)                   // String counting

// BAD: Expensive operations first
condition:
    for all section in pe.sections : (   // Expensive loop
        section.entropy > 7.0
    ) and
    filesize < 5MB                       // Should be first

Testing and Validation

Rule Testing Framework:

#!/bin/bash
# YARA rule testing script

RULE_FILE="new_rule.yar"
POSITIVE_SAMPLES="test_samples/positive/"
NEGATIVE_SAMPLES="test_samples/negative/"

echo "Testing rule: $RULE_FILE"

# Test positive samples (should match)
echo "Testing positive samples..."
for file in $POSITIVE_SAMPLES*; do
    result=$(yara $RULE_FILE "$file")
    if [ -z "$result" ]; then
        echo "FAIL: $file should match but doesn't"
    else
        echo "PASS: $file matches as expected"
    fi
done

# Test negative samples (should not match)
echo "Testing negative samples..."
for file in $NEGATIVE_SAMPLES*; do
    result=$(yara $RULE_FILE "$file")
    if [ -n "$result" ]; then
        echo "FAIL: $file shouldn't match but does"
    else
        echo "PASS: $file doesn't match as expected"
    fi
done

Documentation Standards

Comprehensive Metadata:

rule Comprehensive_Example
{
    meta:
        // Required fields
        author = "Analyst Name "
        description = "Detects XYZ malware family variant seen in Campaign ABC"
        date = "2024-10-07"
        version = "1.0"
        
        // Classification
        family = "XYZ"
        severity = "high"  // low, medium, high, critical
        confidence = "high"  // low, medium, high
        
        // Technical details
        hash = "a1b2c3d4e5f6789..."  // Sample hash
        sample = "malware_sample.exe"
        filetype = "PE32"
        
        // References
        reference = "https://blog.analyst.com/xyz-analysis"
        mitre_attack = "T1055"  // Process Injection
        
        // Licensing and sharing
        license = "Apache 2.0"
        tlp = "WHITE"
        
        // Update history
        changelog = "v1.0 - Initial rule creation"
        
    strings:
        // ... string definitions with comments
        
    condition:
        // Well-documented condition logic
        pe.is_pe and
        filesize > 100KB and filesize < 5MB and  // Size constraints
        2 of ($api_calls*) and                   // API usage pattern
        any of ($network_indicators*)            // Network activity
}

Real-World Case Studies

Case Study 1: Emotet Campaign Detection

Scenario:

A new Emotet campaign was spreading through phishing emails with Word documents containing malicious macros.

Challenge:

  • Multiple payload variants
  • Packed executables
  • Changing C2 infrastructure
  • Anti-analysis techniques

YARA Rule Development:

rule Emotet_Campaign_2024_Q4
{
    meta:
        author = "Incident Response Team"
        description = "Detects Emotet campaign from Q4 2024"
        date = "2024-10-07"
        campaign = "Emotet-Q4-2024"
        
    strings:
        // Macro patterns from Word documents
        $macro1 = "CreateObject(\"WScript.Shell\")" ascii nocase
        $macro2 = "powershell.exe -ep bypass" ascii nocase
        
        // PowerShell download patterns
        $ps1 = "DownloadString(" ascii nocase
        $ps2 = "Invoke-Expression" ascii nocase
        $ps3 = "WebClient" ascii nocase
        
        // Binary payload patterns
        $bin1 = { E8 ?? ?? ?? ?? 83 EC 20 53 55 56 57 33 FF }
        $bin2 = "RegSvr32" ascii nocase
        
        // Network patterns
        $net1 = /https?:\/\/[a-z0-9.-]+\/[a-z0-9]{8,12}\.(exe|dll|bin)/ ascii
        
    condition:
        (
            // Document with macro
            (any of ($macro*) and any of ($ps*)) or
            
            // Binary payload
            (pe.is_pe and any of ($bin*) and $net1)
        ) and
        filesize < 10MB
}

Case Study 2: Living-off-the-Land Techniques

Scenario:

Attackers using legitimate Windows tools for malicious purposes.

Detection Strategy:

rule Suspicious_LOLBin_Usage
{
    meta:
        author = "Threat Hunting Team"
        description = "Detects suspicious use of living-off-the-land binaries"
        date = "2024-10-07"
        technique = "T1218"  // Signed Binary Proxy Execution
        
    strings:
        // PowerShell suspicious patterns
        $ps_encoded = "-EncodedCommand" ascii nocase
        $ps_bypass = "-ExecutionPolicy Bypass" ascii nocase
        $ps_hidden = "-WindowStyle Hidden" ascii nocase
        
        // WMI suspicious usage
        $wmi_exec = "wmic process call create" ascii nocase
        $wmi_query = "SELECT * FROM Win32_Process" ascii nocase
        
        // Rundll32 suspicious patterns
        $rundll_js = "rundll32.exe javascript:" ascii nocase
        $rundll_url = "rundll32.exe url.dll" ascii nocase
        
        // Regsvr32 suspicious patterns
        $regsvr_url = "regsvr32 /s /n /u /i:" ascii nocase
        
        // MSBuild suspicious usage
        $msbuild_url = "MSBuild.exe" ascii nocase
        
    condition:
        2 of ($ps_*) or
        any of ($wmi_*) or
        any of ($rundll_*) or
        $regsvr_url or
        ($msbuild_url and filesize < 1MB)
}

Case Study 3: Ransomware Family Classification

Multi-Family Detection:

rule Ransomware_Multi_Family
{
    meta:
        author = "Ransomware Analysis Team"
        description = "Classifies multiple ransomware families"
        date = "2024-10-07"
        
    strings:
        // Ryuk indicators
        $ryuk1 = "RyukReadMe.txt" ascii
        $ryuk2 = "wake up NEO..." ascii
        
        // Maze indicators  
        $maze1 = "DECRYPT-FILES.html" ascii
        $maze2 = "maze-news.com" ascii
        
        // Conti indicators
        $conti1 = "ContiRecover.txt" ascii
        $conti2 = "CONTI_LOG.txt" ascii
        
        // LockBit indicators
        $lockbit1 = "Restore-My-Files.txt" ascii
        $lockbit2 = "LockBit" ascii
        
        // Generic ransomware patterns
        $ransom_note = /all your (files|data) (have been|are) encrypted/i ascii
        $bitcoin_demand = /send.*bitcoin.*to.*address/i ascii
        $extension_change = /\.[a-z]{3,8}$/ ascii
        
    condition:
        pe.is_pe and
        (
            // Specific family detection
            any of ($ryuk*) or
            any of ($maze*) or
            any of ($conti*) or
            any of ($lockbit*) or
            
            // Generic ransomware behavior
            (2 of ($ransom_note, $bitcoin_demand, $extension_change))
        )
}

Advanced Topics

Custom Modules

Creating Custom YARA Modules:

// Example: Custom module for network analysis
import "network_module"

rule Custom_Network_Analysis
{
    condition:
        network_module.has_suspicious_tld() and
        network_module.domain_generation_algorithm_detected() and
        network_module.unusual_port_usage()
}

Machine Learning Integration

ML-Enhanced Rules:

import "ml_module"

rule ML_Enhanced_Detection
{
    meta:
        description = "Uses ML model for enhanced detection"
        
    condition:
        pe.is_pe and
        ml_module.malware_probability() > 0.8 and
        ml_module.family_classification() == "emotet"
}

Conclusion

YARA is an incredibly powerful tool for malware detection and classification. Mastering YARA requires understanding not just the syntax, but also the nuances of malware behavior, performance optimization, and integration with other security tools.

The key to effective YARA rules is balancing specificity with generalization, ensuring high detection rates while minimizing false positives. Regular testing, validation, and updates are essential for maintaining effective detection capabilities.

As the threat landscape continues to evolve, YARA remains an essential tool in the security analyst's toolkit, providing flexible and powerful pattern matching capabilities for both automated and manual analysis workflows.

Additional Resources

Remember to test rules thoroughly and consider privacy and legal implications when scanning files and systems.