📚 네트워크 · 패킷 분석 › 03. Wireshark 패킷 분석 — 108편
이전 글: 107. Capture Filter와 Display Filter · 다음 글: 109. Statistics로 전체 흐름 파악 — Conversations·Endpoints·Protocol Hierarchy
Display Filter는 해석이 끝난 필드를 조건으로 패킷을 골라 보여 주는 식입니다. Capture Filter(BPF)와의 차이, 대표적인 의도별 필터, != 같은 자주 하는 실수는 107. Capture Filter와 Display Filter에서 정리했습니다. 이 글은 필터 문법 자체를 요소별로 분해합니다.
필터식의 기본 형태는 다음 세 가지입니다.
| 형태 | 예 | 의미 |
|---|---|---|
| 프로토콜·필드 존재 | dns, http.authorization | 그 계층·필드가 있는 패킷 |
| 필드 비교 | ip.ttl < 10 | 필드 값이 조건을 만족 |
| 논리 결합 | dns and not udp.port == 53 | 조건 조합 (and or not, 기호 && !와 OR 기호도 사용 가능) |
필드 이름은 105. Packet Details 구조처럼 Details에서 선택하면 상태 표시줄에 나타납니다.
필터는 패킷마다 해석 결과(필드 트리)에 대해 평가됩니다.
필터 문자열 입력
↓ 문법 검사 (필터 바 색: 초록=유효, 노랑=유효하지만 의도와 다를 수 있음, 빨강=오류)
필드 이름 → 필드 타입 확인 (정수, IP, MAC, 문자열, 바이트열 ...)
↓ 값 표기를 타입에 맞게 변환 (192.168.10.0/24 → 주소 범위)
패킷 N 해석 → 필드 트리에서 해당 필드의 모든 값 조회
↓ 값이 여러 개면 "하나라도 만족하면 참" (== 기준)
참 → 표시 / 거짓 → 숨김 (파일에는 그대로 남음)
필드 값이 여러 개일 수 있다는 점이 중요합니다. ip.addr는 출발지·목적지 두 값을 가지므로 ip.addr == 192.168.10.20은 둘 중 하나만 맞아도 참입니다. 터널이나 ICMP 오류처럼 IP 헤더가 두 개인 패킷에서는 ip.src도 값이 두 개가 됩니다.
연산자
| 연산자 | 예 | 설명 |
|---|---|---|
== != > < >= <= | frame.len >= 1000 | 비교. eq, ne, gt, lt, ge, le로도 표기 |
contains | http.user_agent contains "curl" | 문자열·바이트열 포함 (대소문자 구분) |
matches | dns.qry.name matches "(?i)\\.top$" | 정규식(PCRE 계열). 대소문자 처리는 버전별로 달라 (?i)를 명시하는 편이 안전 |
in {…} | tcp.port in {80 443 8000..8080} | 집합 멤버십. 범위·쉼표 표기는 버전에 따라 지원 차이 |
& | tcp.flags & 0x12 | 비트 AND 결과가 0이 아니면 참 |
[i:n] | eth.src[0:3] == 00:0c:29 | 바이트 슬라이스 (106. Packet Bytes 구조) |
필드 타입별 값 표기
| 타입 | 필드 예 | 값 표기 예 |
|---|---|---|
| 정수 | tcp.dstport, ip.ttl | 443, 0x1bb |
| IPv4 / IPv6 | ip.src, ipv6.dst | 192.168.10.0/24, fe80::/10 |
| MAC | eth.src | 00:0c:29:aa:bb:cc |
| 문자열 | http.host, dns.qry.name | 큰따옴표 필수: "example.com" |
| Boolean·플래그 | tcp.flags.syn | 1 또는 0 |
| 바이트열 | tcp.payload | 47:45:54:20 |
함수: len(http.request.uri) > 200(길이), count(ip.src) > 1(필드 개수), lower(http.host) contains "admin"(소문자 변환) 등을 쓸 수 있습니다. Wireshark 4.0 이상에서는 ip.src#2처럼 몇 번째 계층의 필드인지 지정하는 문법도 지원합니다(버전 확인 필요).
SOC 분석 질문을 필터로 옮긴 예시(값은 환경마다 다름)입니다.
| 분석 질문 | Display Filter |
|---|---|
| 내부에서 외부로 새 연결을 시도했나? | tcp.flags == 0x002 && ip.src == 192.168.10.0/24 && not (ip.dst == 10.0.0.0/8 or ip.dst == 172.16.0.0/12 or ip.dst == 192.168.0.0/16) |
| 비정상적으로 긴 URI가 있나? | http.request && len(http.request.uri) > 200 |
| 경로 조작 문자열이 있나? | http.request.uri contains "../" |
| 평문 인증 정보가 오갔나? | http.authorization or ftp.request.command == "PASS" |
| TLS 접속 대상 이름은? | tls.handshake.extensions_server_name |
| 헤더가 두 번 나오는 패킷은? | count(ip.src) > 1 |
표 안에서는 가독성을 위해 and·or·not 단어 연산자를 사용했습니다. 기호(&&, ! 등)와 의미는 같습니다.
실습 예시 — tshark에서 같은 필터를 -Y로 적용합니다(Rocky/Ubuntu 공통).
F=/tmp/lab.pcapng
# 긴 URI 요청의 출발지·호스트·길이
tshark -r $F -Y 'http.request && len(http.request.uri) > 200' -T fields \
-e frame.time -e ip.src -e http.host -e http.request.uri
# 정규식으로 특정 TLD 질의 찾기
tshark -r $F -Y 'dns.flags.response == 0 && dns.qry.name matches "(?i)\\.(top|xyz)$"' \
-T fields -e ip.src -e dns.qry.name
# 응답을 받지 못한 DNS 질의: 뒤쪽 프레임을 참조하는 필드는 2-pass(-2)가 필요
tshark -r $F -2 -Y 'dns.flags.response == 0 && !dns.response_in' -T fields \
-e frame.number -e ip.src -e dns.qry.name
dns.response_in, icmp.resp_in처럼 뒤에 나오는 프레임을 가리키는 필드는 1-pass 처리에서 아직 계산되지 않았을 수 있습니다. tshark에서 이런 필드로 필터링할 때는 -2를 붙이고, 1-pass 읽기 필터 -R도 -2와 함께 사용합니다.
자주 쓰는 필터는 필터 바 왼쪽의 북마크 아이콘으로 저장하거나, 필터 바 오른쪽 + 버튼으로 필터 버튼을 만들어 한 번에 적용할 수 있습니다.
📷 [실습 화면 삽입 위치] 필터 바가 초록(유효), 노랑(경고), 빨강(오류)으로 바뀌는 세 가지 예시와 상태 표시줄의 Displayed 패킷 수
📷 [실습 화면 삽입 위치] 필터 바
+버튼으로 "외부 SYN", "긴 URI" 등 SOC용 필터 버튼을 만든 화면
http 필터는 HTTP로 해석된 패킷만 보여 주므로 비표준 포트·암호화 통신은 빠집니다. 문자열 필터(contains)도 인코딩(URL 인코딩, 대소문자 변형)으로 쉽게 우회됩니다.matches와 (?i), lower()로 변형에 대비하되, 인코딩된 값은 디코딩 후 확인해야 합니다.File → Export Specified Packets)하면 전후 맥락(DNS 질의, 선행 연결)이 빠질 수 있으므로 원본 pcap은 별도로 보존합니다.Display Filter는 IDS·SIEM이 올린 이벤트를 패킷으로 검증하는 질문을 표현하는 도구입니다.
| 출발 정보 | 필터로 옮기는 방법 | 확인할 질문 |
|---|---|---|
| IDS Alert (출발지·목적지·포트·시각) | ip.addr == A && ip.addr == B && tcp.port == P 후 시간 범위 확인 | Alert 시각 전후에 실제 요청·응답이 있었나? |
| 방화벽 허용 로그 | tcp.stream eq N으로 세션 고정 | 허용된 세션에서 어떤 데이터가 오갔나? |
| 위협 인텔리전스 도메인·IP | dns.qry.name == "...", ip.addr in {...} | 내부 누가, 언제 처음 접촉했나? |
오탐 주의: ip.addr 한 조건으로 "해당 IP와 통신했다"고 결론 내리면, ICMP 오류 속 원본 헤더나 터널 안쪽 헤더에만 그 IP가 있는 경우까지 포함됩니다. Details에서 어느 계층의 값인지 확인한 뒤 결론을 냅니다. 필터 결과를 통계로 보는 방법은 109. Statistics로 전체 흐름 파악 — Conversations·Endpoints·Protocol Hierarchy에서 이어집니다.
==는 그중 하나만 맞아도 참입니다.contains(부분 일치), matches(정규식), in {}(집합), 슬라이스, len()·count()·lower() 함수를 조합해 질문을 표현합니다.dns.response_in 등)로 필터링할 때는 -2를 사용합니다.