Arkime로 침입 탐지¶
한 줄 요약
Arkime는 시그니처 엔진이 아닙니다 — 경보, Zeek 이상 징후, 또는 직감이 증거가 되는 곳입니다: 세션을 찾고, 같은 대상과 또 누가 통신했는지 보고, 증명할 문자열을 페이로드에서 검색하고, pcap을 내려받습니다. 이 페이지는 그 일을 반복 가능하게 해 주는 워크플로 모음이고, Suricata와 WISE를 연결해서 탐지와 회수를 한 화면에서 하는 방법도 다룹니다.
핵심 루프¶
flowchart LR
A[Trigger<br/>Suricata alert · Zeek notice · beacon score · IOC · hunch] --> B[Sessions<br/>search expression, right time window]
B --> C[SPIView<br/>what is odd in this slice?]
C --> D[SPIGraph<br/>timing per value → beacons, bursts]
D --> E[Connections<br/>who else? lateral spread]
E --> F[Hunt<br/>string / regex / YARA in payloads]
F --> G[Tag + export pcap<br/>→ Wireshark / report]
G -.->|new IOCs| B
모든 단계는 같은 검색식을 좁히거나 넓힙니다 — 진행하면서 메모 파일에 적어 두세요. 최종 검색식이 곧 증거 쿼리입니다.
워크플로 1 — Suricata 경보에서 증거까지¶
suricata viewer 플러그인은 Suricata 경보를 Arkime 세션에 써 넣을 수 있습니다(eve.json을 읽고 5-튜플 + 시각으로 맞춘 뒤 suricata.* 필드를 추가). 그다음:
- 경보가 붙은 세션 찾기:
suricata.severity == 1 && ip.src == 10.0.0.0/8(또는suricata.signature == "*ET MALWARE*"). 시간 범위를 경보 주변으로 설정. - 세션 열기 → 펼치기 → 디코딩된 페이로드 읽기. HTTP는 요청/응답이, TLS는 핸드셰이크(SNI, 인증서, JA3)가 보입니다. 판단: 진짜 양성인가?
- 넓히기: 목적지 IP 우클릭 →
ip.dst == X, 시간 범위를 일주일로. 다른 호스트도 통신했나? 언제 시작됐나? - 같은 호스트의 다른 트래픽을 경보 직전과 직후에 확인: ±10분 범위의
ip.src == <피해 호스트>. DNS 쿼리, 다운로드, 후속 비콘을 찾으세요. - 세션에 태그 달기 (
Actions → Add Tags, 예:inc-2026-0916-c2) — 나중에 모두가 찾을 수 있게. 그리고 보고서용으로 Export PCAP.
Tip
오탐이 많은 규칙은 SPIView에서 바로 보입니다: 내부 → 내부 트래픽에 수백 건씩 걸린 suricata.signature 상위 값들. 개수로 정렬하고 꼬리 쪽을 보세요 — 진짜는 드문 시그니처에 숨어 있습니다.
워크플로 2 — SPIGraph로 비콘 헌팅¶
- 검색식: 내부 → 외부, 흔한 C2 포트, 허용 목록 제외:
ip.src == 10.0.0.0/8 && ip.dst != 10.0.0.0/8 && port.dst == [443, 80, 8443, 8080] && ip.dst != $known_saas - SPIGraph → 필드
ip.dst(또는host.tls,host.http), 시간 범위 24시간, 구간 1–5분. 각 행은 목적지 하나와 그 작은 타임라인입니다. 빗살무늬 — 밤낮없이 같은 높이로 고르게 늘어선 막대 — 가 비콘이고, 웹 브라우징은 업무 시간에 뭉친 덩어리처럼 보입니다. - 빗살무늬를 클릭 →
ip.dst == X가 됩니다. 이제 SPIView:ip.src가 몇 개인지(한 호스트 = 수상, 500대 = SaaS),tls.ja3(지문 하나),cert.issuer.cn(자체 서명? 막 발급된 Let's Encrypt?),http.user-agent,databytes.src/.dst분포(일정하게 작은 크기 = 대기 중 체크인). - 시간순 정렬한 Sessions: 간격을 직접 읽으세요(Arkime는 시작 시각을 보여 줌; 60초 ± 지터로 일정). 긴 연결 하나라면
session.segments/rootId를 쓰세요. - Zeek 간격 분석 / RITA로 뒷받침한 뒤 호스트로 가세요 (Sysmon 3 → 프로세스).
브라우저처럼 보여도 임플란트를 드러내는 필드: http.request.header (accept-language/referer 없음), 전사에서 다른 누구도 공유하지 않는 tls.ja3, 아주 짧은 cert.validfor, 443에서 host.tls != EXISTS!, Win11 환경에서 "Windows 6.1의 MSIE 7/9" 계열 http.user-agent.
워크플로 3 — Connections로 횡적 이동 보기¶
- 검색식: 사고 시간대의
ip.src == 10.0.0.0/8 && ip.dst == 10.0.0.0/8 && port.dst == [445, 135, 139, 3389, 5985, 5986, 22]. - Connections 보기, 출발
ip.src, 목적ip.dst, 세션 수로 가중치. 별 모양(한 호스트가 수십 대로 퍼짐) = 관리용 점프 박스 또는 공격자의 피벗; 사슬 모양(몇 시간에 걸친 A→B→C→D) = 사람이 직접 조작하는 이동. - 목적 필드를
smb.user나krb5.cname으로 바꾸면 → 어떤 계정이 이동했는지. 한 계정이 많은 호스트를 건드림 = 탈취된 자격증명; 서비스 계정이 워크스테이션에서 대화형으로 쓰임 = 오용. - 파고들기:
protocols == smb && smb.fn == [*.exe, *.dll, *.ps1, *PSEXESVC*] && smb.share == *ADMIN$*→ 도구 스테이징; 바로 뒤의protocols == dcerpc && ip.dst == <대상>→ 서비스 생성 (pcap을 내보내 Wiresharksvcctl.opnum == 12).port.dst == 5985→ WinRM;port.dst == 3389 && session.length > 600000→4624 type 10과 짝지어 볼 만한 RDP 세션. - 경로에 태그를 달고, 호스트 목록을 엔드포인트 팀에 넘기세요 (
psexesvc/wsmprovhost의 Prefetch,7045,4624 type 3).
워크플로 4 — Hunt: 페이로드에서 문자열 검색¶
Hunt는 SPI가 아니라 일치하는 세션의 패킷 안을 검색합니다. Arkime가 색인하지 않는 것에 쓰세요: 명령 문자열, 웹셸 파라미터, 바이너리 안의 호스트명, YARA 규칙.
- Hunt 탭 → Create a Hunt. 이름 붙이기(
hunt-webshell-cmd). 검색식(protocols == http && ip.dst == <웹서버>)과 시간 범위로 범위를 정하세요 — 범위가 좁으면 몇 시간이 몇 분으로 줄어듭니다. - 유형 선택: ASCII(대소문자 무시 옵션), hex, regex, YARA(규칙 붙여 넣기). 페이로드 방향 src / dst / both 선택. 앞부분만 필요하면 세션당 패킷 수를 제한하세요.
- 실행. 일치한 것은 태그가 붙고(
huntId:<name>) 목록으로 나오며, Hunt에서 바로 세션을 열 수 있습니다. - 흔한 Hunt:
cmd.exe /c,powershell -enc,whoami,net user,mimikatz,IEX(,FromBase64String, 유출된 API 키나 호스트명, 실행 파일이 아닌 URI의 HTTP 응답 본문 속MZ,PSEXESVC, 알려진 C2 URI 패턴, 비콘 설정용 YARA. - Hunt는 무겁습니다 — 디스크에서 pcap을 읽습니다. 좁힌 검색식으로, 한가한 시간에 돌리고, 운영 센서라면 Stats에서 I/O 영향을 확인하세요.
워크플로 5 — IOC 훑기 (IP / 도메인 / 해시 / JA3 / 인증서)¶
| IOC 유형 | 검색식 | 넓히는 방법 |
|---|---|---|
| IP | ip == 203.0.113.7 |
asn.dst == "*<같은 ASN>*", country.dst |
| 도메인 | host == *evil.example* (host.dns, host.http, host.tls, host.email 모두 포함) |
응답의 dns.ip → ip.dst == [응답들] |
| URL 경로 | http.uri == */gate.php* |
결과의 http.user-agent, http.request.header |
| JA3 / JA4 | tls.ja3 == <hash> |
SPIView ip.dst, host.tls — 이 클라이언트 소프트웨어가 또 어디로 가나 |
| 인증서 | cert.hash == <sha1> 또는 cert.serial == … 또는 cert.subject.cn == … |
cert.issuer.cn, 시간에 따라 이 인증서를 내미는 다른 IP |
| 파일 해시 | http.md5 == …, email.md5 == … (본문 해싱이 켜져 있다면) |
http.bodymagic, email.fn |
| SSH 클라이언트 | ssh.hassh == <hash> |
ssh.ver, 목적지 |
| Community ID (Zeek/Suricata에서) | communityId == "1:…" |
직접 피벗 |
재사용할 목록은 Shortcuts($c2_ips, $known_saas)로 저장해 검색식에서 참조하고, 인텔리전스는 WISE로 자동으로 넣으세요.
워크플로 6 — 유출¶
ip.src == 10.0.0.0/8 && ip.dst != 10.0.0.0/8 && databytes.src > 50000000— 내부 호스트가 50 MB 넘게 보낸 세션.ip.dst,host.tls,host.http,asn.dst에 SPIView: 클라우드 스토리지와 메일은 예상되는 것이고, VPS, 가정용 ASN, 거래하지 않는 국가는 아닙니다.- API(
fields=source.ip,destination.ip,source.bytes로/api/sessions를 부르고jq/awk로 넘김)로 일주일 치를 호스트별로 합산하거나,databytes.src로 가중치를 준ip.srcSPIGraph로 몇 시에 일어났는지 보세요. - 프로토콜 단서:
protocols == dns && databytes.src > 100000(DNS 터널),protocols == ssh && session.length > 3600000(긴 SSH 터널),port.dst == 443 && protocols != tls(443에서 다른 것),http.method == PUT,protocols == ftp. - 호스트로 넘어가기: 같은 시간대의 SRUM 앱별 바이트; 준비된 압축 파일은 $UsnJrnl.
워크플로 7 — 스캐닝과 정찰¶
ip.src == <host> && packets <= 3→ SPIViewip.dst.cnt/port.dst— 한 출발지에서 몇 분 만에 수천 개의 서로 다른 목적지나 포트.- Connections 보기
ip.src → port.dst는 수직 스캔을 포트 부채꼴로,ip.src → ip.dst는 수평 스윕을 보여 줍니다. protocols == ldap && ip.dst == <DC>→ SPIViewip.src: 수천 개의 LDAP 쿼리를 보내는 워크스테이션 = BloodHound/SharpHound.protocols == krb5 && krb5.sname == EXISTS!에서 한krb5.cname이 몇 초 만에 서로 다른 SPN 수십 개를 요청 = Kerberoasting.protocols == dns && dns.qt == PTR가 몰려 나옴 = 역방향 조회 스윕.
Arkime가 회수만이 아니라 탐지도 하게 만들기¶
| 기능 | 얻는 것 |
|---|---|
| Suricata 플러그인 | 경보가 검색 가능한 세션 필드(suricata.*)가 됨; 목록에서 세션이 강조됨 |
| WISE | 위협 피드(IP/도메인/해시/JA3/이메일), 직접 만든 CSV/JSON 조회, Zeek 기반 태그(tagger), 역방향 DNS로 실시간 보강, 그리고 우클릭 동작(VT, cont3xt, Shodan). 일치하면 검색 가능한 필드/태그가 추가됨: tags == wise-c2feed |
| Cron Queries | N분/시간마다 검색식 실행; 일치한 세션에 태그를 달거나 웹훅/알림(Slack, 이메일) 발송 — 행동 규칙용 자체 경보 (host.tls != EXISTS! && port.dst == 443 && ip.dst != $known_saas) |
| Hunts | 필요할 때 또는 주기적으로 하는 페이로드 검색; 결과에 태그 |
| Views | UI 전체를 거르는 저장된 검색식 (예: view: exclude-internal) — 잡음을 기본으로 숨기는 "분석가 뷰"를 만드세요 |
| Notifiers | Cron Query / Hunt 결과가 가는 곳 |
최소한의 탐지 태세: Suricata ET Open 규칙 연동; 피드 2–3개와 IOC 목록을 넣은 WISE; 위 행동용 Cron Query 네다섯 개; SaaS/업데이트 트래픽을 숨기는 View; 현재 위협의 문자열을 찾는 주간 Hunt.
"없다"를 믿기 전 상태 점검¶
- Stats → Capture:
Dropped packets,Overloaded drops,Fragments dropped— 0이 아니면 세션이 빠지고 Hunt가 망가진 것입니다. - Stats → ES: 클러스터 상태 녹색; 샤드가 빨간색이 아님; 디스크 워터마크에 도달하지 않음 (도달하면 쓰기가 조용히 멈춤).
- Files: 관심 있는 시간대의 pcap 파일이 아직 있나?
freeSpaceG때문에 이미 삭제됐을 수 있습니다 — pcap 없는 SPI는 검색은 되지만 내보낼 수는 없습니다. - 센서 위치: NAT 안쪽이면 실제 클라이언트 IP가, 바깥이면 방화벽이 보입니다. 암호화된 동서 트래픽(IPsec, WireGuard)은
esp/udp 51820덩어리로 보입니다. - 시간: 센서가 NTP로 동기화돼 있나? 알려진 사건의 타임스탬프를 호스트 로그와 비교하세요.
참고 자료¶
- Arkime — Hunts, Cron Queries, Views, WISE, Suricata plugin (docs & settings)
- WISE service documentation
- Community ID (Zeek / Suricata / Arkime shared flow hash)
- 65sch00l lessons 4–8 — Arkime advanced, Zeek+Arkime+Suricata integration, capstone
- 관련 페이지: 검색 문법 · 비코닝과 C2 · Zeek conn.log · Wireshark와 tshark