Best for
- Use when the user asks for a KQL query, a Microsoft Sentinel / Defender / Azure Log Analytics detection or hunt, or wants to translate a finding from `/hash-investigation` / `/malware-analysis` into KQL.
Liberty91LTD/cti-skills/skills/kql-writing/SKILL.md
Use when the user asks for a KQL query, a Microsoft Sentinel / Defender / Azure Log Analytics detection or hunt, or wants to translate a finding from `/hash-investigation` / `/malware-analysis` into KQL. Format spec + writing guide.
Decision brief
KQL (Kusto Query Language) is used in Microsoft Sentinel, Microsoft Defender, and Azure Data Explorer for querying security logs and building detection rules.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/Liberty91LTD/cti-skills --skill "skills/kql-writing"Inspect the Agent Skill "kql-writing" from https://github.com/Liberty91LTD/cti-skills/blob/97d66b3687ba6d32b316a7df3391beb3e2de88de/skills/kql-writing/SKILL.md at commit 97d66b3687ba6d32b316a7df3391beb3e2de88de. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Review the “Suspicious process execution (T1059)” section in the pinned source before continuing.
Review the “Core Syntax” section in the pinned source before continuing.
Review the “Table references” section in the pinned source before continuing.
Review the “Operators” section in the pinned source before continuing.
Review the “String operators” section in the pinned source before continuing.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 90/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 11 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
KQL (Kusto Query Language) is used in Microsoft Sentinel, Microsoft Defender, and Azure Data Explorer for querying security logs and building detection rules.
SecurityEvent // Windows Security Events
DeviceProcessEvents // Defender for Endpoint
DeviceNetworkEvents // Network connections
DeviceFileEvents // File operations
EmailEvents // Defender for Office 365
SigninLogs // Azure AD sign-ins
AuditLogs // Azure AD audit
CommonSecurityLog // CEF/Syslog
ThreatIntelligenceIndicator // TI feed indicators
| where TimeGenerated > ago(24h) // Time filter
| where EventID == 4688 // Exact match
| where ProcessCommandLine contains "-enc" // Substring
| where ProcessCommandLine matches regex @".*-e(nc)?.*" // Regex
| where SourceIP !in ("10.0.0.1", "10.0.0.2") // Not in list
| where isnotempty(AccountName) // Not null/empty
| extend NewColumn = extract(@"pattern", 1, SourceField) // Extract
| project TimeGenerated, Account, Computer // Select columns
| summarize count() by bin(TimeGenerated, 1h), Account // Aggregate
| sort by TimeGenerated desc // Sort
| take 100 // Limit results
| join kind=inner (OtherTable) on CommonField // Join
| Operator | Description | Case-sensitive |
|---|---|---|
== | Exact match | Yes |
=~ | Exact match | No |
contains | Substring | No |
contains_cs | Substring | Yes |
startswith | Starts with | No |
endswith | Ends with | No |
matches regex | Regex match | Yes |
has | Word boundary match | No |
in | In list | Yes |
in~ | In list | No |
DeviceProcessEvents
| where TimeGenerated > ago(24h)
| where FileName in~ ("powershell.exe", "cmd.exe", "wscript.exe", "cscript.exe")
| where ProcessCommandLine contains_cs "-enc"
or ProcessCommandLine contains "bypass"
or ProcessCommandLine contains "downloadstring"
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine, InitiatingProcessFileName
DeviceProcessEvents
| where TimeGenerated > ago(24h)
| where InitiatingProcessFileName in~ ("outlook.exe", "winword.exe", "excel.exe", "powerpnt.exe")
| where FileName in~ ("cmd.exe", "powershell.exe", "wscript.exe", "mshta.exe", "certutil.exe")
| project TimeGenerated, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine
let IOC_IPs = dynamic(["203.0.113.42", "198.51.100.10"]);
let IOC_Domains = dynamic(["evil.example.com", "c2.badactor.net"]);
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where RemoteIP in (IOC_IPs) or RemoteUrl has_any (IOC_Domains)
| project TimeGenerated, DeviceName, RemoteIP, RemoteUrl, RemotePort, InitiatingProcessFileName
SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType != "0"
| summarize FailedAttempts = count(), DistinctAccounts = dcount(UserPrincipalName)
by IPAddress, bin(TimeGenerated, 15m)
| where FailedAttempts > 10
| sort by FailedAttempts desc
DeviceProcessEvents
| where TimeGenerated > ago(24h)
| where FileName =~ "psexesvc.exe"
or (FileName =~ "cmd.exe" and ProcessCommandLine contains "\\\\")
or ProcessCommandLine contains "-accepteula -s cmd"
| project TimeGenerated, DeviceName, AccountName, FileName, ProcessCommandLine
DeviceEvents
| where TimeGenerated > ago(24h)
| where ActionType == "DnsQueryResponse"
| extend DnsQuery = extractjson("$.DnsQueryString", AdditionalFields)
| where DnsQuery endswith ".onion.ws"
or DnsQuery endswith ".tor2web.io"
or DnsQuery matches regex @"^[a-z0-9]{20,}\."
| project TimeGenerated, DeviceName, DnsQuery
// Rule name: [Descriptive title]
// Description: [What this detects and why]
// MITRE ATT&CK: [Technique IDs]
// Severity: High|Medium|Low|Informational
// Tactics: [InitialAccess, Execution, etc.]
// Query:
[KQL query here]
ago()) to limit query scopehas instead of contains when searching for whole words (faster)in~ over multiple or conditions for case-insensitive list matchinglet statements for IOC lists to keep queries readableproject to select only needed columns (reduces result size)This skill authors queries. To run them against a real Microsoft Sentinel workspace — or to hunt interactively — chain /lookup-sentinel. Two rules apply the moment a query targets live data:
/lookup-sentinel discovers which tables the workspace actually ingests (tables / ingestion / probe) and adapts — e.g. no DeviceProcessEvents (no Defender for Endpoint) means falling back to SecurityEvent EventID 4688, which itself requires command-line auditing. Never ship a hunt referencing unverified tables.For portable analytics rules meant for any environment, keep the canonical table names from this guide and document the connector prerequisite in the rule header comment.
Write KQL queries to: data/detection-rules/kql/<technique-id>-<slug>.kql
Alternatives
narrative-io/narrative-skills-marketplace
Translate a fuzzy analytical question into a rigorous investigation plan. Interrogates the ask, grounds the plan in the available data dictionary, applies analytical best practices, and produces a structured brief of query specifications for a downstream query-writing skill. Plans, does not write SQL. Use when: "why did X drop", "is there a relationship between A and B", "who are our highest-value customers", "what's driving the change in Y", "investigate this trend", "design an analysis for", "
alirezarezvani/claude-skills
Use when the user asks to write SQL queries, optimize database performance, generate migrations, explore database schemas, or work with ORMs like Prisma, Drizzle, TypeORM, or SQLAlchemy.
jabrena/plinth
Use when you need programmatic JDBC in Quarkus — Agroal DataSource, parameterized SQL, transactions, batching, and Dev Services. This should trigger for requests such as Review JDBC or SQL data access in a Quarkus project; Improve transactions and parameter binding for Quarkus JDBC; Translate SQLException to domain exceptions or stream large result sets; Fix CDI self-invocation bypassing @Transactional in Quarkus; Review Agroal DataSource usage in Quarkus JDBC. Part of Plinth Toolkit
jabrena/plinth
Use when you need programmatic JDBC in Micronaut — pooled DataSource, parameterized SQL, io.micronaut.transaction.annotation.Transactional, batching, and domain exception translation. This should trigger for requests such as Review JDBC or SQL data access in a Micronaut project; Improve transactions and parameter binding for Micronaut JDBC; Translate SQLException to domain exceptions or stream large result sets; Fix self-invocation bypassing @Transactional in Micronaut; Review Hikari or pooled d