📚 네트워크 · 패킷 분석 › 02. 포트 · 프로토콜 분석 — 66편
이전 글: 65. SSH · 다음 글: 67. SMTP
참고(리눅스 시스템 기초): SSH 구조 이해 — Telnet을 대체하는 암호화 원격 접속
Telnet은 오래된 원격 접속 프로토콜이지만 아직도 관제에서 자주 등장합니다.
이 글의 목표는 "Telnet은 위험하다"를 외우는 것이 아니라, 직접 캡처해서 평문이 어떻게 보이는지 확인하고, 같은 문제를 가진 다른 평문 프로토콜까지 한 번에 정리하는 것입니다.

그림 1. 같은 원격 접속이라도 평문 프로토콜은 도청 시 모든 내용이 드러납니다
| 항목 | 내용 |
|---|---|
| 포트 | TCP 23 |
| 암호화 | 없음 (인증 정보·명령·출력 모두 평문) |
| 인증 | 서버의 login 프로그램이 계정/비밀번호를 받음 (시스템 PAM 사용) |
| 전송 단위 | 구현·모드에 따라 한 글자씩 전송되는 경우가 많음 |
| 제어 방식 | IAC(0xFF)로 시작하는 옵션 협상(DO / DON'T / WILL / WON'T) |
| 대체 | SSH (TCP 22) |
Telnet 연결 직후에는 사람이 입력한 데이터가 아니라 양쪽 터미널 설정을 맞추는 제어 명령이 먼저 오갑니다. 예를 들어 "에코는 서버가 하겠다(WILL ECHO)", "터미널 종류를 알려 달라(DO TERMINAL-TYPE)" 같은 내용입니다. 캡처 초반에 보이는 알아보기 힘든 바이트들이 이것입니다. 필드 단위 해석은 (03 Wireshark 시리즈에서 다룸)
Telnet만의 문제가 아닙니다. 아래 프로토콜은 기본 형태로는 암호화가 없습니다.
| 평문 프로토콜 | 포트 | 노출되는 것 | 암호화 대체 |
|---|---|---|---|
| Telnet | TCP 23 | 계정·비밀번호·명령 | SSH (22) |
| FTP | TCP 21 (+데이터 포트) | 계정·비밀번호·파일 내용 | SFTP(22), FTPS(21 명시적 / 990 암묵적) |
| HTTP | TCP 80 | 쿠키·폼 입력·Basic 인증 | HTTPS (443) |
| SMTP | TCP 25 | 메일 본문(STARTTLS 미사용 시) | STARTTLS, 587(제출) / 465 |
| POP3 | TCP 110 | 계정·비밀번호·메일 | POP3S (995) |
| IMAP | TCP 143 | 계정·비밀번호·메일 | IMAPS (993) |
| SNMP v1/v2c | UDP 161 | Community 문자열·장비 정보 | SNMPv3 (authPriv) |
| LDAP | TCP 389 | Simple Bind 비밀번호 | LDAPS (636), StartTLS |
| rlogin / rsh | TCP 513 / 514 | 명령, 호스트 기반 신뢰 | SSH |
FTP, 메일, SNMP는 이어지는 12·13·16편에서 각각 다룹니다.
클라이언트 (192.168.10.20:51514) 서버 (192.168.10.10:23)
│ │
│── SYN / SYN-ACK / ACK (3-Way Handshake) ─────────│
│ │
│←─ IAC DO TERMINAL-TYPE, IAC WILL ECHO ... ───────│ 옵션 협상
│── IAC WILL TERMINAL-TYPE ... ───────────────────→│
│ │
│←─ "login: " ─────────────────────────────────────│
│── "t" → "e" → "s" → "t" ... (한 글자씩) ────────→│ ← 평문
│←─ "t" → "e" → "s" → "t" (서버 에코) ──────────────│
│←─ "Password: " ──────────────────────────────────│
│── "P" → "a" → "s" → "s" ... ───────────────────→│ ← 비밀번호도 평문 (에코만 없음)
│ │
│←─ 셸 프롬프트 ───────────────────────────────────│
│── "i" "d" "\r\n" ──────────────────────────────→│ ← 명령 평문
│←─ "uid=1001(testuser) ..." ──────────────────────│ ← 결과 평문
↓
경로상의 누구나(미러 포트, 감염된 중간 장비, 같은 무선망) 읽을 수 있음
비밀번호 입력 시 화면에 글자가 안 보이는 것은 서버가 에코를 보내지 않을 뿐이고, 클라이언트 → 서버 방향으로는 그대로 전송됩니다. "화면에 안 보인다 = 암호화되었다"가 아닙니다.
| 구분 | Telnet | SSH |
|---|---|---|
| 연결 직후 | 옵션 협상 후 바로 로그인 프롬프트 | 버전 문자열 교환 → 키 교환 → 암호화 채널 |
| 서버 신원 확인 | 없음 | 호스트 키 지문 확인 |
| 캡처 시 보이는 것 | 계정·비밀번호·명령·출력 전부 | 버전 문자열(SSH-2.0-...) 이후 암호문 |
| 인증 방식 | 비밀번호 | 비밀번호, 공개키 등 |
SSH의 인증·설정 보안은 기존 글에서 정리했습니다: 12. SSH 인증 방식, 13. SSH 보안 설정
실습 환경: 본인 소유 Linux VM (Rocky Linux 또는 Ubuntu), 인터페이스는 ens33을 예시로 사용합니다. 평문 노출 위험이 있으므로 루프백(127.0.0.1)에서만 접속하고, 실습 전용 테스트 계정을 쓰며, 실습 후 Telnet 서버를 반드시 제거합니다.
# 실습 전용 계정 (실제 쓰는 비밀번호와 다른 값 사용)
sudo useradd -m testuser
sudo passwd testuser
# Rocky Linux: telnet 서버는 systemd 소켓 활성화 방식
sudo dnf install -y telnet-server telnet
sudo systemctl start telnet.socket
# Ubuntu: inetd 방식으로 동작 (버전에 따라 telnetd 또는 inetutils-telnetd 패키지)
sudo apt install -y telnetd telnet
# 23번 포트 대기 확인
sudo ss -tlnp | grep ':23 '
방화벽은 열지 않습니다. 루프백 접속만 하므로 외부에 노출할 필요가 없습니다.
# 터미널 1: 루프백의 23번 포트를 ASCII로 출력
sudo tcpdump -i lo -nn -A 'tcp port 23'
# 터미널 2: 로컬 접속 후 testuser로 로그인, id 명령 실행 후 exit
telnet 127.0.0.1
📷 [실습 화면 삽입] tcpdump -A 출력에서
login:, 입력한 계정 문자,Password:이후 비밀번호 글자가 한 글자씩 보이는 부분 (비밀번호 부분은 캡처 이미지에서 가림 처리)
# Rocky
sudo tail -n 20 /var/log/secure
# Ubuntu
sudo tail -n 20 /var/log/auth.log
# Rocky
sudo systemctl stop telnet.socket
sudo dnf remove -y telnet-server
# Ubuntu (버전에 맞는 패키지명 사용)
sudo apt remove --purge -y telnetd
# 23번 포트가 사라졌는지 확인
sudo ss -tlnp | grep ':23 ' || echo "23/tcp not listening"
# 테스트 계정 제거
sudo userdel -r testuser
📷 [실습 화면 삽입] 정리 후
ss -tlnp에서 23번 포트가 없는 화면
tcpdump -A 출력 (형식 예시(값은 환경마다 다름))
10:30:01.201 IP 127.0.0.1.23 > 127.0.0.1.51514: Flags [P.], ... length 7
E..;..@.@.............login:
10:30:03.410 IP 127.0.0.1.51514 > 127.0.0.1.23: Flags [P.], ... length 1
E..5..@.@.............t
10:30:03.411 IP 127.0.0.1.23 > 127.0.0.1.51514: Flags [P.], ... length 1
E..5..@.@.............t
...
10:30:05.020 IP 127.0.0.1.23 > 127.0.0.1.51514: Flags [P.], ... length 10
E..>..@.@.............Password:
10:30:06.115 IP 127.0.0.1.51514 > 127.0.0.1.23: Flags [P.], ... length 1
E..5..@.@.............P ← 비밀번호 첫 글자 (서버 에코 없음)
인증 로그 (형식 예시(값은 환경마다 다름)) — PAM 서비스 이름(login, remote 등)은 배포판 설정에 따라 다릅니다.
login[5123]: pam_unix(remote:session): session opened for user testuser(uid=1001) by (uid=0)
login[5123]: LOGIN ON pts/1 BY testuser FROM localhost
점검 체크리스트
| 관찰 | 정상일 수 있는 경우 | 의심해야 하는 경우 |
|---|---|---|
| 내부 → 네트워크 장비 23번 | 레거시 장비 관리(개선 필요 대상) | 관리자 대역이 아닌 일반 PC에서 접속 |
| 외부 → 내부 23번 | 거의 없음 | 인터넷에서 직접 도달 = 노출 자체가 문제 |
| 짧은 연결 다수, 여러 목적지 | 없음 | 23/2323 포트 대량 접속 시도 = 스캔·감염 확산 징후 (05 스캔 시리즈에서 다룸) |
| 로그인 실패 반복 | 관리자 비밀번호 오입력 | 기본 계정명(admin, root 등) 순차 시도 |
| 내부 서버가 외부 23번으로 접속 | 거의 없음 | 감염된 내부 단말이 외부를 스캔하는 중일 수 있음 |
| 서버에 23번 LISTEN | 정책상 허용된 예외(문서화 필요) | 관리자도 모르는 Telnet 서비스 |
# 서버에 Telnet이 떠 있는지 (리슨 포트 점검)
sudo ss -tlnp | grep ':23 '
# 현재 23번 포트로 맺어진 연결
sudo ss -tnp state established '( sport = :23 or dport = :23 )'
# 캡처: 23번 포트 SYN만 (접속 시도 규모 파악)
sudo tcpdump -i ens33 -nn 'tcp port 23 and tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
# 인증 실패 확인
sudo grep -i "fail" /var/log/secure # Rocky
sudo grep -i "fail" /var/log/auth.log # Ubuntu
리슨 포트 점검 방법은 37. Listening Port 보안 점검에서 자세히 다뤘습니다.
흔적이 남는 곳
| 위치 | 남는 정보 |
|---|---|
| 방화벽 | 23번 포트 허용/차단 세션 로그, 출발지 국가·IP |
| IDS/IPS | Telnet 로그인 시도, 알려진 기본 계정 사용 시그니처 (06 방화벽·IDS 시리즈에서 다룸) |
| 서버 인증 로그 | /var/log/secure, /var/log/auth.log의 login/PAM 기록 |
| 네트워크 장비 로그 | vty 접속 로그(장비가 syslog를 중앙으로 보낼 때) |
| 패킷 캡처 | 계정·명령·출력 전체 (평문이므로 사후 재구성 가능) |
관제 시 핵심 포인트
한계와 오탐 주의
telnet host 80). 23번이 아닌 포트로 telnet 명령을 쓴 흔적은 Telnet 프로토콜 사용과 다릅니다.