Lion Cipher Lab
Normalize three log sources into OCSF from scratch (the layer under your SIEM)
- SOC Analyst / Blue Team
- Advanced
- ~100 min
- Free · no account required
Why it matters
Every security team eventually hits the same wall, and almost nobody names it correctly. You have SSH logs from Linux boxes, Security events from Windows domain controllers, and sign-in records from a cloud provider. Each one records the exact same real-world act — a person tried to authenticate and failed — and each one describes it in a completely different vocabulary: sshd writes a line of English prose with the username buried in the middle and no year and no timezone; Windows emits event 4625 with a field called TargetUserName, an uppercase account name, a domain in a separate field, and…
What you'll learn
- what schema normalization is, and why it is a discipline rather than a parsing chore
- the OCSF Authentication class [3002]: category_uid, class_uid, activity_id, severity_id, status_id, time, metadata, actor/user, src_endpoint
- computing type_uid the way OCSF requires it: class_uid * 100 + activity_id (3002 * 100 + 1 = 300201)
- hand-mapping three genuinely different sources -- OpenSSH syslog, Windows Security 4625 JSON, AWS CloudTrail ConsoleLogin -- into one class
- using `unmapped` for source fields the class has no home for, and `raw_data` for the verbatim original
- measuring the payoff: a cross-source question that is unanswerable BEFORE and answered by ONE query AFTER
- failure mode: lossy mapping -- a dropped field returns 0, which reads like 'no attacks' rather than 'no data'
- failure mode: enum drift -- why metadata.version must be pinned, and what a renumbered activity_id does to a hardcoded rule
- failure mode: time normalization -- a source in local time producing a false ordering and a shrunken correlation window
- failure mode: over-normalization -- why 'invalid user' and 'bad password' collapse to the same OCSF tuple, and why raw_data stays
Steps in this lab
- The problem, in one sentence — and the words you need
- Open a terminal and check Python (zero-assumption setup)
- Meet the three raw sources, and why they cannot talk to each other
- Write mkcorpus.py — synthesize the three raw sources
- The OCSF Authentication class [3002] and the type_uid arithmetic
- Write ocsf_pipeline.py — the parsers, the mappers, the query and the failure-mode harness
- Run it — and read the BEFORE/AFTER measurement
- Look at one normalized event, field by field
- Failure mode (a): lossy mapping — the field you quietly dropped
- Failure mode (b): enum drift — why metadata.version is not decoration
- Failure mode (c): time normalization — how local time forges a timeline
- Failure mode (d): over-normalization — the schema is a summary, not the evidence
- Why this, not that: OCSF vs ECS vs Splunk CIM vs just grepping the raw text
- When NOT to normalize
- How a pro does it differently: unit-test the mappings