Threat Hunting
Threat hunting identifies malicious activity that evades automated detection by applying human analysis to security telemetry. This task covers the complete hunt cycle from hypothesis formation through investigation, documentation, and conversion of findings into persistent detections. Execute hunts on a scheduled cadence and in response to new threat intelligence relevant to your organisation’s risk profile.
Prerequisites
| Requirement | Detail |
|---|---|
| Access | Read access to SIEM, EDR console, and log aggregation platform |
| Permissions | Query execution rights; incident creation rights if findings warrant |
| Tools | SIEM query interface, EDR console, spreadsheet for tracking |
| Information | Current threat intelligence reports, asset inventory, network topology |
| Time | 4-8 hours per hunt cycle |
Verify SIEM query access before beginning:
-- Test query to confirm access (Wazuh example)SELECT agent.name, rule.description, @timestampFROM wazuh-alerts-*WHERE @timestamp > now() - 1hLIMIT 10Expected output shows recent alerts with agent names. If the query returns a permissions error, request the wazuh_read role from your identity provider administrator.
For EDR access, confirm you can view endpoint telemetry:
# Wazuh agent list verification/var/ossec/bin/agent_control -lExpected output lists managed agents with status. A count of zero agents indicates configuration issues with the EDR deployment rather than permission problems.
Procedure
Phase 1: Hypothesis Development
Select a threat behaviour to investigate based on current intelligence or organisational risk. Good hypotheses are specific, testable, and grounded in attacker tradecraft. The hypothesis “Attackers may have compromised our network” is too vague. The hypothesis “An attacker with access to Office 365 credentials is forwarding email to an external address for exfiltration” is specific and testable.
Structure your hypothesis using this format:
HYPOTHESIS: [Actor type] is [technique] to [objective]DATA SOURCES: [Logs/telemetry required]INDICATORS: [Observable evidence if true]TIMEFRAME: [Investigation period]Example hypothesis document:
HYPOTHESIS: An external attacker is using compromised credentialsto access SharePoint and download sensitive documentsoutside business hoursDATA SOURCES: Azure AD sign-in logs, SharePoint audit logs,Conditional Access logsINDICATORS: - Sign-ins from unusual locations- Large volume downloads (>50 files/session)- Access between 22:00-06:00 local time- Sign-ins without MFA challengeTIMEFRAME: Past 30 daysMap the hypothesis to MITRE ATT&CK techniques to standardise documentation and enable cross-referencing with threat intelligence. For the SharePoint exfiltration hypothesis, relevant techniques include T1078 (Valid Accounts), T1213 (Data from Information Repositories), and T1567 (Exfiltration Over Web Service).
Document the mapping:
MITRE ATT&CK MAPPING:- T1078.004: Cloud Accounts- T1213.002: SharePoint- T1567.002: Exfiltration to Cloud StorageIdentify required data sources and verify availability. Each hypothesis requires specific telemetry. Absent or incomplete data sources limit what you can detect.
+------------------------------------------------------------------+| DATA SOURCE VERIFICATION |+------------------------------------------------------------------+| || Hypothesis: SharePoint credential abuse || || +------------------------+ +------------------------+ || | Azure AD Sign-in Logs | | SharePoint Audit Logs | || | | | | || | Required fields: | | Required fields: | || | - userPrincipalName | | - UserId | || | - ipAddress | | - Operation | || | - location | | - ObjectId | || | - createdDateTime | | - CreationTime | || | - status | | - ClientIP | || | | | | || | Retention: 30 days | | Retention: 90 days | || | Status: AVAILABLE | | Status: AVAILABLE | || +------------------------+ +------------------------+ || || +------------------------+ || | Conditional Access | || | | || | Required fields: | || | - policyName | || | - result | || | - conditions | || | | || | Retention: 30 days | || | Status: AVAILABLE | || +------------------------+ || |+------------------------------------------------------------------+Figure 1: Data source verification for SharePoint credential abuse hunt
Query to verify Azure AD log ingestion:
-- Check Azure AD log availability (30-day window)SELECT COUNT(*) as event_count,MIN(createdDateTime) as earliest,MAX(createdDateTime) as latestFROM azure_signin_logsWHERE createdDateTime > now() - 30dExpected output shows event count in thousands with timestamps spanning the full period. Gaps in the timestamp range indicate log ingestion failures.
Phase 2: Investigation
Execute baseline queries to understand normal patterns before searching for anomalies. Establish what typical behaviour looks like for the activity you are investigating.
For the SharePoint example, first establish baseline access patterns:
-- Baseline: Normal SharePoint access hours by userSELECT userPrincipalName,HOUR(createdDateTime) as access_hour,COUNT(*) as access_countFROM sharepoint_auditWHERE CreationTime > now() - 30dAND Operation = 'FileDownloaded'GROUP BY userPrincipalName, HOUR(createdDateTime)ORDER BY userPrincipalName, access_hourDocument baseline findings before proceeding:
BASELINE FINDINGS:- 94% of file downloads occur 08:00-18:00 local time- Average downloads per user per day: 12- Maximum downloads per user per day: 87 (finance team)- Users with after-hours access: 23 (identified roles)Execute detection queries targeting the specific indicators from your hypothesis. Start with broad queries and narrow based on results.
Query for after-hours SharePoint access:
-- Detection: After-hours file downloadsSELECT UserId,ClientIP,ObjectId,CreationTime,OperationFROM sharepoint_auditWHERE CreationTime > now() - 30dAND Operation = 'FileDownloaded'AND (HOUR(CreationTime) < 6 OR HOUR(CreationTime) > 22)ORDER BY CreationTime DESCQuery for high-volume download sessions:
-- Detection: High-volume download sessions (>50 files in 1 hour)SELECT UserId,ClientIP,DATE_TRUNC('hour', CreationTime) as session_hour,COUNT(*) as download_countFROM sharepoint_auditWHERE CreationTime > now() - 30dAND Operation = 'FileDownloaded'GROUP BY UserId, ClientIP, DATE_TRUNC('hour', CreationTime)HAVING COUNT(*) > 50ORDER BY download_count DESCQuery for sign-ins from unusual locations:
-- Detection: Sign-ins from countries not in approved listSELECT userPrincipalName,ipAddress,location.countryOrRegion,createdDateTime,status.errorCodeFROM azure_signin_logsWHERE createdDateTime > now() - 30dAND location.countryOrRegion NOT IN ('GB', 'KE', 'UG', 'ET', 'JO')AND status.errorCode = 0 -- Successful sign-ins onlyORDER BY createdDateTime DESCCorrelate findings across data sources. A single anomalous event requires context from related telemetry to determine whether it represents malicious activity or legitimate behaviour.
+------------------------------------------------------------------+| CORRELATION WORKFLOW |+------------------------------------------------------------------+| || SHAREPOINT ANOMALY AZURE AD CONTEXT || +-------------------+ +-------------------+ || | UserId: jsmith | | Same user: | || | Time: 02:15 UTC +------>| Sign-in: 02:10 | || | Files: 156 | | Location: Lagos | || | ClientIP: x.x.x.x | | MFA: Not required | || +-------------------+ +-------------------+ || | | || | | || v v || +-------------------+ +-------------------+ || | EDR CONTEXT | | HISTORICAL | || | | | | || | No endpoint | | Prior sign-ins: | || | associated with | | All from London | || | this IP | | MFA always used | || +-------------------+ +-------------------+ || | | || +-------------+-------------+ || | || v || +----------------------------+ || | ASSESSMENT: HIGH SUSPICION | || | - Unusual location | || | - No MFA challenge | || | - Bulk download | || | - Outside business hours | || +----------------------------+ || |+------------------------------------------------------------------+Figure 2: Cross-source correlation for suspicious SharePoint activity
Correlation query joining Azure AD and SharePoint data:
-- Correlate SharePoint downloads with Azure AD sign-insSELECT sp.UserId,sp.ClientIP,sp.CreationTime as download_time,sp.ObjectId as file_path,az.location.countryOrRegion as signin_country,az.conditionalAccessStatus,az.mfaDetail.authMethodFROM sharepoint_audit spJOIN azure_signin_logs azON sp.UserId = az.userPrincipalNameAND az.createdDateTime BETWEEN sp.CreationTime - INTERVAL '1 hour'AND sp.CreationTimeWHERE sp.CreationTime > now() - 30dAND sp.Operation = 'FileDownloaded'AND (HOUR(sp.CreationTime) < 6 OR HOUR(sp.CreationTime) > 22)ORDER BY sp.CreationTime DESCInvestigate each finding to classify as true positive, false positive, or requiring further investigation. Document your analysis for each finding.
Classification criteria:
Finding characteristics Classification Action Matches known malicious indicators, no legitimate explanation True positive Escalate to incident response Matches indicators but legitimate business reason confirmed False positive Document, tune detection Ambiguous, requires additional context Requires investigation Gather additional data For each finding, document:
FINDING #1----------User: jsmith@example.orgEvent: 156 file downloads, 02:15 UTC, IP: 102.89.xx.xx (Lagos)INVESTIGATION:- Contacted user's manager: User not travelling- Checked HR system: User based in London office- Reviewed prior 90 days: No prior access from Nigeria- Checked approved travel: No travel authorisationCLASSIFICATION: TRUE POSITIVE - Credential compromise suspectedACTION: Escalate to incident responseREFERENCE: INC-2024-0892
Phase 3: Documentation and Handoff
Document all findings regardless of classification. False positives inform detection tuning. Ambiguous findings may become relevant in future investigations.
Create a hunt report with the following structure:
THREAT HUNT REPORT==================Hunt ID: TH-2024-047Analyst: [Your name]Date: 2024-11-16Duration: 6 hoursHYPOTHESIS----------An external attacker is using compromised credentials to accessSharePoint and download sensitive documents outside business hours.MITRE ATT&CK: T1078.004, T1213.002, T1567.002DATA SOURCES USED------------------ Azure AD sign-in logs (30 days)- SharePoint audit logs (30 days)- Conditional Access logs (30 days)QUERIES EXECUTED----------------[Include all queries with results summary]FINDINGS--------Total findings: 3True positives: 1False positives: 1Requires investigation: 1[Detailed finding documentation]DETECTION OPPORTUNITIES-----------------------Based on this hunt, the following detections should be created:1. Alert on sign-ins from non-approved countries followed bySharePoint access (priority: HIGH)2. Alert on >50 file downloads in 1-hour window (priority: MEDIUM)RECOMMENDATIONS---------------1. Enable Conditional Access policy to require MFA forall SharePoint access regardless of location2. Implement geographic access restrictions for high-riskdocument librariesConvert validated findings into persistent detections. Each true positive represents a detection gap that should be closed to catch similar activity automatically in future.
+------------------------------------------------------------------+| HYPOTHESIS-TO-DETECTION PIPELINE |+------------------------------------------------------------------+| || HUNT FINDING DETECTION RULE || +------------------+ +------------------+ || | True positive: | | Rule name: | || | Credential abuse +------->| Suspicious | || | from unusual | | SharePoint | || | location | | access pattern | || +------------------+ +------------------+ || | || v || +------------------+ || | TESTING | || | | || | - Backtest 90d | || | - FP rate check | || | - Threshold tune | || +------------------+ || | || v || +------------------+ || | PRODUCTION | || | | || | - Deploy rule | || | - Set severity | || | - Assign owner | || +------------------+ || |+------------------------------------------------------------------+Figure 3: Converting hunt findings to production detections
Example SIEM detection rule (Wazuh format):
<rule id="100201" level="10"><if_sid>87801</if_sid><field name="azure.signin.location.countryOrRegion">^(?!GB|KE|UG|ET|JO)</field><field name="azure.signin.status.errorCode">0</field><description>Successful sign-in from non-approved country: $(azure.signin.location.countryOrRegion)</description><group>authentication,credential_abuse,T1078</group></rule>Example correlation rule for SharePoint after sign-in:
<rule id="100202" level="12"><if_matched_sid>100201</if_matched_sid><field name="sharepoint.operation">FileDownloaded</field><timeframe>3600</timeframe><description>SharePoint file download following sign-in from non-approved country</description><group>exfiltration,T1213</group></rule>Hand off true positive findings to incident response if they represent active threats. Provide the incident handler with:
- Complete finding documentation
- All relevant log entries
- User and asset context
- Your preliminary assessment
- Queries for further investigation
Create an incident referral:
INCIDENT REFERRAL=================From: Threat Hunting (TH-2024-047)To: Incident ResponsePriority: HIGHSUMMARY-------Suspected credential compromise for user jsmith@example.org.Evidence of bulk file exfiltration from SharePoint.EVIDENCE--------- Sign-in from Lagos, Nigeria at 02:10 UTC (user based London)- No MFA challenge (Conditional Access gap)- 156 files downloaded from /sites/finance/ at 02:15-02:47 UTC- No prior history of Nigeria access- User's manager confirms no travelRECOMMENDED ACTIONS-------------------1. Suspend user account pending investigation2. Revoke active sessions3. Review downloaded files for sensitivity4. Investigate source IP for other activityATTACHED--------- Full query results (CSV)- Timeline of events- User profile from HR system
Variants
Minimal Capacity Hunt
Organisations without dedicated security staff can conduct simplified hunts using built-in cloud platform capabilities.
For Microsoft 365 environments, use the built-in Advanced Hunting in Microsoft Defender:
// Simplified hunt: Unusual sign-in locationsSigninLogs| where TimeGenerated > ago(30d)| where ResultType == 0| summarize Countries = make_set(LocationDetails.countryOrRegion), SigninCount = count() by UserPrincipalName| where array_length(Countries) > 3| order by SigninCount descFor Google Workspace, use the Admin Console investigation tool with these search parameters:
- Event: Login
- Time range: Last 30 days
- Filter: Suspicious login = Yes
Document findings in a spreadsheet if no formal case management exists.
Intelligence-Driven Hunt
When threat intelligence indicates a specific actor or campaign targeting your sector, structure the hunt around known indicators and techniques.
- Extract indicators from the intelligence report
- Map techniques to data sources you have available
- Query for both exact indicator matches and behavioural patterns
- Document negative findings (absence of indicators is useful intelligence)
Example hunt for a known credential phishing campaign:
-- Search for known phishing infrastructureSELECT srcip, dstip, url, timestampFROM web_proxy_logsWHERE timestamp > now() - 14d AND ( domain LIKE '%login-microsoftonline%' OR domain LIKE '%sharepoint-auth%' OR domain IN ('malicious-domain-1.com', 'malicious-domain-2.net') )Automated Hunt Queries
Schedule recurring queries for high-priority hypotheses that should run continuously rather than periodically.
# Example scheduled hunt configuration (Wazuh)scheduled_hunts: - name: "Impossible travel detection" schedule: "0 */4 * * *" # Every 4 hours query: | SELECT userPrincipalName, location1, location2, time1, time2, distance_km, required_hours, actual_hours FROM ( -- Subquery calculating travel feasibility ) WHERE actual_hours < required_hours threshold: 1 action: create_alert
- name: "Service account interactive login" schedule: "0 * * * *" # Hourly query: | SELECT userPrincipalName, ipAddress, appDisplayName FROM azure_signin_logs WHERE userPrincipalName LIKE 'svc-%' AND appDisplayName NOT IN ('Approved App 1', 'Approved App 2') threshold: 1 action: create_alertVerification
Confirm hunt completion by verifying each phase produced expected outputs:
HUNT COMPLETION CHECKLIST=========================
Phase 1: Hypothesis[ ] Hypothesis documented with ATT&CK mapping[ ] Data sources identified and availability confirmed[ ] Investigation timeframe defined
Phase 2: Investigation[ ] Baseline queries executed and documented[ ] Detection queries executed[ ] All findings correlated across sources[ ] Each finding classified (TP/FP/needs investigation)
Phase 3: Documentation[ ] Hunt report completed[ ] Queries saved for reuse[ ] Detection rules drafted for true positives[ ] Incidents created for active threatsVerify detection rules before production deployment:
-- Backtest new detection against historical dataSELECT COUNT(*) as alert_count, COUNT(DISTINCT userPrincipalName) as unique_usersFROM azure_signin_logsWHERE createdDateTime > now() - 90d AND location.countryOrRegion NOT IN ('GB', 'KE', 'UG', 'ET', 'JO') AND status.errorCode = 0Expected output provides baseline alert volume. More than 50 alerts per day indicates the detection threshold needs tuning to reduce analyst burden.
Troubleshooting
| Symptom | Cause | Resolution |
|---|---|---|
| Query returns no results when data should exist | Field names incorrect for your SIEM platform | Check platform documentation for exact field names; use field discovery query |
| Query timeout after 5+ minutes | Query scanning too much data without filters | Add time range filter first; use indexed fields in WHERE clause |
| Too many false positives in results | Baseline not established; thresholds too low | Run baseline analysis first; increase thresholds based on normal patterns |
| Cannot correlate across data sources | Timestamps not aligned; user identifiers differ | Normalise timestamps to UTC; create lookup table for user identifier mapping |
| Log retention insufficient for hunt timeframe | Logs aged out before analysis | Adjust hunt timeframe to available retention; request retention extension for future hunts |
| Hypothesis too broad to investigate | Multiple techniques combined | Split into separate hypotheses; investigate one technique at a time |
| EDR telemetry gaps on some endpoints | Agent not deployed or unhealthy | Query agent status; remediate unhealthy agents before hunting on those endpoints |
| Cannot access required data sources | Permissions not provisioned | Request access through IAM process; document access requirements for future hunters |
| Hunt taking longer than allocated time | Scope creep; following tangential leads | Time-box investigation; document tangential findings for separate future hunts |
| Detection rule generating alerts on historical data | Rule applied retroactively to existing logs | Configure rule with start time; suppress alerts for events before rule creation |
| Findings do not match threat intelligence | Intelligence outdated or not applicable to environment | Validate intelligence recency; confirm techniques are relevant to your technology stack |
| Cannot determine if finding is true or false positive | Insufficient context about user/asset | Engage HR, manager, or asset owner for context; document as “requires investigation” if unresolved |
Hunt Cadence and Prioritisation
Schedule hunts based on threat landscape and available capacity:
| Hunt type | Frequency | Trigger | Priority |
|---|---|---|---|
| Intelligence-driven | Within 48 hours of relevant intelligence | New sector-specific threat report | High |
| Technique-based | Monthly rotation through priority techniques | MITRE ATT&CK coverage plan | Medium |
| Baseline validation | Quarterly | Calendar schedule | Low |
| Post-incident | Within 1 week of significant incident | Incident closure | High |
Maintain a hunt backlog prioritised by:
- Techniques observed in sector-specific attacks
- Techniques with no current detection coverage
- Techniques targeting your highest-value assets
- Techniques newly added to ATT&CK or emerging in threat reports