1994년에 만들어진 코드가 2026년에 이르러서야 패치됐다. GNU inetutils의 telnetd에서 발견된 CVE-2026-32746은 인증 이전 단계에서 발동하는 버퍼 오버플로 취약점으로, DREAM Security Research Team이 찾아내고 보안업체 watchTowr가 분석을 공개했다. 취약점 자체는 30년 넘게 아무도 눈치채지 못한 채 여러 시스템으로 복사·이식되며 살아남았다. 보안 관점에서 흥미로운 점은 '발견됐다'는 사실보다, 막상 실전에서 악용하려 들면 생각보다 훨씬 까다롭다는 데 있다.
Telnet은 왜 아직도 살아 있나
Telnet은 TCP/IP 위에서 원격 서버에 명령줄 인터페이스를 제공하는 프로토콜이다. 사실상 원격 코드 실행을 서비스로 제공하는 셈인데, 통신이 평문으로 이뤄져 아이디와 비밀번호가 네트워크에 그대로 노출된다는 치명적 약점을 가진다. 오늘날 사실상의 대체재는 SSH이며 Telnet의 사용은 점점 줄고 있다. 그럼에도 여전히 운영 환경에 남아 있는 이유는 다양하다. 벤더가 그것만 지원하거나, 8비트 마이크로컨트롤러 위에서 돌아가는 산업 장비처럼 SSH 클라이언트를 얹을 여력조차 없는 경우가 대표적이다. 모든 주요 리눅스 배포판 저장소에 Telnet이 남아 있다는 사실 자체가 이 프로토콜의 끈질긴 생존력을 방증한다.
문제의 코드는 Telnet의 LINEMODE 기능, 그중에서도 SLC(Set Linemode Characters) 협상 처리기에 있다. LINEMODE는 RFC 1184에 정의된 기능으로, 한 글자마다 패킷을 주고받는 대신 명령줄 단위로 트래픽을 묶어 지연이 큰 네트워크나 패킷당 과금 네트워크에서 비용을 줄이려던 설계다. 취약점이 '패킷당 과금'이 현실이던 시절의 유물이라는 점이 그 나이를 실감케 한다.
세 바이트 삼중항이 만든 오버플로
Telnet 클라이언트와 서버는 연결 직후 0xFF, 즉 IAC(Interpret As Command) 바이트를 이용한 대역 내 신호로 기능을 협상한다. 서버가 DO로 SLC 기능을 요청하면 클라이언트는 WILL로 응답하고, 이후 서버는 '대체할 특수문자, 지원 수준, 실제 문자값'으로 이뤄진 3바이트 삼중항 목록을 주고받는다. 문제는 서버가 클라이언트가 보낸 이 값들을 고정 크기의 전역 배열에 경계 검사 없이 그대로 저장한다는 데 있다. 그 결과 약 400바이트 분량의 인접 변수를 공격자가 통제하는 데이터로 덮어쓸 수 있다.
흥미롭게도 이 결함은 판박이 전례가 있다. 2005년에 공개된 CVE-2005-0469는 같은 버그가 서버가 아닌 클라이언트 측(slc_add_reply 함수)에 있던 사례였고, 당시 적용된 경계 검사 패치는 이번 수정과 사실상 동일하다. 20년이 지나서야 누군가 서버 쪽에도 같은 문제가 있는지 확인한 셈이다. 참고로 최근에는 환경 변수 공유 기능을 악용한 또 다른 Telnet RCE(CVE-2026-24061)도 보고된 바 있어, 기능이 많은 만큼 공격 표면도 넓다는 원칙을 다시 확인시킨다.
덮어쓸 수 있다고 곧 뚫리는 건 아니다
실제 악용 난도를 높이는 것은 데이터 형식에 걸린 여러 제약이다. process_slc 함수는 func 바이트가 NSLC 상수(0x1e)를 넘으면 나머지 바이트를 버리고, func가 0이면 삼중항 자체를 저장하지 않는다. 이어지는 change_slc는 flag 바이트의 하위 두 비트가 0이면 최상위 비트(SLC_ACK, 0x80)를 세우는 식으로 값을 변형한다. 게다가 0xFF는 자동으로 0xFF 0xFF로 두 배가 되어, 연속된 0xFF를 홀수 개로 배치할 수 없다. 이 점은 특정 위치에 반복되는 0xFF 바이트가 흔한 64비트 x86 환경에서 특히 큰 장벽이 된다. 다만 이 제약을 역이용해 0xFF로 버퍼를 패딩하며 삼중항 정렬을 어긋나게 만들면, 포인터의 최하위 바이트만 부분적으로 덮어쓰는 식의 정밀 조작이 가능해지는 여지도 남는다.
공간 제약도 뚜렷하다. 전송 데이터는 0x200바이트짜리 단일 서브네고시에이션 패킷 안에 들어가야 하고, 오버플로 대상인 slcbuf는 0x6C바이트에 헤더 4바이트를 이미 담고 있어 실질적으로 덮어쓸 수 있는 범위는 0x190바이트 남짓이다. 결정적으로 그 범위에 어떤 전역 변수가 놓이는지는 컴파일러가 결정하므로 바이너리마다 달라진다. watchTowr는 실제로 살펴본 어떤 시스템에서도 곧바로 익스플로잇으로 이어지는 경로를 찾지 못했다고 밝혔다. 노출 정도는 시스템마다 제각각이었고, 64비트 x86에서는 위 제약들 탓에 공격이 유독 어려웠다.
실무자에게 이 사건이 주는 함의는 두 가지다. 첫째, '악용이 어렵다'는 것이 '안전하다'는 뜻은 아니다. 인증 이전 단계에서 발동하며 GNU inetutils 계열을 기반으로 한 수많은 포크와 모든 주요 배포판에 영향을 미치는 만큼, 영향 범위 자체는 넓고 정확히 가늠하기 어렵다. 산업 장비나 레거시 환경에 Telnet을 남겨둔 조직이라면 배포판 패치 적용과 함께 해당 서비스의 노출 여부부터 점검해야 한다. 둘째, 30년을 버틴 버그와 20년 전의 판박이 사례가 보여주듯, 한쪽에서 고친 결함은 반대쪽에도 같은 형태로 남아 있을 가능성이 높다. 코드가 복사·이식되며 퍼진 취약점은 원본 패치만으로 끝나지 않는다는 점을 다시 새길 만하다.