Cybersecurity Analyst · HX, UK
I turn noisy alerts into clear answers. I triage, investigate, and write the detection that catches it faster next time.
Open to Tier 1 and Tier 2 SOC roles
How I work an alert
Read the alert, check severity and asset value, and decide in minutes whether it needs a deeper look.
Pull the logs, build the timeline, and test each explanation until one fits the evidence.
Recommend containment, document indicators, and escalate with everything the next tier needs.
Write or tune a rule so the same behaviour is caught earlier and with less noise.
Case files
Each case follows one alert from first triage to verdict. Open a case to see what fired, what I checked, and how it ended.
A reported invoice email led to a fake Microsoft 365 login page hosted on a newly registered domain.
Sysmon showed winword.exe spawning PowerShell with a Base64 command that downloaded a second stage.
Sign-ins from two countries ten minutes apart looked like account takeover. The evidence said otherwise.
Long, high-entropy subdomain queries to a single domain stood out against normal traffic.
No cases under this topic yet.
Detection engineering
Each rule comes out of a case above. Replace these samples with rules you have tested in your own lab.
From case 002. Any platform that reads Sigma.
title: Office Application Spawning PowerShell
status: experimental
logsource:
category: process_creation
product: windows
detection:
selection:
ParentImage|endswith:
- '\winword.exe'
- '\excel.exe'
- '\powerpnt.exe'
Image|endswith:
- '\powershell.exe'
- '\pwsh.exe'
condition: selection
falsepositives:
- Legitimate macros run by finance or IT teams
level: high
tags:
- attack.execution
- attack.t1059.001
Possible password spraying. Microsoft Sentinel.
SigninLogs
| where TimeGenerated > ago(1h)
| summarize Failures = countif(ResultType != "0"),
Successes = countif(ResultType == "0")
by UserPrincipalName, IPAddress
| where Failures >= 10 and Successes >= 1
| order by Failures desc
From case 004. Possible DNS tunnelling. Splunk.
index=dns sourcetype=stream:dns
| eval qlen=len(query)
| where qlen > 60
| rex field=query "(?<domain>[^.]+\.[^.]+)$"
| stats count dc(query) as unique_queries by src_ip, domain
| where unique_queries > 100
| sort - unique_queries
MITRE ATT&CK
Every technique here links back to a case number, so the coverage is backed by work you can read.
Toolkit
Home lab
An isolated virtual network where I run attacks, collect the logs, and test every rule before it goes on this site.
Atomic Red Team tests and manual techniques
Sysmon and auditd on every host
Endpoint, DNS, firewall, and sign-in logs
Alerts feed the cases on this page