COMMENTS (0)
댓글은 익명으로 작성되며, 삭제 비밀번호를 설정하면 본인만 삭제할 수 있습니다. 비밀번호를 설정하지 않은 댓글은 누구나 삭제할 수 있습니다.
YARA 룰의 내부 구조를 해부한다. Aho-Corasick 알고리즘의 2단계 매칭 원리, 문자열/모듈/조건 시스템, Lazarus와 Kimsuky 실전 탐지 사례, YARA-X 전환까지.
댓글은 익명으로 작성되며, 삭제 비밀번호를 설정하면 본인만 삭제할 수 있습니다. 비밀번호를 설정하지 않은 댓글은 누구나 삭제할 수 있습니다.
Nextron Systems의 Valhalla 저장소에는 23,795개의 YARA 룰이 등록되어 있다. 이 룰들이 매일 VirusTotal에 제출되는 수십억 개의 파일을 스캔한다. Sigma가 로그에서 공격 흔적을 찾는 도구라면, YARA는 파일과 메모리에서 악성코드 자체를 식별하는 도구다. 1개의 YARA 룰이 어떻게 수백만 개의 파일 속에서 특정 악성코드를 정확히 집어내는지, 그 내부 구조를 해부한다.
보안 탐지에는 두 가지 질문이 있다. "무슨 일이 일어났는가" 와 "이 파일이 무엇인가" 다.
Sigma는 첫 번째 질문에 답한다. Windows Event Log, Sysmon, 방화벽 로그를 분석해서 공격자의 행위를 탐지한다. 하지만 디스크에 저장된 파일이 악성인지, 메모리에 로드된 코드가 백도어인지는 Sigma의 영역이 아니다.
YARA는 두 번째 질문에 답한다. 파일의 바이너리 패턴, 문자열, 구조적 특징을 분석해서 "이 파일은 Lazarus 그룹의 AppleJeus 다운로더다"라고 식별한다.
현실적인 시나리오를 하나 보자. SOC 분석가가 EDR 알림을 받았다. 의심스러운 DLL이 C:\Windows\Temp\에 생성되었다. VirusTotal에 해시를 조회하면 "0/70 탐지" — 안티바이러스는 아무것도 못 잡았다. 이때 YARA 룰로 파일을 직접 스캔하면 "Kimsuky_Backdoor_DLL" 매칭이 뜬다. 안티바이러스 시그니처에 등록되기 전에, 위협 인텔리전스 기반의 YARA 룰이 먼저 잡아내는 것이다. YARA가 없으면 이 파일은 "정상"으로 분류되어 침해가 확산된다.
| 기준 | Sigma | YARA |
|---|---|---|
| 대상 | 로그, 이벤트 | 파일, 메모리, 네트워크 스트림 |
| 문법 | YAML | C 유사 문법 |
| 출력 | SIEM 쿼리 (SPL, KQL 등) | 직접 매칭 결과 (True/False) |
| 질문 | "무슨 행위가 발생했나?" | "이 파일이 무엇인가?" |
| 탄생 | 2017년, Florian Roth | 2013년, Victor Alvarez (VirusTotal) |
두 도구는 경쟁이 아니라 보완 관계다. 침해사고 대응에서 Sigma로 공격 타임라인을 재구성하고, YARA로 공격에 사용된 악성코드를 식별한다. HarfangLab EDR처럼 두 엔진을 동시에 탑재한 제품이 늘어나는 이유다. Sigma 룰의 내부 구조는 Sigma Rule의 내부 구조: 1개 YAML이 Splunk·Elastic·Sentinel에서 실행되는 원리에서 다뤘다.
YARA 룰은 3개 섹션으로 구성된다. 아래는 Kaspersky GReAT 팀이 Lazarus 그룹의 AppleJeus 다운로더를 탐지하기 위해 작성한 룰의 구조를 단순화한 예시다.
rule Lazarus_AppleJeus_Downloader {
meta:
description = "Lazarus AppleJeus 다운로더 탐지"
author = "Kaspersky GReAT"
reference = "https://securelist.com/operation-applejeus/87553/"
date = "2018-08-23"
strings:
$pdb1 = "H:\\DEV\\TManager\\" ascii
$pdb2 = "Z:\\jeus\\" ascii
$str1 = "CryptStringToBinaryA" ascii
$str2 = "IsDebuggerPresent" ascii
condition:
uint16(0) == 0x5A4D and
filesize < 500KB and
(any of ($pdb*)) and
all of ($str*)
}
meta 섹션은 탐지에 영향을 주지 않는 메타데이터다. 작성자, 날짜, 참고 자료, 위협 그룹명을 기록한다. MITRE ATT&CK ID를 태깅하면 위협 인텔리전스 플랫폼과 연동할 수 있다.
YARA는 3가지 타입의 문자열을 지원한다.
텍스트 문자열: "CryptStringToBinaryA" — 파일 내 ASCII/유니코드 문자열을 탐색한다. nocase(대소문자 무시), wide(유니코드), ascii, fullword(단어 경계 매칭) 수정자를 조합할 수 있다.
16진수 패턴: { E2 34 ?? A1 [4-6] 00 } — 바이트 단위로 탐색한다. ??는 와일드카드, [4-6]은 4~6바이트 가변 길이를 뜻한다. 바이너리 분석에서 함수 프롤로그나 암호화 상수를 잡을 때 사용한다.
정규 표현식: /https?:\/\/[a-z0-9\-\.]+\.onion/i — 복잡한 패턴을 표현한다. 다만 성능에 직접 영향을 주므로 VirusTotal은 Livehunt에서 느린 정규식을 자동 거부한다.
YARA 4.x부터 xor(XOR 인코딩 자동 탐색)와 base64(Base64 인코딩 변형 탐색) 수정자가 추가되었다. 공격자가 문자열을 단순 인코딩으로 숨기는 기법에 대응한다.
condition은 strings에서 정의한 패턴들을 논리 조건으로 조합하는 섹션이다. 위 예시를 분해하면:
uint16(0) == 0x5A4D — 파일 첫 2바이트가 MZ (PE 실행 파일 매직 넘버)filesize < 500KB — 파일 크기 제한 (대형 파일 스킵으로 성능 확보)any of ($pdb*) — PDB 경로 중 하나라도 매칭all of ($str*) — API 문자열은 모두 존재해야 함이 조합이 핵심이다. 개별 문자열은 정상 파일에도 존재할 수 있지만, PDB 개발 경로 + 특정 API 조합 + PE 파일 + 500KB 이하라는 교차 조건은 Lazarus의 AppleJeus 다운로더에만 해당한다.
YARA의 성능 비밀은 Aho-Corasick 알고리즘에 있다. 이 알고리즘은 문자열 매칭 분야의 표준으로, 모든 패턴을 하나의 오토마톤(유한 상태 기계)으로 결합한 뒤 대상 파일을 단 1회 스캔으로 모든 패턴의 위치를 찾아낸다.
일반적인 문자열 검색은 패턴 수(k)에 비례해서 느려진다. 10,000개 패턴이면 파일을 10,000번 읽어야 한다. Aho-Corasick은 패턴 수에 관계없이 파일을 1번만 읽으면 된다. 시간 복잡도는 O(n + m + z)로, n은 파일 크기, m은 모든 패턴의 총 길이, z는 매칭 발생 횟수다.
YARA의 매칭은 2단계로 나뉜다.
1단계 — Atom 추출 + Aho-Corasick 스캔: 정규식이나 16진수 와일드카드가 포함된 패턴에서 고정 부분(atom)을 추출한다. 예를 들어 /somethinghere[0-9]{5,43}/에서 somethinghere가 atom이 된다. 모든 atom을 Aho-Corasick 오토마톤에 넣고 파일을 1회 스캔하여 후보 위치를 찾는다.
2단계 — 정밀 검증 + 조건 평가: 1단계에서 찾은 후보 위치에서만 전체 정규식 매칭을 실행한다. 그 후 condition 섹션의 논리식을 평가하여 최종 매칭 여부를 결정한다.
이 2단계 구조 덕분에 비용이 큰 정규식 연산을 최소화한다. THOR Scanner의 30,000개 이상 시그니처가 실시간으로 동작할 수 있는 이유다. Malpedia의 signator-rules 프로젝트는 이 구조를 활용해 1,595개 악성코드 패밀리를 자동 분류하며, 정밀도(precision) 0.994, F1 스코어 0.972를 달성했다.
단순 문자열 매칭만으로는 정교한 탐지가 어렵다. YARA 모듈은 파일 구조를 파싱하여 파일 포맷 수준의 조건을 작성할 수 있게 한다.
pe 모듈 — Windows 실행 파일(PE)의 구조를 분석한다. 섹션 수, 임포트 함수, 디지털 서명, 컴파일 타임스탬프, 엔트리 포인트 위치를 조건에 사용할 수 있다.
import "pe"
import "math"
rule Suspicious_Packed_PE {
condition:
pe.number_of_sections > 0 and
for any section in pe.sections : (
math.entropy(section.offset, section.size) >= 7.0
) and
pe.number_of_signatures == 0
}
이 룰은 "서명 없는 PE 파일 중, 섹션 엔트로피가 7.0 이상(패킹/암호화 가능성)"인 파일을 탐지한다. 엔트로피 7.0은 데이터가 거의 랜덤하다는 뜻으로, UPX나 Themida 같은 패커를 사용했을 가능성을 나타낸다.
주요 모듈 목록:
| 모듈 | 용도 |
|---|---|
| pe | Windows PE 파일 분석 (섹션, 임포트, 서명) |
| elf | Linux ELF 바이너리 분석 |
| dotnet | .NET 어셈블리 분석 (스트림, 클래스명) |
| math | 엔트로피, 평균, 편차 계산 |
| hash | MD5, SHA-1, SHA-256 해시 비교 |
YARA-X에서는 lnk(바로가기), macho(macOS), dex(Android) 모듈이 추가되어 분석 범위가 확장되었다.
이론을 넘어 실제 위협 헌팅 사례를 보자. 아래는 McAfee ATR(Advanced Threat Research) 팀의 Christiaan Beek이 작성한 Kimsuky 그룹 백도어 탐지 룰이다.
rule Kimsuky_Backdoor_DLL {
meta:
description = "Kimsuky 그룹 키로거/백도어 DLL 탐지"
author = "McAfee ATR - Christiaan Beek"
reference = "Kaspersky Kimsuky Research"
strings:
$dbg1 = "taskmgr.exe Execute Ok!!!" ascii wide
$dbg2 = "KeyLog End" ascii wide
$dbg3 = "Start Keylog" ascii wide
$op1 = { 8D 85 ?? FD FF FF 50 68 00 04 00 00 }
$op2 = { 68 ?? ?? 41 00 68 ?? ?? 41 00 FF 15 }
condition:
uint16(0) == 0x5A4D and
(2 of ($dbg*)) and
(any of ($op*))
}
이 룰이 효과적인 이유는 개발자의 실수를 노린다는 점이다.
"taskmgr.exe Execute Ok!!!" — Kimsuky 개발자가 테스트 중 남긴 디버그 문자열이다. 배포 전에 제거해야 하지만 그대로 남아 있다. "Start Keylog", "KeyLog End" 역시 키로거 기능의 디버그 메시지다.
이런 디버그 흔적은 APT 그룹이 의외로 자주 남긴다. Lazarus 그룹도 AppleJeus 캠페인에서 개발 경로(H:\DEV\TManager\)를 PDB 정보에 포함시켰고, CISA가 공개한 Volgmer 탐지 룰은 놀라울 정도로 단순하다.
rule CISA_Volgmer_RAT {
meta:
description = "HIDDEN COBRA Volgmer RAT 탐지"
source = "CISA Alert TA17-318B"
strings:
$useragent = "Mozillar/" ascii
condition:
$useragent
}
HTTP User-Agent에서 "Mozillar/"라는 단 하나의 문자열로 Lazarus RAT를 식별한다. 개발자가 "Mozilla/"를 하드코딩하다가 r을 하나 더 넣은 것이다. 이 오타는 수년간 수정되지 않았고, 전 세계 정상 소프트웨어 중 "Mozillar/"를 User-Agent로 쓰는 프로그램은 없다. 오탐률 제로에 가까운 룰이다.
YARA 룰은 작성만으로 끝나지 않는다. 실제 운영 환경에서 배포하는 방식에 따라 효과가 달라진다.
1. 로컬 파일 스캔: 침해사고 현장에서 분석관이 직접 실행한다. yara -r suspicious_rule.yar /target/directory/ 명령으로 디렉토리 전체를 재귀 스캔한다. yarac로 룰을 미리 컴파일하면 반복 스캔 시 속도가 향상된다.
2. 엔드포인트 스캔: Velociraptor의 Windows.Detection.Yara.Process 아티팩트를 사용하면 수천 대 엔드포인트의 프로세스 메모리를 동시에 YARA 스캔할 수 있다. THOR Scanner는 30,000개 이상의 암호화된 시그니처를 내장하고 있어 별도 룰 관리 없이 배포할 수 있다.
3. VirusTotal Livehunt: YARA 룰을 등록하면, VirusTotal에 제출되는 모든 파일에 대해 실시간으로 매칭한다. 새로운 Lazarus 변종이 처음 업로드되는 순간 알림을 받을 수 있다. Retrohunt는 과거에 제출된 파일까지 소급 스캔한다.
YARA가 만능은 아니다. 공격자는 세 가지 방법으로 YARA 탐지를 우회한다.
UPX, Themida, VMProtect 같은 패커로 바이너리를 감싸면 원본 문자열이 사라진다. YARA는 디스크에 저장된 상태의 파일을 스캔하므로, 패킹된 파일에서는 문자열 기반 룰이 무력화된다. 대응 방법은 두 가지다.
같은 기능이지만 빌드할 때마다 코드가 달라지는 다형성(polymorphic) 악성코드는 고정 문자열 기반 탐지를 어렵게 만든다. 이 경우 행위 기반 문자열(API 호출 조합, 특정 뮤텍스 이름)이나 코드 구조 패턴(opcode 시퀀스)으로 대응한다.
공격자가 문자열을 런타임에만 복호화하고 사용 후 즉시 메모리에서 제거하면, 디스크 스캔과 일반 메모리 스캔 모두 회피할 수 있다. xor와 base64 수정자가 단순 인코딩에는 대응하지만, 커스텀 암호화에는 한계가 있다.
실전에서 흔한 문제는 잘못 작성된 룰이 시스템을 느리게 만드는 것이다.
.*나 .+는 역추적(backtracking)을 유발한다. {0,10}처럼 범위를 제한해야 한다.for all i in (1..filesize) 같은 조건은 대형 파일에서 타임아웃을 일으킨다. filesize < 1MB 가드를 반드시 앞에 배치한다.2024년 5월, VirusTotal은 "YARA is dead, long live YARA-X"를 선언했다. 11년간 C로 작성된 YARA의 후계자는 Rust로 완전히 재작성되었다.
YARA-X v1.0.0은 2025년 6월에 안정 버전으로 출시되었고, 2026년 3월 기준 v1.14.0까지 업데이트되었다. 기존 YARA 4.x 룰의 약 99%가 수정 없이 동작한다.
핵심 개선점은 성능이다. 정규식과 루프 처리에서 기존 대비 5~10배 빠른 속도를 달성했다. Rust의 메모리 안전성 덕분에 악의적으로 조작된 파일이 YARA 엔진 자체를 크래시시키는 취약점도 원천 차단되었다.
VirusTotal의 Livehunt와 Retrohunt는 이미 YARA-X로 전환되어 수십억 개의 파일을 처리하고 있다. Python, Go, C, JavaScript(WebAssembly) 바인딩을 제공하므로 기존 파이프라인 마이그레이션이 용이하다.
YARA는 파일과 메모리에서 악성코드를 식별하는 패턴 매칭 도구다. Sigma가 "무슨 일이 일어났는가"에 답한다면, YARA는 "이 파일이 무엇인가"에 답한다.
Aho-Corasick 알고리즘으로 수천 개 패턴을 단 1회 스캔으로 매칭하고, pe/elf/math 모듈로 파일 구조 수준의 정밀 탐지를 수행한다. Lazarus의 PDB 경로, Kimsuky의 디버그 문자열, Volgmer의 User-Agent 오타처럼 공격자의 실수를 포착하는 것이 YARA의 강점이다.
패킹, 다형성, 런타임 복호화로 우회는 가능하지만, 메모리 스캔과 구조적 탐지로 대응할 수 있다. YARA-X로의 전환이 진행 중이며, Rust 재작성으로 성능과 안전성이 크게 향상되었다.