📚 네트워크 · 패킷 분석 › 03. Wireshark 패킷 분석 — 125편
이전 글: 124. SYN/ACK Packet 분석 · 다음 글: 126. FIN Packet 분석
TCP 연결이 성립한 뒤에는 첫 SYN을 제외한 거의 모든 패킷에 ACK 비트가 켜져 있습니다. 그래서 "ACK 패킷"은 한 종류가 아니라, ACK 비트 외에 어떤 조건이 붙었는지로 역할이 나뉩니다. 이 글은 ACK 비트가 켜진 패킷을 Wireshark에서 역할별로 구분하는 방법을 다룹니다. ACK 번호 값의 계산과 검증은 130. ACK Number 분석, 중복 ACK는 132. Duplicate ACK에서 자세히 봅니다.
| 역할 | 구분 조건 | Wireshark 표시 |
|---|---|---|
| Handshake 세 번째 ACK | SYN/ACK 직후, tcp.len == 0, 상대 seq 1·ack 1 | [ACK] |
| 순수 ACK (수신 확인만) | tcp.flags == 0x010 && tcp.len == 0 | [ACK] |
| 데이터 동반 ACK | tcp.len > 0 (흔히 PSH 포함 0x018) | [PSH, ACK] 또는 [ACK] + 데이터 |
| FIN 확인 ACK | 상대 FIN 직후, ack = FIN seq + 1 | [ACK] |
| Keep-Alive | seq가 다음 seq보다 1 작고 len 0 또는 1 | [TCP Keep-Alive] |
| Keep-Alive 응답 | Keep-Alive에 대한 ACK | [TCP Keep-Alive ACK] |
| Window Update | 데이터 없이 Window 값만 바뀜 | [TCP Window Update] |
| 중복 ACK | 같은 ack 번호 반복 | [TCP Dup ACK N#M] |
Wireshark가 ACK 패킷에 분석 정보를 붙이는 과정입니다.
ACK 비트가 켜진 패킷 도착
↓
tcp.ack가 가리키는 바이트까지 확인된 상대 세그먼트 찾기
↓ 찾으면
[This is an ACK to the segment in frame: N] → tcp.analysis.acks_frame
[The RTT to ACK the segment was: 0.0402 s] → tcp.analysis.ack_rtt
↓ 이전 상태와 비교
len 0이고 ack·window 같음 → Dup ACK
len 0이고 window만 커짐 → Window Update
seq = 다음 seq − 1 (len 0/1) → Keep-Alive
↓
해당 없으면 일반 ACK
tcp.analysis.ack_rtt는 데이터를 보낸 쪽에서 본 왕복 시간입니다. 수신 측의 지연 ACK(Delayed ACK) 때문에 순수 ACK는 데이터 도착 후 수십~수백 ms 늦게 나갈 수 있습니다(대기 시간은 OS마다 다름).tcp.seq가 증가하지 않습니다. 같은 seq의 ACK가 이어지는 것은 정상입니다.ACK 관련 필터 모음
| 목적 | Display Filter |
|---|---|
| 순수 ACK | tcp.flags == 0x010 && tcp.len == 0 |
| 데이터가 실린 패킷 | tcp.len > 0 |
| Keep-Alive와 응답 | tcp.analysis.keep_alive or tcp.analysis.keep_alive_ack |
| Window Update | tcp.analysis.window_update |
| ACK 왕복 시간이 큰 것 | tcp.analysis.ack_rtt > 0.2 |
| 캡처가 보지 못한 데이터를 확인한 ACK | tcp.analysis.ack_lost_segment |
연결 상태와 맞지 않는 ACK
| 보이는 모습 | 해석 | 비고 |
|---|---|---|
| SYN 없이 ACK만, 대상이 RST 응답 | 대상에 연결이 없음 → ACK 스캔 또는 오래된 세션 | 222편 |
| ACK만, 대상 응답 없음 | 상태 기반 방화벽이 버림 | 방화벽의 invalid 차단 로그 |
[TCP ACKed unseen segment] | ACK가 가리키는 데이터를 캡처가 보지 못함 | 캡처 누락·비대칭 경로 |
| 매우 짧은 간격의 ACK 반복 | ACK storm, 양쪽 seq 불일치 | 중간 장비의 세션 동기화 문제 |
실습 예시 — 본인 VM에서 파일을 내려받으며 ACK 종류를 분류합니다. 명령과 출력은 예시(값은 환경마다 다름)입니다.
# 서버(192.168.10.30): 테스트 파일과 간이 웹 서버 (실습 후 종료)
head -c 5M /dev/urandom > /tmp/test.bin && cd /tmp && python3 -m http.server 8080
# 클라이언트: 캡처 후 다운로드
sudo tshark -i ens33 -f "tcp port 8080" -w /tmp/ack.pcapng &
curl -s -o /dev/null http://192.168.10.30:8080/test.bin
sudo pkill -INT tshark
# ACK 비트 패킷을 역할별로 집계
tshark -r /tmp/ack.pcapng -Y 'tcp.flags.ack == 1' -T fields -e tcp.len -e _ws.col.Info \
| awk '{ if ($0 ~ /Keep-Alive/) k="keepalive"; else if ($0 ~ /Window Update/) k="win_update";
else if ($0 ~ /Dup ACK/) k="dup_ack"; else if ($1 > 0) k="data"; else k="pure_ack";
c[k]++ } END {for (k in c) print k, c[k]}'
# 순수 ACK가 확인한 프레임과 RTT
tshark -r /tmp/ack.pcapng -Y 'tcp.flags == 0x010 && tcp.len == 0' -T fields -E header=y \
-e frame.number -e ip.src -e tcp.ack -e tcp.analysis.acks_frame -e tcp.analysis.ack_rtt | head
firewalld(Rocky)나 ufw(Ubuntu)가 켜져 있다면 8080/tcp를 실습 동안만 허용하고 끝나면 제거합니다. 집계 결과 형식 예시입니다.
data 3620
pure_ack 1790
win_update 3
| 확인 포인트 | 읽는 법 |
|---|---|
| data가 pure_ack의 약 2배 | 수신 측이 세그먼트 두 개마다 ACK 하나를 보내는 일반적 패턴 |
| pure_ack의 출발지가 거의 클라이언트 | 다운로드 방향의 반대로 ACK가 흐름 |
| Window Update 소수 | 수신 버퍼 여유가 생겨 알린 것, 정상 |
📷 [실습 화면 삽입 위치] 순수 ACK 패킷 Details의
[SEQ/ACK analysis]에서[This is an ACK to the segment in frame]과[The RTT to ACK the segment was]가 보이는 화면
| 관찰 | 가능한 해석 | 확인할 것 |
|---|---|---|
| 한 출발지가 여러 포트로 ACK만 전송, 대상은 RST | ACK 스캔으로 방화벽 상태 확인 시도 | 응답 유무를 포트별로 정리 |
| 긴 연결에서 데이터 없이 Keep-Alive만 주기적으로 | 유휴 세션 유지, 원격 접속 도구 가능성 | 목적지, 주기, 세션 시작 시점 |
| 데이터 없는 ACK 대량 유입 | ACK Flood 가능성 | pps, 출발지 수 |
| ACK의 ack 번호가 상대가 보낸 적 없는 범위 | 조작 패킷, 세션 하이재킹 시도 가능성 | [TCP ACKed unseen segment], 캡처 누락 여부 |
| 흔적 위치 | 확인할 수 있는 것 |
|---|---|
| 패킷 캡처 | ACK 역할, acks_frame·ack_rtt, 비정상 표시 |
| 방화벽 로그 | 세션 없는 ACK 차단(invalid, out-of-state 등 장비별 명칭) |
| IDS/IPS | ACK 스캔, 스트림 이상 이벤트 |
| NetFlow | 패킷 수 대비 바이트가 매우 작은 흐름 |
관제자가 확인할 질문
tcp.stream에 SYN이 있는가)?오탐 주의: 캡처 시작 전에 열린 연결은 SYN 없이 ACK부터 보입니다. 또 [TCP ACKed unseen segment]는 대부분 캡처 누락 때문이므로 공격 판단 전에 센서의 드롭 통계를 확인합니다.
tcp.flags == 0x010 && tcp.len == 0, 데이터 동반은 tcp.len > 0으로 구분합니다.tcp.analysis.acks_frame과 ack_rtt로 어떤 세그먼트를 얼마 만에 확인했는지 봅니다.