📚 네트워크 · 패킷 분석 › 03. Wireshark 패킷 분석 — 121편
이전 글: 120. UDP Header 분석 · 다음 글: 122. TCP Connection 종료 분석
3-Way Handshake가 왜 세 단계인지, 서버의 SYN 대기열이 어떻게 동작하는지는 41. TCP 3-Way Handshake — 연결이 성립했다는 증거과 44. SYN, SYN/ACK, ACK에서 다뤘습니다. 이 글은 캡처 파일 안에서 Handshake를 찾아내고, 성립했는지·어디서 실패했는지를 Wireshark 필드로 판별하는 방법에 집중합니다. 개별 패킷(SYN, SYN/ACK, ACK)의 세부 필드는 123. SYN Packet 분석~125. ACK Packet 분석에서 따로 봅니다.
Handshake 분석에 쓰는 Wireshark 도구와 필드입니다.
| 도구·필드 | 위치 | 알 수 있는 것 |
|---|---|---|
tcp.stream | TCP 트리 [Stream index] | 세 패킷이 같은 연결인지 |
tcp.flags.syn, tcp.flags.ack | Flags 트리 | 각 패킷의 역할 |
tcp.seq_raw, tcp.ack_raw | SEQ 번호 원래 값 | SYN/ACK의 ack가 SYN seq + 1인지 |
tcp.analysis.initial_rtt | [SEQ/ACK analysis] → [iRTT] | 첫 SYN부터 세 번째 ACK까지 걸린 시간 |
tcp.completeness | [Conversation completeness] | 연결에서 관찰된 단계의 합 (Wireshark 3.6 이후) |
| Statistics → Flow Graph | 메뉴 | 방향과 순서를 화살표로 확인 |
Wireshark가 Handshake를 인식하는 순서입니다.
패킷 A SYN (0x002) client:51000 → server:443 seq_raw = x
↓ 같은 4-tuple → tcp.stream = N 부여, 상대 seq 0 기준 설정
패킷 B SYN/ACK (0x012) server:443 → client:51000 seq_raw = y, ack_raw = x+1
↓ 반대 방향도 상대 seq 0 기준 설정
패킷 C ACK (0x010) client → server seq = 1, ack = 1 (상대값)
↓ 세 패킷 확인
iRTT = C 시각 − A 시각 → 이후 패킷의 [SEQ/ACK analysis]에 표시
completeness 비트: SYN(1) + SYN-ACK(2) + ACK(4) = 7 누적
Handshake 결과 패턴 (같은 tcp.stream 안에서 보이는 패킷)
| 보이는 패킷 | 판정 | 비고 |
|---|---|---|
| SYN → SYN/ACK → ACK → 데이터 | 정상 성립 | iRTT 표시됨 |
| SYN → RST/ACK | 포트 닫힘 또는 reject | 127. RST Packet 분석 |
| SYN, SYN(재전송), SYN… | 응답 없음(drop·경로 문제) | tcp.analysis.retransmission이 SYN에 붙음 |
| SYN → SYN/ACK → RST | 클라이언트가 수립 거부 | SYN 스캔의 전형(143. SYN Scan Packet 분석) |
| SYN → SYN/ACK, SYN/ACK(재전송)… | 세 번째 ACK 미도착 | 위조 출발지, 비대칭 경로 |
| SYN/ACK 또는 ACK부터 시작 | 캡처가 연결 도중 시작 | 판정 보류 |
Conversation completeness 값 (비트 합산, 예시)
| 값 | 포함된 단계 | 해석 |
|---|---|---|
| 1 | SYN | 응답을 받지 못한 시도 |
| 3 | SYN + SYN-ACK | 수립 직전에서 멈춤 |
| 7 | SYN + SYN-ACK + ACK | 성립했지만 데이터·종료 없음 |
| 15 | 위 + DATA(8) | 데이터 교환, 종료 미관찰 |
| 31 | 위 + FIN(16) | 성립부터 정상 종료까지 전부 관찰 |
| +32 | RST 포함 | RST가 한 번이라도 관찰됨 |
값은 Details에 [Conversation completeness: Complete, WITH_DATA (31)]처럼 문자열과 함께 표시됩니다. 세부 표기는 버전마다 조금씩 다르므로 문자열을 함께 확인합니다.
실습 예시 — 본인 소유 VM 두 대(클라이언트 192.168.10.20, 서버 192.168.10.30)에서 열린 포트와 닫힌 포트에 한 번씩 접속합니다. 명령과 출력은 예시(값은 환경마다 다름)입니다.
# 클라이언트: 캡처 시작 (Rocky: dnf install wireshark-cli / Ubuntu: apt install tshark)
sudo tshark -i ens33 -f "tcp and host 192.168.10.30" -w /tmp/hs.pcapng &
curl -s -o /dev/null http://192.168.10.30/ # 열린 포트(80)
timeout 3 bash -c '</dev/tcp/192.168.10.30/8081' # 닫힌 포트
sudo pkill -INT tshark
# 연결 시도(SYN)와 수락(SYN/ACK)만 보기
tshark -r /tmp/hs.pcapng -Y 'tcp.flags.syn == 1' -T fields -E header=y \
-e frame.number -e frame.time_relative -e tcp.stream -e ip.src -e tcp.srcport \
-e tcp.dstport -e tcp.flags.str -e tcp.seq_raw -e tcp.ack_raw
# 연결별 iRTT (Handshake가 끝난 연결만 값이 있음)
tshark -r /tmp/hs.pcapng -Y 'tcp.analysis.initial_rtt' -T fields \
-e tcp.stream -e tcp.analysis.initial_rtt | sort -u -k1,1n
# 연결별 completeness 최댓값 (-2: 두 번 읽어 누적 값을 반영)
tshark -r /tmp/hs.pcapng -2 -T fields -e tcp.stream -e tcp.completeness \
| awk '$2 > m[$1] {m[$1] = $2} END {for (s in m) print s, m[s]}' | sort -n
첫 번째 추출 결과의 형식 예시입니다.
frame time stream ip.src srcport dstport flags.str seq_raw ack_raw
1 0.000000 0 192.168.10.20 51000 80 ··········S· 3055000100 0
2 0.000412 0 192.168.10.30 80 51000 ·······A··S· 1200500000 3055000101
7 0.051230 1 192.168.10.20 51002 8081 ··········S· 2210400000 0
| 확인 포인트 | 읽는 법 |
|---|---|
frame 2의 ack_raw | frame 1 seq_raw + 1 → 같은 연결의 정상 응답 |
| stream 1에 SYN/ACK 없음 | 8081은 수락되지 않음. 전체 패킷에서 RST/ACK 확인 |
📷 [실습 화면 삽입 위치] Statistics → Flow Graph에서 "Limit to display filter"와 Flow type "TCP Flows"를 선택해 stream 0(SYN → SYN/ACK → ACK)과 stream 1(SYN → RST/ACK)이 나란히 보이는 화면
📷 [실습 화면 삽입 위치] 세 번째 ACK 이후 패킷의 Details에서
[SEQ/ACK analysis]→[iRTT]와[Conversation completeness]가 펼쳐진 화면
| 관찰 | 가능한 해석 | 확인할 것 |
|---|---|---|
| 한 출발지에서 completeness가 1·3이거나 DATA 없이 RST만 더해진 스트림이 다수 | 포트 스캔·접속 실패 반복 | 목적지 포트 분포 (142. Port Scan Packet 분석) |
| 서버로 SYN 급증, 대부분 SYN/ACK 뒤 ACK 없음 | SYN Flood 가능성 | 출발지 수·TTL 분포 |
| 요청 없이 SYN/ACK만 대량 수신 | 우리 IP가 위조 출발지로 쓰인 Backscatter | 우리 쪽 SYN 존재 여부 |
| Handshake 성립 후 곧바로 RST | 애플리케이션 거부, IPS 차단 | RST의 TTL·출발지 (127. RST Packet 분석) |
| 흔적 위치 | 확인할 수 있는 것 |
|---|---|
| 패킷 캡처 | 세 패킷 존재 여부, iRTT, completeness |
| 방화벽 세션 로그 | 세션 생성·종료 사유, 패킷 수가 1~2개인 미성립 세션 |
| IDS/IPS | Suricata flow 이벤트의 tcp_flags, 스트림 이벤트 |
관제자가 확인할 질문
오탐 주의: 캡처가 연결 도중에 시작되면 SYN이 없어 "비정상 연결"처럼 보입니다. 첫 패킷의 시각과 캡처 시작 시각을 먼저 비교합니다. 로드밸런서 헬스체크의 짧은 연결도 정상입니다.
tcp.stream 안에서 SYN(0x002) → SYN/ACK(0x012) → ACK(0x010) 순서로 확인합니다.tcp.ack_raw가 SYN의 tcp.seq_raw + 1인지로 같은 연결의 응답인지 검증합니다.tcp.analysis.initial_rtt는 연결 성립 시간이고, 구간 비율로 캡처 지점을 추정할 수 있습니다.tcp.completeness(1·3·7·15·31, RST면 +32)로 연결이 어디까지 진행됐는지 한 번에 분류합니다.