IRQL -- 사고 대응 쿼리 언어
선택기, 추출기 및 보강기 구성 요소로부터 IRQL 함수 파이프라인을 구성합니다. IRQL은 원시 KQL 보안 테이블을 의도가 드러나고 구성 가능한 함수로 감싸서, 분석가(및 LLM)가 스키마, 클러스터 위치, 조인 키를 기억하지 않고도 헌팅을 표현할 수 있게 합니다.
활성화 트리거
사용자가 다음과 같은 경우 이 스킬을 사용합니다.
- IRQL,
Get_*,Extract_*또는Enrich_*함수를 명시적으로 언급하는 경우 - "IRQL을 사용해" 또는 "IRQL 쿼리를 작성해"라고 말하는 경우
- 알려진 IRQL 선택기를 사용하여 구성 가능한 헌팅 파이프라인을 요청하는 경우
활성화하지 않음(예: "실패한 로그인 찾기") 사용자가 명시적으로 IRQL을 요청하지 않는 한, 일반 보안 쿼리의 경우 azure-kusto로 라우팅하세요.
자연어-TO-IRQL 변환기가 아닙니다. 이 스킬은 IRQL 함수 파이프라인을 구성하며, 알려진 선택기와 간단한 필터에 직접 매핑되는 기본 자연어 요청을 처리할 수 있습니다. 일반 NL-to-KQL 또는 NL-to-IRQL 변환의 경우 전용 쿼리 생성 스킬(별도 제공)을 사용하세요.
IRQL 함수 사전 확인
파이프라인을 생성하기 전에 대상 데이터베이스에서 IRQL을 사용할 수 있는지 확인합니다.
.show functions
| where Name startswith "Get_" or Name startswith "Extract_" or Name startswith "Enrich_"
| project Name
IRQL 함수가 발견되지 않으면 대상 데이터베이스에 IRQL이 배포되지 않았음을 사용자에게 알리고, 원시 KQL 쿼리에는 azure-kusto를 사용할 것을 제안합니다. IRQL 함수는 필수 구성 요소입니다. 이 스킬은 기본 IRQL 선택기를 배포하지 않습니다.
IRQL이란 무엇인가
IRQL은 KQL 위에 구축된 함수 기반 방언입니다. 다음을 제공합니다.
- 통합 스키마 -- 서로 다른 보안 테이블이 기본 데이터 소스와 관계없이 일관된 열 이름으로 투영됩니다.
- 구성 가능성 -- 작은 함수들이
| invoke를 통해 체인으로 연결되어 간단한 단계로 복잡한 헌팅을 구축합니다. - 이식성 -- 동일한 IRQL 파이프라인이 다른 클러스터/데이터베이스에서 작동합니다.
Get_*기본 요소만 다시 지정하면 됩니다.
IRQL은 별도의 언어가 아닙니다. 호출하는 KQL 함수입니다. 모든 유효한 KQL이 IRQL 함수와 함께 작동합니다.
IRQL 배포
IRQL 함수는 저장된 KQL 함수(.create-or-alter function)입니다. 이 스킬이 파이프라인을 생성하려면 먼저 대상 데이터베이스에 배포되어 있어야 합니다.
공개 예제 클러스터(함수 사전 배포됨):
- 클러스터:
https://kc7001.eastus.kusto.windows.net - 데이터베이스:
ValdyTimes,JoJosHospital
IRQL을 새 클러스터/데이터베이스로 이식하려면 소스 테이블을 통합 스키마(아래 열 이름)로 투영하는 Get_* 선택기를 만든 다음, 추출기와 보강기를 배포합니다. 입력 스키마가 일치하는 한 추출기와 보강기는 변경 없이 작동합니다.
함수 카탈로그
1. 선택기 -- Get_*
소스 테이블의 투영된 스키마 통합 뷰를 반환합니다. 기본적으로 최소 양식을 사용하고, 추가 열이 필요할 때 _All을 사용합니다.
| 함수 | 열 |
|---|---|
Get_Event_Authentication | EnvTime, Hostname, ClientIp, Username, Result |
Get_Event_Authentication_All | + Description, UserAgent, PasswordHash |
Get_Email | EnvTime, EmailSender, EmailRecipient, Subject, Url |
Get_Email_All | + ReplyTo, Verdict |
Get_Employees | Name, ClientIp, Email, Username, Hostname, Role |
Get_Employees_All | + HireDate, UserAgent, Domain |
Get_Event_FileCreation | EnvTime, Hostname, Filename, Path |
Get_Event_FileCreation_All | + Username, Sha256, ProcessName |
Get_Event_NetworkInbound | EnvTime, ClientIp, Url |
Get_Event_NetworkInbound_All | + Method, UserAgent, StatusCode |
Get_Event_NetworkOutbound | EnvTime, ClientIp, Url |
Get_Event_NetworkOutbound_All | + Method, UserAgent |
Get_Dns_All | EnvTime, Domain, ClientIp |
Get_Event_Process | EnvTime, ProcessCommandLine, ProcessName, Hostname, Username |
Get_Event_Process_All | + ParentProcessName, ParentProcessHash, ProcessHash |
Get_SecurityAlerts_All | EnvTime, AlertType, Severity, Description, Indicators |
Get_Network_Connection_All | EnvTime, SourceIp, SourcePort, DestinationIp, DestinationPort, Protocol, Bytes |
2. 추출기 -- Extract_*
기존 열에서 새 열을 파생합니다. 선택기 뒤에 호출합니다.
| 함수 | 입력 열 | 추가 |
|---|---|---|
Extract_Email_Sender_Domain(T) | EmailSender | Domain |
Extract_Employee_Firstname(T) | Name | Firstname |
Extract_Event_Network_Domain(T) | Url | DomainName |
3. 보강기 -- Enrich_*
관련 테이블의 컨텍스트를 연결하는 왼쪽 조인 도우미입니다.
| 함수 | 키 열 | 보강 내용 |
|---|---|---|
Enrich_Event_Authentication_Username(T) | Username | 해당 사용자의 인증 이벤트 |
Enrich_Ip_Employee(T) | ClientIp | IP에서 직원 신원 |
Enrich_Username_Employee(T) | Username | 사용자 이름에서 직원 신원 |
Enrich_Ip_Domain(T) | ClientIp | IP로 확인된 DNS 도메인 |
Enrich_Ip_Event_NetworkOutbound(T) | ClientIp | IP의 아웃바운드 네트워크 |
Enrich_Ip_Network_Connection(T) | ClientIp | IP의 네트워크 흐름 |
4. 외부 보강
| 함수 | 소스 | 요구 사항 |
|---|---|---|
Enrich_Sha256_VirusTotal(T) | VirusTotal 파일 보고서 | API 키 + 콜아웃 정책 |
Get_CISA_KEV() / Enrich_CISA_KEV(T) | CISA KEV 카탈로그 | 콜아웃 정책 |
조합 규칙
선택기 -> 추출 -> 필터 -> 보강 -> 요약/투영
- 선택기로 시작:
Get_Event_Authentication,Get_Email등 - 파생 필드 추출:
| invoke Extract_Email_Sender_Domain() - 신호로 필터링:
| where Result == "Failed Login" - 컨텍스트로 보강:
| invoke Enrich_Username_Employee() - 답변 요약/투영:
항상 단계 사이에 파이프(|)를 사용합니다. 추출기와 보강기는 | invoke FunctionName()을 사용합니다.
쿼리 생성 지침
- 추가 열이 필요하지 않으면 최소 선택기를 사용하고, 필요하면
_All을 사용합니다. - 추출기를 보강기보다 먼저 체인으로 연결합니다(추출기는 보강기가 키로 사용할 수 있는 열을 추가합니다).
where필터는 가능한 한 빨리 배치합니다.- 집계에는
summarize, 최종 열 선택에는project를 사용합니다. - 출력을 제한하려면
order by+take로 마무리합니다.
예제
추가 프롬프트 및 작업 예제는 references/EXAMPLES.md를 참조하세요.
무차별 대입 공격 탐지
Get_Event_Authentication
| where Result == "Failed Login"
| summarize FailedCount = count() by Username
| where FailedCount > 19
| invoke Enrich_Username_Employee()
| project Username, Name, Role, Email, FailedCount
| order by FailedCount desc
수신자 직급별 피싱 분류
Get_Email
| invoke Extract_Email_Sender_Domain()
| project EnvTime, EmailSender, Domain, Username = EmailRecipient, Subject, Url
| invoke Enrich_Username_Employee()
| extend Seniority = case(
Role has_any ("CEO", "Chief", "Director", "VP", "President"), 3,
Role has_any ("Manager", "Lead", "Senior"), 2,
1)
| summarize
TotalEmails = count(),
SeniorityScore = sum(Seniority),
Recipients = make_set(Name, 50),
DistinctRecipients = dcount(Username)
by Domain
| where DistinctRecipients >= 2
| order by SeniorityScore desc
| take 20
지표에서 사후 익스플로잇 피벗
let victims =
Get_Event_FileCreation_All
| where Filename has "<INDICATOR>"
| distinct Hostname;
Get_Event_Process
| where Hostname in (victims)
| where ProcessCommandLine has_any ("rundll32", "regsvr32", "powershell", "systeminfo")
| project EnvTime, Hostname, Username, ProcessName, ProcessCommandLine
| order by EnvTime asc
신원 정보가 포함된 의심스러운 아웃바운드 트래픽
Get_Event_NetworkOutbound
| invoke Extract_Event_Network_Domain()
| where DomainName has_any ("<SUSPICIOUS_DOMAIN_1>", "<SUSPICIOUS_DOMAIN_2>")
| invoke Enrich_Ip_Employee()
| project EnvTime, Name, Role, DomainName, Url, ClientIp
| order by EnvTime desc
외부 IP 인증 이상
Get_Event_Authentication_All
| where not(ClientIp startswith "10.") and not(ClientIp startswith "192.168.")
| summarize
Attempts = count(),
Failures = countif(Result == "Failed Login"),
Users = make_set(Username)
by ClientIp
| order by Failures desc
| take 20
사용되는 MCP 도구
| 도구 | 목적 |
|---|---|
kusto_query | Kusto 데이터베이스에 대해 IRQL 파이프라인 실행 |
kusto_table_schema_get | 사용 가능한 테이블 및 열 검색 |
kusto_cluster_list | 사용 가능한 ADX 클러스터 나열 |
kusto_database_list | 클러스터의 데이터베이스 나열 |
Kusto Explorer에서 쿼리 열기(Windows 전용)
선택적 편의 기능. 기본 워크플로는 채팅에 KQL을 출력하고 사용자가 수동으로 Kusto Explorer 또는 VS Code Kusto 확장 프로그램에 복사하도록 하는 것입니다. 자동 실행은 옵트인(opt-in) 방식입니다.
항상 완전한 KQL 쿼리를 1단계(연결)와 2단계(쿼리)로 명확히 구분하여 채팅 응답에 출력합니다.
// 1단계: 클러스터에 연결(이미 연결된 경우 건너뜀)
// 예: KC7 훈련 클러스터에 연결하려면 주석 해제
// #connect cluster('kc7001.eastus.kusto.windows.net').database('ValdyTimes')
// 또는 자신의 클러스터로 대체:
// #connect cluster('<YOUR_CLUSTER>').database('<YOUR_DATABASE>')
// 2단계: 아래 쿼리 실행
<KQL_QUERY>
사용자가 Kusto Explorer에 저장하거나 열기를 요청하는 경우 references/KUSTO_EXPLORER_LAUNCH.md의 절차를 따르세요. 주요 규칙:
- 파일을 쓰거나 실행 파일을 시작하기 전에
ask_user를 사용하여 확인 - 사용자가 열기 전에 검토할 수 있도록 파일 내용을 채팅에 표시
- 셸 보간 또는 here-strings를 사용하지 말고
Set-Content/Add-Content를 통해 파일 작성 - 쿼리를 브라우저 URL로 인코딩하지 마세요.
- macOS/Linux에서는
.kql파일을 저장하고 VS Code Kusto 확장 프로그램 또는 ADX Web Explorer를 제안 - IRQL 데이터의 그래프 시각화는
azure-kusto-graph및azure-kusto-irql-graph를 참조하세요.