📚 네트워크 · 패킷 분석 › 03. Wireshark 패킷 분석 — 140편
이전 글: 139. FTP Packet 분석 · 다음 글: 141. SMB Traffic 이해
SSH가 TCP 22 위에서 버전 교환 → 키 교환 → 인증 → 채널로 진행하는 구조와 셸·SFTP·포트 포워딩이 한 연결에 함께 실리는 방식은 65. SSH에서 다뤘습니다. 이 글은 Wireshark에서 SSH 세션을 열었을 때 무엇을 읽을 수 있고 무엇은 읽을 수 없는가를 다룹니다.
| 구간 | 암호화 여부 | Wireshark에서 보이는 것 |
|---|---|---|
| 식별 문자열 교환 | 평문 | SSH-2.0-OpenSSH_x.x 같은 소프트웨어·버전 |
| KEXINIT 교환 | 평문 | 양쪽이 제시한 키 교환·암호·MAC·압축 알고리즘 목록 |
| 키 교환(DH/ECDH) | 평문(공개값) | 공개 키 값, 서버 호스트 키 |
| NEWKEYS 이후 | 암호화 | 길이·방향·시간만 보임 (인증 성공 여부, 명령 내용은 안 보임) |
Client 192.168.10.20:52000 Server 192.168.10.30:22
│── SSH-2.0-OpenSSH_x (ssh.protocol) ──────────→│
│←────────────── SSH-2.0-OpenSSH_y ─────────────│
│── KEXINIT (msg 20) : 알고리즘 목록 ───────────→│
│←─────────────── KEXINIT (msg 20) ─────────────│
│── ECDH/DH Init (msg 30) ──────────────────────→│
│←─────── ECDH/DH Reply (msg 31) + 호스트 키 ────│
│── NEWKEYS (msg 21) ↔ NEWKEYS (msg 21) ─────────│
↓ 이후 모든 메시지는 "Encrypted packet"
│══ 사용자 인증 · 채널 열기 · 셸/SFTP 데이터 ═════│ ← 내용 확인 불가
frame contains "SSH-2.0-"로 찾아 확인합니다./var/log/secure 또는 /var/log/auth.log)로 확인합니다.평문 구간에서 읽는 필드 (버전에 따라 이름이 다를 수 있음)
| 필드 | 의미 | 활용 |
|---|---|---|
ssh.protocol | 식별 문자열 | 클라이언트 종류(OpenSSH, PuTTY, 라이브러리 등) |
ssh.message_code | 메시지 번호 (20, 21, 30, 31 등) | 키 교환 단계 위치 확인 |
ssh.kex_algorithms | 키 교환 알고리즘 목록 | 오래된 알고리즘 제시 여부 |
ssh.encryption_algorithms_client_to_server | 암호 알고리즘 목록 | 약한 암호 협상 가능성 |
ssh.kex.hassh / ssh.kex.hasshserver | 알고리즘 목록의 해시(HASSH) | 같은 도구·라이브러리끼리 묶기 |
HASSH는 클라이언트가 KEXINIT에 넣은 알고리즘 목록을 MD5로 요약한 값으로, 같은 버전의 같은 클라이언트는 같은 값이 나오는 경향이 있습니다. 식별 문자열은 쉽게 바꿀 수 있지만 알고리즘 목록까지 맞추기는 상대적으로 번거롭기 때문에 보조 지표로 씁니다.
암호 구간에서 추정할 수 있는 세션 성격 (추정이며 확정 근거가 아님)
| 관찰 | 추정 가능한 성격 |
|---|---|
| 작고 비슷한 크기의 패킷이 사람 타이핑 간격으로 오감 | 대화형 셸 |
| 한 방향으로 큰 패킷이 연속 | 파일 전송(SCP/SFTP), 업로드인지 다운로드인지는 방향으로 판단 |
| 키 교환 직후 짧게 끝나는 연결이 반복 | 인증 실패 반복 또는 배너 수집 |
| 몇 시간 이상 유지되며 트래픽이 드문드문 | 터널·포트 포워딩, 유지되는 관리 세션 |
실습 예시 — 본인 소유 실습 서버에 SSH로 접속해 세션 유형별로 캡처를 비교합니다. 명령과 출력은 예시(값은 환경마다 다름)입니다. 서버 로그 경로는 Rocky /var/log/secure, Ubuntu /var/log/auth.log입니다.
# 캡처 (Rocky/Ubuntu 공통)
sudo tcpdump -i ens33 -nn -w /tmp/ssh.pcapng 'tcp port 22'
# 다른 터미널: (a) 대화형 접속 후 몇 개 명령 입력, (b) 파일 전송
ssh user@192.168.10.30
scp ./sample.bin user@192.168.10.30:/tmp/
# 1) 연결별 식별 문자열
tshark -r /tmp/ssh.pcapng -Y 'ssh.protocol' -T fields -e tcp.stream -e ip.src -e ssh.protocol
# 2) 클라이언트 KEXINIT의 HASSH (필드가 없는 버전이면 알고리즘 목록 필드로 대체)
tshark -r /tmp/ssh.pcapng -Y 'ssh.message_code == 20 && tcp.dstport == 22' \
-T fields -e tcp.stream -e ssh.kex.hassh
# 3) 연결별 방향별 바이트와 지속 시간
tshark -r /tmp/ssh.pcapng -q -z conv,tcp
3)번 결과의 형식 예시입니다.
연결 서버→클라이언트 클라이언트→서버 시작(초) 지속(초)
192.168.10.20:52000 <-> 192.168.10.30:22 약 40 KB 약 15 KB 5.1 120.4 ← 대화형
192.168.10.20:52010 <-> 192.168.10.30:22 약 20 KB 약 10 MB 130.2 3.8 ← 한 방향 대량
실제 -z conv,tcp 출력은 방향별 Frames·Bytes, 합계, Relative Start, Duration 열로 나오며 위는 읽기 쉽게 줄인 형식입니다.
(a)는 양방향 바이트가 비슷하고 오래 유지되며, (b)는 클라이언트 → 서버 방향 바이트가 파일 크기만큼 큽니다. 패킷 크기 분포는 Statistics → Packet Lengths, 시간 흐름은 Statistics → I/O Graphs로 비교합니다.
📷 [실습 화면 삽입 위치] 클라이언트 KEXINIT 패킷의 Details에서 식별 문자열, 알고리즘 목록, HASSH 값이 펼쳐진 화면
📷 [실습 화면 삽입 위치] 대화형 세션과 SCP 세션을 I/O Graph에 함께 그려 패킷 크기·빈도 차이가 보이는 화면
| 관찰 | 정상일 수 있는 경우 | 추가 확인이 필요한 경우 |
|---|---|---|
| 식별 문자열이 라이브러리·스크립트 형태 | 자동화 도구, 백업 시스템 | 평소 없던 클라이언트가 여러 서버에 접속 |
| 짧은 SSH 연결이 한 출발지에서 반복 | 모니터링의 배너 확인 | 무차별 대입 형태 (232. SSH Brute Force에서 다룸) |
| 내부 → 외부 22번 장시간 연결 | 승인된 원격 관리 | 터널·포트 포워딩을 통한 우회 통신 |
| 22번이 아닌 포트에서 SSH 식별 문자열 | 정책상 포트 변경 | 443 등 허용 포트에 숨긴 SSH |
암호 구간의 내용은 볼 수 없으므로, "무엇을 했는가"는 호스트 로그와 명령 기록으로, 패킷은 "언제·얼마나·어디로"를 확인하는 데 씁니다.
| 흔적 위치 | 확인할 수 있는 것 |
|---|---|
| 패킷 캡처 | 식별 문자열, 알고리즘 목록·HASSH, 세션 길이, 방향별 바이트 |
| 서버 인증 로그 | 계정, 성공·실패, 인증 방식 (패킷으로는 확인 불가한 부분) |
| 방화벽 로그 | 22번(또는 비표준 포트) 연결 허용·차단, 세션 시간 |
| IDS/IPS | 짧은 시간 다수 연결 임계치 이벤트, 비표준 포트의 SSH 식별 |
관제자가 확인할 질문
오탐 주의: 형상 관리·백업·모니터링 시스템은 짧은 SSH 연결을 주기적으로 반복합니다. HASSH는 클라이언트 버전이 바뀌면 달라지므로 단독 탐지 근거로 쓰지 않습니다.
ssh.protocol과 KEXINIT 알고리즘 목록, HASSH로 클라이언트 종류를 구분할 수 있습니다.frame contains "SSH-2.0-"로 찾고 Decode As로 해석합니다.