257자 암호로 로그인 성공: 별표 하나가 수백만 서버를 뚫은 이유

257자 암호로 로그인 성공: 별표 하나가 수백만 서버를 뚫은 이유

사이버보안LinuxOpenSSHAlpine취약점 분석

데이터 소스:sshhole.com 취약점 최초 공개 문서 + OpenSSH 10.6p2 릴리스 노트 · HN · Lobsters

2026년 11월 5일, 리눅스 생태계에서 암호학적 상식을 뒤흔드는 보안 취약점이 공개되었다. Alpine 리눅스 기반으로 구동되는 수백만 개의 컨테이너에서 비밀번호 로그인이 비활성화되어 있던 관리자 계정들이 뚫려버렸다. 공격자가 비밀번호 입력창에 257자의 무작위 문자열을 집어넣기만 하면 별다른 인증 없이 시스템 권한을 바로 탈취할 수 있었다.

비밀번호는 길수록 안전하며 지나치게 긴 입력값은 방어선에서 차단되는 것이 일반적인 상식이다. 그러나 현실은 정반대로 흘러갔다. 단단히 잠겨 있어야 할 출입문이 아무렇게나 두드려 넣은 긴 문자열 하나에 힘없이 열리고 말았다.

초장문 암호가 뚫어낸 방어선: 257바이트 입력으로 시스템 장악

취약점의 발동 조건은 매우 정밀하면서도 흔했다. 대상 서버가 musl C 라이브러리와 링크된 OpenSSH 서비스를 실행 중이어야 하고, PAM 모듈이 비활성화되어 있어야 한다. 공격자가 257바이트에서 1024바이트 길이의 암호를 전달하면 인증 로직은 즉시 통과 처리된다.

257바이트는 일반적인 암호 길이를 아득히 넘어선 수치다. 하지만 이 비정상적인 데이터가 검증 로직을 파고들자 시스템은 로그인이 불가능해야 할 계정을 ‘인증 통과’로 판정해버렸다. 견고하게 설계된 방어 체계가 바로 이 순간 무력화되었다.

피해를 입은 계정에는 명확한 공통점이 있었다. 섀도 파일인 /etc/shadow에서 계정의 암호 해시 필드에 단일 문자 *가 입력되어 있었다는 점이다. 유닉스 규약에서 별표는 해당 계정이 비밀번호 로그인을 절대 허용하지 않음을 뜻한다. 클라우드 벤더의 기본 이미지나 SSH 키 인증 전용 배포 계정들이 바로 이러한 설정을 채택하고 있다.

가장 손쉬운 표적은 클라우드에 있었다. Alpine 공식 AWS 및 Azure 이미지의 기본 사용자인 alpine 계정이 정확히 이 구조였으며, 심지어 암호 없이 doas를 실행할 수 있는 권한까지 갖고 있었다. 이 계정을 장악하면 즉시 루트 권한을 확보하는 것이나 다름없었다.

시스템 내에 정상적인 해시를 가진 또 다른 계정이 하나만 존재해도 공격은 확정적으로 성공한다. OpenSSH가 타이밍 부채널 공격을 방지하기 위해 해당 계정의 솔트(salt)를 빌려와 계산 시간을 맞추기 때문이다. 여러 겹의 방어 설정이 복합적으로 맞물리면서 역설적으로 침입자에게 권한을 활짝 열어준 셈이다.

46년의 세월을 건너 충돌한 세 가지 합리적 결정

시스템 발전의 역사를 살펴보면 이 백도어는 수십 년의 시차를 두고 내려진 세 가지 독립적인 설계 결정이 결합되어 만들어졌다. 각 결정은 도입 당시에는 매우 합리적이고 치밀했으나, 이들이 최종적으로 조합되었을 때는 재앙으로 변했다.

첫 번째 결정은 1980년에 내려졌다. Bill Joy가 3BSD 시스템의 암호 파일에서 서비스 계정 daemon의 암호 필드에 *를 입력했다. 당시의 암호화 알고리즘으로는 단일 별표 문자를 결과값으로 출력할 수 없었기에, 사용자가 어떤 암호를 입력하든 결코 일치할 수 없었다.

1980년 3BSD 시스템의 암호 파일 원본 그림: 1980년 3BSD의 passwd 파일 원본 캡처. daemon 계정의 암호 필드에 별표가 들어가 있다. 출처: sshhole.com

별표 하나로 계정을 잠그는 이 간결한 관례는 무려 46년 동안 이어지며 로그인 불가 계정을 식별하는 업계 표준으로 정착했다.

두 번째 결정은 1997년에 등장했다. 유닉스 국제 표준 규격은 암호화 함수 crypt()가 연산 오류를 일으킬 때 널 포인터(NULL)를 반환하도록 규정했다. 그러나 많은 C 언어 프로그램들이 널 포인터 검사를 누락했고, NULL 역참조로 인한 세그멘테이션 폴트로 네트워크 서비스가 비정상 종료되는 문제가 빈번했다.

2012년 8월, 경량 C 라이브러리인 musl 개발팀이 조치에 나섰다. 널 포인터 충돌 위험을 제거하기 위해 커밋 b3c4cc12를 통해 함수 오류 발생 시 단일 문자 *를 반환하도록 수정했다. ‘오류가 발생할 수 없다’고 단언했던 기존 주석도 함께 삭제되었다.

오류 시 별표 반환을 정의한 musl 헤더 파일 그림: 연산 오류 발생 시 단일 별표를 직접 반환한다고 명시한 musl 헤더 파일 주석. 출처: sshhole.com

musl 개발자들에게 별표는 ‘연산 실패’를 의미했다. 반면 시스템 관리자들에게 별표는 ‘로그인 금지’를 뜻했다. 서로 다른 두 진영이 동일한 단일 문자에 완전히 다른 의미를 부여하고 있었던 것이다.

서비스 거부 방지 길이 제한과 타이밍 방어 동작의 충돌

세 번째 결정이 앞선 두 별표를 결정적으로 이어 붙였다. 2012년 8월 29일, 이전 변경으로부터 불과 20일 뒤 musl 라이브러리는 SHA-512 암호화 알고리즘 지원을 추가했다.

SHA-512는 연산 자원을 집중적으로 소모하며 입력 길이에 비례해 연산 시간이 늘어난다. 과도하게 긴 암호로 인해 서버 자원이 고갈되는 DoS 공격을 막기 위해 musl은 암호 입력 상한선을 256바이트로 제한했다. 256바이트를 초과하는 입력이 들어오면 연산을 거부하고 에러의 의미로 단일 문자 *를 반환하도록 설계했다.

256바이트 제한을 적용한 musl 소스 코드 그림: 과도한 연산으로 인한 자원 고갈을 방지하기 위해 crypt_sha512.c에 256바이트 상한을 추가한 musl libc 코드. 출처: sshhole.com

이 256바이트 하드 리밋은 자원 고갈 공격을 성공적으로 막아내며 수년간 라이브러리 내부에서 안정적으로 동작했다.

이후 2016년 7월, OpenSSH는 타이밍 분석 취약점인 CVE-2016-6210을 수정했다. 존재하지 않는 사용자에 대한 응답 속도가 더 빠르다는 점을 이용해 유효한 사용자 이름을 유추하는 공격을 막기 위해, OpenSSH는 더미 연산 보호 로직을 도입했다.

해시가 등록되지 않은 계정에 로그인 요청이 들어오면, 시스템은 다른 유효 계정의 솔트를 빌려와 임의로 암호 연산을 한 차례 실행해 응답 시간을 동일하게 맞췄다. 암호 필드가 별표로 비활성화된 계정 역시 이 더미 연산 경로를 타게 되었다.

이로써 취약점 고리가 완벽하게 완성되었다. 공격자가 root 계정을 대상으로 257바이트 암호를 보낸다. OpenSSH는 솔트를 빌려와 crypt()를 호출한다. musl은 입력 길이가 초과된 것을 확인하고 연산을 중단한 뒤 별표 *를 반환한다. OpenSSH는 반환된 *와 섀도 파일의 값을 비교한다. 양쪽이 완벽하게 일치하므로 시스템은 인증을 승인하고 셸 권한을 넘겨주게 된다.

컨테이너 경량화가 부른 확산: 소수 설계가 수백만 클라우드로

과거 musl은 가정용 공유기 같은 임베디드 기기에서 주로 쓰였기에 공격 노출 표면이 제한적이었다. 그러나 컨테이너 가상화 물결이 일면서 이 경량 라이브러리는 전 세계 클라우드 인프라의 최전선으로 진출했다.

5MB에 불과한 Alpine 리눅스는 글로벌 컨테이너 환경의 표준 베이스 이미지로 빠르게 자리 잡았다. 2019년 공식 이미지의 root 계정 빈 암호 취약점을 수정할 당시 비밀번호 필드를 느낌표 !로 잠근 적이 있었다. 그러나 느낌표 설정은 비-PAM 환경에서 SSH 공개키 인증까지 차단하여 주요 클라우드 호스트에서 접속 장애를 유발했다.

이에 따라 2022년 출시된 Alpine 3.16에서는 root의 암호 필드를 다시 별표 *로 되돌렸다. 공개키 로그인은 정상화되었지만, 이 변경으로 인해 이후 출시된 모든 컨테이너와 이미지가 취약점 발동 조건에 무방비로 노출되었다.

기술 커뮤니티에서는 즉각 피해 규모에 대한 분석이 이어졌다. 개발자 jbyers는 단일 컨테이너가 모든 조건을 충족할 확률이 절대적으로 높지는 않더라도, 전 세계에 배포된 천문학적인 컨테이너 총수를 감안하면 영향을 받는 인스턴스는 충격적인 규모에 달할 것이라고 지적했다. 수백만 개의 실제 서비스 환경이 부지불식간에 이 암초 위에 올라서 있던 것이다.

표준 구현에 대한 논쟁도 불거졌다. 개발자 NewJazz는 musl의 구현이 POSIX 표준 규격에서 벗어났다고 지적했으나, 다른 개발자들은 POSIX 원문을 근거로 규격 자체에 에러 코드가 하나만 정의되어 있을 뿐 예외 처리에 대한 명확한 기준이 애초에 부재했다고 반박했다.

Lobsters 커뮤니티의 mort는 취약점 공개 문서의 서술 방식에 대해 비판하며, 보안 보고서는 극적인 연출보다 영향 범위와 구체적인 조치 방법을 명확하고 간결하게 설명해야 한다고 짚었다.

패키지 설치만으로는 부족: 데몬 프로세스를 즉시 재기동해야

2026년 11월 5일, OpenSSH 업스트림은 10.6p2 버전을 긴급 출시했다. 새 버전에는 단일 문자 반환값을 무조건 차단하는 필터링 규칙이 추가되어, 응용 계층이 시스템 라이브러리의 반환 규약을 무비판적으로 신뢰하지 않도록 보완했다.

그러나 패키지 매니저로 업데이트를 진행하는 것만으로는 충분하지 않다. 리눅스 프로세스의 상주 특성은 시스템 관리자들이 흔히 간과하는 사각지대다.

패키지 매니저가 디스크 상의 바이너리를 교체하더라도 메모리에 상주 중인 데몬 프로세스는 기존 코드를 그대로 실행한다. 서비스를 수동으로 재기동하지 않는 한 취약점은 그대로 노출된다. 인프라 운영 담당자는 즉시 sshd 서비스를 재시작하거나 실행 중인 컨테이너를 재생성해야 방어 효과를 얻을 수 있다.

이번 보안 사고 전체를 복기해보면 그 어떤 코드도 악의적으로 작성된 적이 없다. 1980년 권한 차단을 위해 도입한 별표, 1997년 비정상 종료를 피하기 위해 정한 에러 반환값, 2012년 서비스 거부를 막기 위한 길이 제한, 2016년 타이밍 분석을 방어하기 위한 가짜 연산까지. 모든 선택은 당시의 기술적 맥락에서 충분히 합리적이었다.

하지만 수십 년에 걸쳐 겹겹이 쌓인 방어 논리가 한자리에서 교차했다. 의미론적인 작은 틈새 하나가 비인가 접근의 은밀한 통로를 만들어냈다. 복잡한 현대 소프트웨어 환경에서는 상호 간의 가정을 검증하지 않은 채 중첩된 방어 체계가 단일 결함보다 훨씬 찾기 힘들고 위험하다.

참고 링크:

  • sshhole.com 취약점 최초 보고서 및 온라인 점검 도구
  • Alpine aports 커밋 fbe926af: root 비밀번호 필드를 느낌표에서 별표로 변경
  • OpenSSH 10.6p2 공식 릴리스 공지 및 패치 소스코드
  • Hacker News의 musl crypt 오류 반환값 관련 기술 토론
  • Lobsters 커뮤니티의 컨테이너 보안 경계 및 대응 방안 논의