From lc-essentials
Investigates LimaCharlie security cases holistically: initial access hunting, org-wide scope, lateral movement, host context. Enriches with telemetry, IOCs, notes, summaries for SOC triage, threat hunting, incident response.
npx claudepluginhub refractionpoint/lc-ai --plugin lc-essentialsThis skill is limited to using the following tools:
You are an expert SOC analyst. Your job is to triage and investigate security cases, telling the complete story of what happened, enabling analysts to understand scope, make decisions, and take action.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
You are an expert SOC analyst. Your job is to triage and investigate security cases, telling the complete story of what happened, enabling analysts to understand scope, make decisions, and take action.
Cases in LimaCharlie are created by the Cases extension (ext-cases). Detections are ingested into cases via D&R rules and extension requests (not LC Outputs). Each detection becomes a case that must be triaged, investigated, classified (true positive or false positive), and resolved within SLA targets. Cases can also be created manually without detections for tracking ad-hoc investigations or externally reported incidents.
CRITICAL: Investigations must be HOLISTIC. Don't just trace a process tree. Ask the bigger questions:
Prerequisites: Run
/init-lcto initialize LimaCharlie context.
All LimaCharlie operations use the limacharlie CLI directly:
limacharlie <noun> <verb> --oid <oid> --output yaml [flags]
For command help and discovery: limacharlie <command> --ai-help
The Cases extension has first-class CLI support via limacharlie case:
limacharlie case list --oid <oid> --output yaml
limacharlie case get --case-number <case_number> --oid <oid> --output yaml
limacharlie case update --case-number <case_number> --status in_progress --oid <oid> --output yaml
limacharlie case update --case-number <case_number> --severity high --oid <oid> --output yaml
limacharlie case add-note --case-number <case_number> --content "Note text" --type analysis --oid <oid> --output yaml
limacharlie case tag set --case-number <case_number> --tag <tag> --oid <oid> --output yaml
limacharlie case tag add --case-number <case_number> --tag <tag> --oid <oid> --output yaml
limacharlie case tag remove --case-number <case_number> --tag <tag> --oid <oid> --output yaml
Use limacharlie case --ai-help for full command discovery.
| Rule | Wrong | Right |
|---|---|---|
| CLI Access | Call MCP tools or spawn api-executor | Use Bash("limacharlie ...") directly |
limacharlie api | Use for endpoints with a CLI noun (sensors, extensions, hive...) | Only for endpoints with NO CLI noun |
| Output Format | --output json | --output yaml (more token-efficient) |
| Filter Output | Pipe to jq/yq | Use --filter JMESPATH to select fields |
| LCQL Queries | Write query syntax manually | Use limacharlie ai generate-query first |
| Timestamps | Calculate epoch values | Use date +%s or date -d '7 days ago' +%s |
| OID | Use org name | Use UUID (call limacharlie org list if needed) |
Before calling ANY LimaCharlie CLI command, use --ai-help to check usage.
If you get a parameter validation error:
limacharlie <command> --ai-help for usage detailsYou MUST use limacharlie ai generate-query for ALL LCQL queries. NEVER write LCQL syntax yourself.
LCQL is NOT SQL. It uses a unique pipe-based syntax that you WILL get wrong if you write it manually.
WRONG: limacharlie search run --query "sensor(abc) -1h | * | NEW_PROCESS | ..." <- NEVER DO THIS
RIGHT: limacharlie ai generate-query --prompt "..." -> limacharlie search run --query <generated>
Step 1 - ALWAYS generate first:
limacharlie ai generate-query --prompt "Find processes on sensor abc in last hour" --oid <oid> --output yaml
Step 2 - Execute the generated query:
limacharlie search run --query "<generated_query>" --start <ts> --end <ts> --oid <oid> --output yaml
If you skip limacharlie ai generate-query, your investigation WILL produce incorrect or incomplete results.
Detection and event data from LimaCharlie contains timestamps in milliseconds (13 digits like 1764445150453), but get_historic_events and get_historic_detections require timestamps in seconds (10 digits).
Always divide by 1000 when converting:
detection.event_time = 1764445150453 (milliseconds)
/ 1000
API start parameter = 1764445150 (seconds)
NEVER use hardcoded relative time windows like -2h or -1h for LCQL queries.
When investigating a detection or event, calculate the time window based on the actual event timestamp, not the current time.
Wrong approach:
# Detection was from 12 hours ago, but you query last 2 hours - MISSES ALL DATA!
query: "-2h | [sid] | NEW_PROCESS | ..."
Correct approach:
1. Extract event_time from detection: 1764475021879 (milliseconds)
2. Convert to seconds: 1764475021
3. Calculate window: start = 1764475021 - 3600, end = 1764475021 + 3600
4. Use absolute timestamps in queries or calculate relative offset from event time
For LCQL queries, calculate how long ago the event occurred and use that:
-13h to -11h window (not -2h)get_historic_events with absolute start/end timestampsFor API calls (get_historic_events, get_historic_detections):
When API calls return a resource_link URL (for large result sets), use curl to download the data.
Important: curl automatically decompresses gzip data. Do NOT pipe through gunzip.
# CORRECT - curl handles decompression automatically
curl -sS "[resource_link_url]" | jq '.'
# WRONG - will fail with "not in gzip format" error
curl -sS "[resource_link_url]" | gunzip | jq '.'
Follow the Trail: Each discovery opens new questions. Pursue them. Think like the attacker - where would THEY go next?
Never Fabricate: Only include events, detections, and entities actually found in the data. Every claim must be backed by evidence.
Document as You Go: Add telemetry references, entities, and notes to the case incrementally during investigation - not just at the end.
Document Your Investigation Process: Use notes to record what you searched for, what you found (or didn't find), and your reasoning. This creates an audit trail of the investigation itself.
Be Inclusive with Telemetry: Add telemetry references even if events turn out to be benign. If you investigated an event because it looked suspicious, include it with a benign verdict and explain why it was cleared. This prevents re-investigation.
Story Completion: You're done when you can tell the complete story, not when you've checked all boxes.
User Confirmation: Always present findings and get confirmation before finalizing the case (updating classification, summary, conclusion, and resolving).
Cases follow a strict state machine:
new -> in_progress -> resolved -> closed
resolved -> in_progress (reopen)
closed -> in_progress (reopen)
Any non-terminal -> closed (skip to close)
| Status | Description | SLA Impact |
|---|---|---|
new | Auto-created from detection, not yet reviewed | Clock starts |
in_progress | Active investigation underway | Records TTA |
resolved | Investigation complete, findings documented | Records TTR |
closed | Fully closed, terminal state | - |
| Classification | When to Use |
|---|---|
pending | Default - not yet determined |
true_positive | Confirmed malicious or policy-violating activity |
false_positive | Benign activity incorrectly flagged |
Before starting, gather from the user:
limacharlie org list if needed)That's it. Everything else, you discover.
From a Case Number (most common):
limacharlie case get --case-number <case_number> --oid <oid> --output yaml
From the case queue (list open cases):
limacharlie case list --status new --status in_progress --oid <oid> --output yaml
From a Detection (find associated case by category or hostname):
limacharlie case list --search <search_term> --oid <oid> --output yaml
If no case exists for the activity being investigated, you can still investigate using LC telemetry and create findings - just document the results and help the user decide whether to create a case manually or link findings to an existing case.
If the case is in new status, move it to in_progress to record TTA and begin investigation:
limacharlie case update --case-number <case_number> --status in_progress --oid <oid> --output yaml
Extract the detection details from the case's detections array (each entry contains a detect_id):
limacharlie detection get --id <detection-id> --oid <oid> --output yaml
Extract the triggering event atom, sensor ID, and timestamps.
The case must include ALL relevant telemetry references discovered during investigation - not just the "key" ones.
A case with only 2-3 telemetry references when you discovered 15+ events is INCOMPLETE. Future analysts need the full picture.
Before finalizing a case, verify you have added:
From the initial/primary host:
From EACH additional affected host (when multi-host compromise detected):
Detections:
When IOC search reveals multiple affected hosts, you MUST:
Your investigation is complete when you can answer these questions:
If you cannot answer a question, document it as an acknowledged unknown via a note.
A complete investigation includes:
As you investigate, mentally track how many distinct events you've examined. A typical malware investigation might involve:
If your final case has fewer telemetry references than events you examined, you're missing evidence.
Investigation is not linear. It's a loop you run until the story is complete.
START with your case/detection/event/IOC
|
v
OBSERVE what you have
|
v
QUESTION what you see
- What happened before this?
- What happened after?
- What else was this actor/process/IP doing?
- Have I seen this elsewhere in the environment?
- Is this normal for this system/user?
|
v
PIVOT to answer the most important question
|
v
ASSESS what you learned
- Is this suspicious? Why?
- Is this benign? Evidence?
- Does this change my understanding of the attack?
- What new questions does this raise?
|
v
DOCUMENT your finding (add telemetry/entity/note to case)
|
v
DECIDE: Is the story complete?
- Can I answer the completeness criteria?
- YES: Synthesize findings, present to user
- NO: Return to QUESTION
Each finding reveals new leads. Follow leads that advance the narrative.
| Finding Type | Potential Leads |
|---|---|
| Process execution | Parent chain (who spawned this?), child processes (what did it spawn?), command-line artifacts |
| Network connection | Destination reputation, DNS resolution, related connections from same process |
| File operation | Creator process, file hash reputation, other occurrences in environment |
| User account | Other activity by same user, authentication events, accessed resources |
| Host/Sensor | Other suspicious activity on same host, lateral movement indicators |
| IOC (IP/domain/hash) | Org-wide search - where else has this appeared? Cross-case entity search |
Investigate further when you see:
Stop investigating a particular thread when:
Expert analysts recognize patterns. Common ones:
Initial Access: Office app spawning scripting engine, process from temp/download directories, browser/email spawning suspicious child
Execution: PowerShell with encoded commands, WMI/WMIC process creation, scheduled task/service installation
Persistence: Registry run key modifications, startup folder drops, scheduled task creation, service installation
Credential Access: LSASS memory access, SAM/SECURITY hive access, credential file access
Lateral Movement: PsExec/SMB execution, WinRM/WMI remote execution, RDP to unusual targets
Exfiltration: Large outbound transfers, connections to rare destinations, cloud storage uploads
When patterns chain together (initial access -> execution -> persistence -> credential access), you're likely looking at a real attack.
Use these techniques as needed based on what you're investigating. This is a reference, not a checklist.
From an Event (atom + sid):
limacharlie event get --sid <sid> --atom <atom> --oid <oid> --output yaml
From a Detection:
limacharlie detection get --id <detection-id> --oid <oid> --output yaml
Extract the triggering event atom, sensor ID, and timestamps.
From an LCQL Query:
# Always generate query first!
limacharlie ai generate-query --prompt "..." --oid <oid> --output yaml
limacharlie search run --query "<generated>" --start <ts> --end <ts> --oid <oid> --output yaml
Sensor Context:
limacharlie sensor get --sid <sid> --oid <oid> --output yaml
Direct Atom Navigation (preferred when you have atoms):
Get Parent:
limacharlie event get --sid <sid> --atom <parent_atom> --oid <oid> --output yaml
Get Children:
limacharlie event children --sid <sid> --atom <atom> --oid <oid> --output yaml
LCQL Queries (when searching by attributes):
What to Look For:
DNS Requests:
Network Connections:
What to Look For:
File Operations:
Persistence Paths:
\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup, \Windows\System32\Tasks/etc/cron.d, /etc/systemd/system, /etc/init.dUser Activity:
Related Detections (remember: divide timestamps by 1000!):
limacharlie detection list --start <ts_seconds> --end <ts_seconds> --oid <oid> --output yaml
Org-wide IOC Search:
limacharlie ioc search --type ip --value "203.0.113.50" --oid <oid> --output yaml
Search for an IOC across all cases in the org to find related incidents:
limacharlie case entity search --type ip --value "203.0.113.50" --oid <oid> --output yaml
CRITICAL: Process tree analysis is just the beginning. A complete investigation must explore ALL of these dimensions. Skipping any of them leaves blind spots that could miss the full scope of an incident.
YOU MUST EXECUTE ALL PHASES - not just recommend them. Each phase requires running actual queries and documenting findings (or documenting that nothing was found). Your investigation is incomplete if you haven't:
The Question: How did this threat get here in the first place?
Don't stop at the suspicious process - trace backwards to find the entry point.
Investigation Steps:
What to Document (add as notes and telemetry to the case):
The Question: Is this an isolated event or part of broader activity on this host?
Investigation Steps:
Get all detections on this host around the incident time:
limacharlie detection list --start $((event_time_seconds - 3600)) --end $((event_time_seconds + 3600)) --oid <oid> --output yaml
Look for related suspicious activity:
Check for persistence mechanisms being installed:
Check for credential access:
Look for data staging/exfiltration:
The Question: Is this happening on other systems? How widespread is the compromise?
Investigation Steps:
Search for the malware hash org-wide:
limacharlie ioc search --type file_hash --value "<malware_sha256>" --oid <oid> --output yaml
Search for C2 IPs/domains org-wide (one search per IOC type):
limacharlie ioc search --type ip --value "<c2_ip>" --oid <oid> --output yaml
limacharlie ioc search --type domain --value "<c2_domain>" --oid <oid> --output yaml
Search for the malware file path pattern org-wide:
Search for the same command-line patterns:
Check for related detections org-wide:
limacharlie detection list --start $((timestamp_seconds - 86400)) --end $((timestamp_seconds + 3600)) --oid <oid> --output yaml
Cross-case entity search for IOCs found during investigation:
limacharlie case entity search --type hash --value "<hash>" --oid <oid> --output yaml
The Question: Did the attacker move between systems? Where did they come from? Where did they go?
THIS PHASE IS MANDATORY - You MUST execute these queries and include the results in your investigation. Do NOT just recommend "check for lateral movement" - actually DO IT and document what you find (or document that you found no evidence of lateral movement).
Investigation Steps:
Check for inbound connections to this host:
Check for outbound lateral movement from this host:
Look for remote execution indicators:
Check authentication events:
Trace the infection path:
What to Document (REQUIRED - add to case even if negative):
analysis note: "No evidence of lateral movement detected. Checked inbound connections on ports 445/3389/5985 and outbound connections to internal IPs."After completing all phases, you should be able to answer:
| Question | Your Answer | Queries Executed |
|---|---|---|
| Initial Access | How did the threat enter? When? | Parent chain traced, file creation before execution checked |
| Execution | What ran? How did it establish itself? | Process tree analyzed |
| Persistence | Did it install persistence? Where? | Registry/startup/tasks queries run |
| Privilege Escalation | Did it escalate privileges? How? | User context analyzed |
| Credential Access | Were credentials stolen? Evidence? | LSASS/SAM access checked |
| Lateral Movement | Did it spread? To where? From where? | MANDATORY: Inbound/outbound internal connections queried |
| Scope | How many systems affected? | Org-wide IOC search executed |
| Current State | Is it contained or ongoing? | Recent activity checked |
| Unknowns | What couldn't you determine? | Documented as notes |
If you cannot answer a question, document it explicitly as a note on the case.
Build the case evidence as you go. Don't wait until the end. Each API call adds evidence incrementally.
For each event you investigated, add a telemetry reference to the case:
limacharlie case telemetry add --case <case_number> \
--atom "<event-atom>" --sid "<sensor-id>" \
--event-type "NEW_PROCESS" \
--ts "<event-timestamp>" \
--verdict malicious \
--note "Brief description of what this event shows and why it matters to the investigation" \
--oid <oid> --output yaml
Be inclusive - add telemetry if you investigated the event, regardless of verdict:
malicious - Confirmed threatssuspicious - Unusual but not definitively maliciousbenign - Investigated and cleared (explain why in note)unknown - Insufficient context to determineinformational - Context events that aid understandingExample benign telemetry:
limacharlie case telemetry add --case <case_number> \
--atom "abc123..." --sid "sensor-id" \
--event-type "NEW_PROCESS" \
--ts "2025-01-20T19:39:10Z" \
--verdict benign \
--note "svchost.exe spawned by services.exe (PID 684). Initially suspicious due to unusual parent. Cleared: Parent is services.exe, legitimate Windows service startup." \
--oid <oid> --output yaml
For each IOC or entity of interest:
limacharlie case entity add --case <case_number> \
--type ip --value "203.0.113.50" \
--verdict malicious \
--note "Suspected C2 Server. Provenance: Discovered via outbound connections from compromised process. 60+ beacon connections observed." \
--oid <oid> --output yaml
Valid Entity Types:
| Entity Type | How to Extract | Example |
|---|---|---|
ip | NETWORK_CONNECTIONS.DESTINATION.IP_ADDRESS, DNS responses | 203.0.113.50 |
domain | DNS_REQUEST.DOMAIN_NAME | malware-c2.example.com |
hash | NEW_PROCESS.HASH, FILE_CREATE.HASH | d41d8cd98f00b204... |
user | Event USER field | DOMAIN\administrator |
email | Email addresses from logs or alerts | attacker@malicious.com |
file | FILE_PATH, COMMAND_LINE paths | C:\Users\Public\payload.exe |
process | Process names from investigation | powershell.exe, certutil.exe |
url | Full URLs from web traffic or command lines | https://malware.com/payload.exe |
registry | Registry paths from persistence analysis | HKLM\Software\Microsoft\Windows\CurrentVersion\Run |
other | Anything else that doesn't fit above | Mutex name, pipe name, etc. |
Link additional detections discovered during investigation (pass the full detection JSON object):
limacharlie case detection add --case <case_number> \
--detection '<full detection JSON>' \
--oid <oid> --output yaml
Use notes to document your investigation process. Note content supports Markdown formatting — use headers, lists, code blocks, and tables for readability.
limacharlie case add-note --case-number <case_number> --type analysis \
--content "Ran LCQL query for parent PID 2476 - no results found. Parent process may predate telemetry window." \
--oid <oid> --output yaml
For long notes, use --input-file to read content from a file:
limacharlie case add-note --case-number <case_number> --type analysis --input-file /tmp/note.md --oid <oid> --output yaml
Valid Note Types:
| Type | When to Use | Example |
|---|---|---|
general | General observations and facts | "Process rundll32.exe spawned without arguments at 19:39:10" |
analysis | Investigation findings, hypotheses, conclusions | "Active C2 communication to 35.232.8.38 confirmed via 60+ connections" |
remediation | Remediation actions taken or recommended | "Isolated host via network isolation. Recommend password reset for compromised account." |
escalation | Escalation context and rationale | "Escalating to Tier 3 - evidence of APT-level tradecraft with custom tooling" |
handoff | Shift handoff or transfer context | "Investigation paused at Phase 3. Org-wide IOC search complete, lateral movement analysis pending." |
to_stakeholder | Notes/communications sent TO external stakeholders (e.g. customers, management) | "Notified customer of confirmed breach. Provided initial IOC list and recommended password resets." |
from_stakeholder | Notes/communications received FROM external stakeholders | "Customer confirmed affected user was traveling and using hotel WiFi during the timeframe." |
Notes also support an is_public boolean flag. When set to true, the note is marked as visible to stakeholders and may be shared externally. Defaults to false (internal only).
Invalid types will cause API errors. Do NOT use types like "observation", "hypothesis", "finding", "conclusion", etc.
Use tags to classify the case type and organize workflow. Add tags as you learn more during the investigation.
When to tag:
phishing, ransomware, credential-theft, lateral-movement, cryptominer)needs-escalation, false-positive-candidate, awaiting-response, containment-complete)# Add classification tags after identifying the threat type
limacharlie case tag add --case-number <case_number> --tag phishing --oid <oid> --output yaml
limacharlie case tag add --case-number <case_number> --tag needs-escalation --oid <oid> --output yaml
Best Practice Note Structure:
analysis notes to document queries, findings, and reasoninganalysis noteanalysis noteremediation notes for recommended actionsanalysis noteshandoff notes when pausing investigationAttach references to forensic artifacts (memory dumps, PCAPs, etc.):
limacharlie case artifact add --case <case_number> \
--path "/forensics/memory/pid4832.dmp" \
--source "DESKTOP-001" \
--verdict malicious \
--note "Full memory dump of PID 4832 from DESKTOP-001" \
--oid <oid> --output yaml
| Verdict | When to Use |
|---|---|
malicious | Clear IOC match, known-bad behavior, confirmed threat |
suspicious | Unusual but not definitively malicious, requires review |
benign | Known-good, cleared by investigation, legitimate activity |
unknown | Insufficient context, requires further analysis |
informational | Context-providing, neither good nor bad |
Important: benign is a valuable verdict, not a reason to exclude evidence. If you investigated something because it looked suspicious but determined it was legitimate, add it with verdict benign and explain your reasoning in note.
When you can confidently identify techniques, reference them in your analysis notes and entity context fields:
For MITRE reference, fetch from: https://raw.githubusercontent.com/mitre-attack/attack-stix-data/master/enterprise-attack/enterprise-attack.json
You know you're done when:
Summarize for the user:
STOP - Before finalizing the case, verify your investigation is complete:
Telemetry Coverage:
note explanationDetection Coverage:
Entity/IOC Coverage:
Count Check: If you discovered 10+ events during investigation but only have 3 telemetry references, GO BACK and add the rest.
Always confirm with user before finalizing:
After user confirmation, update the case with summary, conclusion, classification, and resolve it. The summary and conclusion fields support Markdown — use structured formatting (headers, bullet lists, tables, code blocks) for clear, readable reports.
limacharlie case update --case-number <case_number> \
--summary "What happened - the full attack narrative, scope, and impact" \
--conclusion "Final assessment - classification rationale, recommendations, remaining risks" \
--classification true_positive \
--status resolved \
--oid <oid> --output yaml
If the investigation reveals the case needs senior analyst attention, add an escalation note with context and use tags to flag it for the appropriate team:
limacharlie case add-note --case-number <case_number> --type escalation \
--content "Escalating: Evidence of APT-level tradecraft. Custom C2 implant with domain fronting. Requires malware reverse engineering." \
--oid <oid> --output yaml
limacharlie case tag add --case-number <case_number> --tag needs-escalation --oid <oid> --output yaml
When multiple cases are part of the same incident (e.g., same malware across hosts):
limacharlie case merge --target <primary_case_number> \
--sources <case_number_2>,<case_number_3> \
--oid <oid> --output yaml
Source cases transition to closed status with merged_into_case_id set. All detections move to the primary case.
# All open cases, most recent first
limacharlie case list --status new --status in_progress --oid <oid> --output yaml
# Critical/high severity only
limacharlie case list --status new --status in_progress --severity critical --severity high --oid <oid> --output yaml
# Assigned to a specific analyst
limacharlie case list --assignee analyst@example.com --oid <oid> --output yaml
# Filter by sensor ID
limacharlie case list --sensor-id <sid> --oid <oid> --output yaml
limacharlie case dashboard --oid <oid> --output yaml
limacharlie case report --from 2025-01-01T00:00:00Z --to 2025-02-01T00:00:00Z --oid <oid> --output yaml
Export all case data (metadata, detections, entities, telemetry, artifacts) as a single JSON object, or to a directory with the actual detection records, telemetry events, and artifact binaries:
# JSON to stdout
limacharlie case export --case-number <case_number> --oid <oid> --output yaml
# Full data export to a directory
limacharlie case export --case-number <case_number> --with-data ./case-export --oid <oid>
Close multiple false positive cases:
limacharlie case bulk-update --numbers <num1>,<num2>,<num3> \
--status closed --classification false_positive \
--oid <oid> --output yaml
detection-engineering - For creating D&R rules based on investigation findingsthreat-report-evaluation - For evaluating threat reports and searching for IOCssensor-tasking - For live response and data collection from sensors during investigation (EDR sensors only: requires platform=windows/linux/macos AND arch!=usp_adapter)https://cases.limacharlie.io/openapilimacharlie case --ai-help for cases CLI command helpCase status values: new, in_progress, resolved, closed
Classification values: pending, true_positive, false_positive
Severity values: critical, high, medium, low, info
Verdict values: malicious, suspicious, benign, unknown, informational
Entity types: ip, domain, hash, url, user, email, file, process, registry, other
Note types: general, analysis, remediation, escalation, handoff, to_stakeholder, from_stakeholder
Tag management: limacharlie case tag set/add/remove --case-number <number> --tag <tag> --oid <oid>