📚 네트워크 · 패킷 분석 › 03. Wireshark 패킷 분석 — 128편
이전 글: 127. RST Packet 분석 · 다음 글: 129. Sequence Number 분석
앞선 글들에서 SYN, SYN/ACK, ACK, FIN, RST 패킷을 하나씩 봤습니다. 이 글은 시야를 넓혀 캡처 전체와 연결 단위에서 Flag를 어떻게 세고, 어떤 순서로 나타났는지로 무엇을 판단하는가를 다룹니다. Flag 각각의 뜻은 01 영역 43. TCP Flag, TCP 헤더 필드 전체는 119. TCP 헤더와 Flag 분석 — 포트·시퀀스·Flag·옵션 필드 읽는 법을 참고합니다.
Wireshark는 Flag 영역을 12비트(예약 비트 포함)로 해석하고, tcp.flags에 16진수로 보여 줍니다.
| 비트 값 | Flag | Wireshark 필드 | 자주 보는 조합 값 |
|---|---|---|---|
| 0x001 | FIN | tcp.flags.fin | FIN/ACK = 0x011 |
| 0x002 | SYN | tcp.flags.syn | SYN = 0x002, SYN/ACK = 0x012 |
| 0x004 | RST | tcp.flags.reset | RST/ACK = 0x014 |
| 0x008 | PSH | tcp.flags.push | PSH/ACK = 0x018 |
| 0x010 | ACK | tcp.flags.ack | ACK = 0x010 |
| 0x020 | URG | tcp.flags.urg | 드묾 |
| 0x040 | ECE | tcp.flags.ece | ECN 협상 SYN = 0x0C2 |
| 0x080 | CWR | tcp.flags.cwr | ECN 혼잡 신호 |
| 0x100 | AE (구 NS) | tcp.flags.ae | 구버전 Wireshark는 tcp.flags.ns |
tcp.flags.str은 ·······A··S·처럼 켜진 Flag만 글자로 보여 주는 문자열 필드라, 열(Column)로 추가해 두면 목록에서 바로 읽기 좋습니다.
Flag를 필터링하는 방법은 세 가지이며, 결과가 서로 다릅니다.
| 방식 | 예 | 걸리는 패킷 |
|---|---|---|
| 정확히 일치 | tcp.flags == 0x002 | SYN 만 켜진 패킷 (ECN SYN 0x0C2는 제외) |
| 비트 포함 | tcp.flags & 0x002 | SYN 비트가 켜진 모든 패킷 (SYN, SYN/ACK, ECN SYN …) |
| 개별 필드 비교 | tcp.flags.syn == 1 && tcp.flags.ack == 0 | 연결 시작 SYN (ECN SYN 포함) |
⚠️ tcp.flags.syn처럼 값 비교 없이 필드 이름만 쓰면 "필드가 존재하는가"를 검사하게 되어, 의도와 달리 거의 모든 TCP 패킷이 걸릴 수 있습니다. Flag 필드는 항상 == 1 / == 0으로 비교합니다.
분석은 보통 다음 순서로 진행합니다.
캡처 파일
↓
① Flag 조합 분포 세기 : 어떤 조합이 몇 개인가 (전체 모양 파악)
↓
② 핵심 비율 확인 : SYN 대비 SYN/ACK, SYN 대비 RST
↓
③ 연결(tcp.stream)별 Flag 순서 : 연결마다 어떻게 끝났는가
↓
④ 규격 밖 조합·이상 순서만 추려 상세 분석 (146편·142편·143편으로 연계)
연결 하나의 Flag 순서를 보면 그 연결이 어떻게 끝났는지 판정할 수 있습니다. 개별 스캔 판정은 142. Port Scan Packet 분석과 143. SYN Scan Packet 분석에서 자세히 다룹니다.
| 연결별 Flag 순서 (C=클라이언트, S=서버) | 판정 | 참고 |
|---|---|---|
| C:SYN → S:SYN/ACK → C:ACK → 데이터 → FIN 교환 | 정상 연결·정상 종료 | 가장 흔한 모양 |
| C:SYN → S:RST/ACK | 포트 닫힘 (LISTEN 없음) | 방화벽 reject일 수도 있음 |
| C:SYN → (재전송) → 응답 없음 | 필터링 또는 호스트 없음 | 재전송 간격이 점점 늘어남 |
| C:SYN → S:SYN/ACK → C:RST | Handshake 미완료 후 끊음 | Half-open 스캔 형태 |
| C:SYN → S:SYN/ACK → C:ACK → C:RST (데이터 없음) | 연결만 확인하고 끊음 | Connect 스캔·헬스체크 형태 |
| 앞선 SYN 없이 S:SYN/ACK 또는 RST만 수신 | 캡처가 도중에 시작됐거나, 위조된 출발지로 인한 응답(Backscatter) | 캡처 시작 시각 먼저 확인 |
캡처 전체에서 보는 핵심 비율입니다.
| 비율 | 정상에 가까운 모습 | 이상을 의심할 모습 |
|---|---|---|
| 연결 시작 SYN : SYN/ACK | 비슷함 (대부분 응답) | SYN이 훨씬 많음 → 응답 없는 대상에 대량 시도 |
| SYN : RST | RST가 적음 | RST가 SYN과 비슷 → 닫힌 포트 대량 접근 |
| FIN 종료 : RST 종료 | FIN 종료가 많음 | 데이터 없는 RST 종료가 많음 → 확인 후 끊기 반복 |
분석 방법 예시 — 이미 확보한 캡처 파일에서 Flag 조합 분포와 비율을 셉니다. 명령과 출력은 예시(값은 환경마다 다름)이며, tshark는 Rocky wireshark-cli, Ubuntu tshark 패키지에 들어 있습니다.
# ① Flag 조합 분포 (16진수 값 기준)
tshark -r sample.pcapng -Y tcp -T fields -e tcp.flags | sort | uniq -c | sort -rn
# ② 1분 단위로 SYN, SYN/ACK, RST 개수 비교
tshark -r sample.pcapng -q -z io,stat,60,\
"tcp.flags.syn==1 && tcp.flags.ack==0",\
"tcp.flags.syn==1 && tcp.flags.ack==1",\
"tcp.flags.reset==1"
# ③ 연결별 Flag 순서를 한 줄로 이어 보기
tshark -r sample.pcapng -Y tcp -T fields -e tcp.stream -e tcp.flags.str \
| awk -F'\t' '{gsub("·","",$2); s[$1]=s[$1]" "$2} END{for(k in s) print k":"s[k]}' \
| sort -n | head
①의 출력 형식 예시:
1840 0x0010
612 0x0018
405 0x0002
41 0x0012
362 0x0014
38 0x0011
이 예시라면 SYN(0x0002) 405개에 SYN/ACK(0x0012)는 41개뿐이고 RST/ACK(0x0014)가 362개입니다. "시도한 연결 대부분이 닫힌 포트에서 거절됐다"는 모양이므로, ③에서 해당 스트림들의 출발지·목적지 포트를 확인하는 순서로 이어 갑니다.
Wireshark 화면에서는 Packet List 열 머리글을 오른쪽 클릭 → Column Preferences에서 tcp.flags.str 열을 추가하고, tcp.stream eq 5처럼 연결 하나를 골라 순서를 읽습니다.
📷 [실습 화면 삽입 위치] Packet List에
tcp.flags.str사용자 정의 열을 추가하고tcp.stream eq N으로 한 연결의 SYN → SYN/ACK → ACK … FIN 순서가 보이는 화면
0x0C2(SYN+ECE+CWR)는 ECN을 지원하는 호스트의 정상 SYN이므로, Flag가 여러 개 켜졌다는 이유만으로 XMAS 형태로 오인하지 않습니다. 규격상 나올 수 없는 조합은 146. 비정상 TCP Traffic 분석에서 다룹니다.| 흔적 위치 | Flag로 확인할 수 있는 것 |
|---|---|
| 패킷 캡처 | 조합 분포, 연결별 순서, 비율 |
| 방화벽 로그 | 세션 종료 사유(예: TCP FIN / TCP RST / 타임아웃), invalid 상태 차단 |
| IDS/IPS | Flag 조건 기반 룰(flags: 키워드) 탐지 (282. Signature Rule에서 다룸) |
| NetFlow 등 흐름 기록 | 흐름에 누적된 Flag의 OR 값 (예: 데이터 없이 S·R만 있는 흐름) |
관제자가 확인할 질문
오탐 주의: 로드밸런서·헬스체크는 짧은 연결 후 RST로 끊는 경우가 많아 스캔처럼 보일 수 있습니다. 또 캡처 시작·종료 시점에 걸친 연결은 순서가 잘려 보이므로, 캡처 경계 근처의 스트림은 판정에서 따로 다룹니다.
tcp.flags 값(예: SYN/ACK 0x012)과 개별 필드, tcp.flags.str 문자열로 보여 줍니다.== 0x002), 비트 포함(& 0x002), 개별 필드 비교(tcp.flags.syn == 1)의 결과가 다르며, 필드 이름만 쓰는 필터는 피합니다.