257자리 암호를 입력하자 잠긴 계정이 열렸다: Alpine과 OpenSSH의 인증 우회 취약점

257자리 암호를 입력하자 잠긴 계정이 열렸다: Alpine과 OpenSSH의 인증 우회 취약점

사이버보안OpenSSHLinux시스템 아키텍처

데이터 소스:Alpine Linux + OpenSSH + Lobsters

전통적인 보안 상식에 따르면 운영체제에서 잠긴 계정은 어떤 경우에도 로그인할 수 없어야 합니다. 어떤 암호를 입력하더라도 즉시 거부되는 것이 당연했습니다. 그러나 2026년 11월 5일, 경량 운영체제 Alpine Linux가 발표한 보안 권고는 이러한 상식을 완전히 뒤흔들었습니다.

공격자가 256바이트를 초과하는 긴 암호를 입력하기만 하면 잠겨 있던 관리자 계정의 문이 열립니다. 메모리를 손상시키지도 않았고 암호 해시를 무차별 대입하지도 않았습니다. 방어 시스템이 입력된 암호와 계정 잠금 표시를 대조한 뒤 스스로 로그인을 허용해 버린 것입니다.

이 비정상적인 우회 현상은 독립적으로 설계된 세 가지 방어 메커니즘이 뜻밖의 충돌을 일으킨 결과였습니다. “계산 실패”를 알리는 오류 신호와 “로그인 금지”를 의미하는 계정 상태가 동일한 기호를 공유하면서, 인터페이스 교차점에서 인증 로직 전체가 무너졌습니다.

세 가지 방어선이 맞물려 257자리 암호에 뚫린 인증 체계

유닉스 계열 시스템의 암호 파일인 /etc/shadow에서 별표 *는 수십 년간 계정 잠금 상태를 나타내는 표식으로 쓰였습니다. 암호 필드에 단일 별표가 적혀 있으면 시스템은 암호 로그인을 비활성화된 상태로 취급합니다. 정상적인 암호 계산으로 생성되는 해시는 수십 자에 이르므로 단일 별표와 일치하는 일은 불가능합니다.

동시에 OpenSSH 개발자들은 공격자가 사용자 계정의 유효 여부를 탐색하는 타이밍 공격을 막기 위한 방어 로직을 마련했습니다. 암호가 설정되지 않은 계정이라도 인증 프로세스는 즉시 오류를 반환하지 않습니다. 대신 시스템에 등록된 다른 정상 계정의 솔트를 가져와 비슷한 처리 시간이 소요되는 가짜 해시 계산을 수행합니다. 이렇게 하여 계정의 존재 여부에 따른 서버 응답 시간 차이를 균일하게 맞춥니다.

여기에 경량 C 표준 라이브러리인 musl도 자체적인 방어 한계를 두고 있었습니다. 악의적인 긴 문자열 입력으로 메모리가 고갈되는 문제를 막기 위해 musl의 crypt() 함수는 최대 입력 길이를 제한합니다. 입력 암호가 256바이트를 초과하면 함수는 계산을 즉시 중단하고 오류 신호로 정적 문자 포인터 *를 반환합니다.

세 가지 장치 모두 개별적으로 살펴보면 빈틈없는 보안 철학을 따르고 있습니다. 그러나 한 시스템 안에서 동시에 동작하는 순간 뜻밖의 공격 경로가 완성되었습니다.

공격자에게 필요한 전제 조건은 네 가지뿐입니다:

  1. 대상 서버의 SSH 포트에 접속할 수 있을 것
  2. 시스템 내에 암호 필드가 *로 지정된 계정이 존재할 것 (Alpine 3.16 이후 기본 root 등)
  3. 시스템에 $5$ 또는 $6$ 형식의 정상 해시를 가진 계정이 하나 이상 존재할 것
  4. 257바이트에서 1024바이트 사이의 비정상적으로 긴 암호를 전달할 것

이 네 가지 조건이 충족되면 OpenSSH는 긴 암호를 받아 빌려온 솔트로 가짜 해시 계산을 시작합니다. 기저 라이브러리인 musl은 길이 초과를 감지하고 오류 표식 *를 반환합니다. 이어서 OpenSSH가 반환값과 shadow 파일의 암호 필드를 비교하는데, 양쪽 모두 단일 별표이므로 일치한다고 판정하고 잠긴 계정의 로그인을 승인합니다.

Alpine 공식 보안 권고 원문 화면 캡처 그림: PAM 미사용 환경에서의 인증 우회를 공개한 Alpine Linux 공식 보안 권고. 출처: alpinelinux.org

별표의 두 얼굴: 계정 잠금과 계산 실패의 의미 충돌

이 취약점의 핵심 원인은 소프트웨어 계층에 따라 동일한 기호에 정반대의 의미를 부여했다는 데 있습니다. 상위 소프트웨어는 별표를 단단한 자물쇠로 여겼고 하위 라이브러리는 오류를 알리는 백기로 반환했습니다.

전통적인 유닉스 구현에서는 암호화 함수에 유효하지 않은 입력이 들어오면 널 포인터를 반환하는 방식이 흔했습니다. 호출 측에서 널 검사만 수행하면 계산 성공과 실패를 명확히 구분할 수 있었습니다. 그러나 널 포인터 반환은 오래된 코드에서 역참조 오류를 일으키는 원인이 되기도 했습니다. 이에 musl은 표준 규격을 준수하여 오류 발생 시 정적 문자 *를 가리키는 포인터를 반환하도록 설계되었습니다.

이 설계는 호출 측이 별표를 비정상 해시로 인식할 것이라는 전제를 깔고 있었습니다. 일반적인 상황에서는 문제가 되지 않았습니다. 암호 계산 결과로 별표가 나오더라도 대상 계정에 실제 해시가 등록되어 있다면 서로 일치할 리 없기 때문입니다.

하지만 OpenSSH의 가짜 솔트 계산 로직이 결정적인 틈을 만들었습니다. PAM 모듈이 없는 경량 환경에서는 sshd 프로세스가 직접 암호를 검증합니다. 공격자가 응답 시간을 바탕으로 계정 상태를 추측하지 못하도록 잠긴 계정에 대해서도 암호화 연산을 강제로 실행했던 것입니다.

이로 인해 인증 흐름은 예상치 못한 모순에 빠졌습니다. OpenSSH는 입력된 암호를 정상적으로 검증하고 있다고 판단했지만 실제 하위 라이브러리는 오류를 내며 연산을 멈춘 상태였습니다. 계약 해석의 미세한 차이가 잠금용 표식을 시스템 통과 증표로 바꿔 놓았습니다.

클라우드 템플릿의 잠재적 위험: 계정 하나 추가에 열린 뒷문

취약점이 알려진 후 보안 커뮤니티는 실제 환경에서의 파급력을 점검했습니다. 모든 Alpine 환경이 위험에 노출된 것은 아니며 실제 위험은 시스템 구성에 따라 달라집니다.

단독 마이크로서비스를 실행하는 경량 컨테이너는 보통 SSH 포트 자체를 외부에 열어두지 않으므로 공격 경로가 차단됩니다. 암호 인증을 비활성화(PasswordAuthentication no)한 서버 역시 영향을 받지 않습니다. PAM 모듈을 사용하는 환경의 경우 인증 검증 과정이 별도 모듈로 분리되어 있으므로 두 표식이 직접 비교되는 상황이 일어나지 않습니다.

진짜 문제는 클라우드 이미지에 숨어 있었습니다. Alpine 공식 tiny-cloud 이미지(버전 3.18.11부터 3.24.3)에는 기본 계정인 alpine이 포함되어 있었습니다. 이 계정의 shadow 암호 필드는 *였으며 암호 없이 최고 권한을 얻을 수 있는 sudo 권한이 설정되어 있었습니다.

방금 생성된 초기 시스템에서는 이 계정이 안전했습니다. 시스템에 솔트가 포함된 일반 계정이 없어 OpenSSH가 가짜 계산 로직을 작동시키지 못했기 때문입니다. 많은 관리자가 기본 템플릿 상태에서는 위험이 없다고 오판하기 쉬운 구조였습니다.

그러나 관리자가 첫 번째 일반 사용자를 추가하고 shadow 파일에 암호 해시가 기록되는 순간 위험이 수면 위로 드러납니다. 잠들어 있던 alpine 계정이 원격 권한 상승 경로로 즉시 전환되는 것입니다. 공격자는 257바이트 암호로 로그인하여 시스템 최고 권한을 손쉽게 획득할 수 있었습니다.

패키지 업그레이드 후 데몬 재시작 필수, 암호 인증 차단으로 긴급 대응

이번 취약점 발표 당시 별도의 CVE 번호는 부여되지 않았습니다. 보안 연구원 Özgün Kültekin이 이상 현상을 처음 발견했으며 OpenSSH와 Alpine 팀이 신속하게 패치를 마련했습니다. 수정 내역은 OpenSSH 10.6p2 릴리스 노트에 기록되었으며 Alpine 3.16부터 3.24 및 edge 개발 브랜치까지 반영되었습니다.

영향을 받는 시스템은 즉시 저장소를 동기화하고 패키지를 업데이트해야 합니다. Alpine 터미널에서 apk upgrade -U -a를 실행하면 OpenSSH가 수정 버전으로 교체됩니다. 패치된 코드에서는 하위 라이브러리가 반환하는 비정상적인 문자열을 엄격히 차단하여 표식 혼동 가능성을 제거했습니다.

OpenSSH 공식 릴리스 노트 화면 캡처 그림: 비정상적으로 긴 암호와 가짜 솔트 매칭 로직 수정을 다룬 OpenSSH 10.6p2 릴리스 노트. 출처: openssh.com

주의할 점은 디스크의 바이너리를 교체하는 것만으로는 위험이 완전히 해소되지 않는다는 사실입니다. 메모리에서 계속 실행 중인 이전 프로세스는 기존 취약한 로직을 유지하므로 공격에 노출될 수 있습니다. 관리자는 패키지 업데이트를 마친 후 반드시 rc-service sshd restart를 실행하여 서비스 프로세스를 재시작해야 합니다.

즉시 재시작 점검을 진행하기 어려운 운영 서버에서는 설정을 변경하여 임시 조치를 취할 수 있습니다. /etc/ssh/sshd_config.d/00-no-passwords.conf에 PasswordAuthentication no를 작성하고 설정을 다시 불러온 뒤 sshd -T를 실행해 암호 인증이 비활성화되었는지 확인합니다. 과거 접속 기록을 점검할 때 /var/log/messages 파일에서 암호 없는 계정의 로그인 성공 기록이 확인된다면 이미 침해를 당했을 가능성을 의심해야 합니다.

소프트웨어 보안 취약점은 서로 다른 시스템 경계가 만나는 지점에서 자주 발생합니다. 각 구성 요소가 독립적으로는 최선의 보안 관행을 따르더라도 인터페이스 규약 해석에서 작은 차이가 생기면 견고하게 세운 방어선도 내부에서 무너질 수 있습니다. 규약의 의미를 명확하고 단일하게 유지하는 것은 복잡한 방어 규칙을 겹겹이 쌓는 것보다 훨씬 본질적인 엔지니어링 원칙입니다.

참고 링크:

  • Alpine Linux 보안 권고
  • OpenSSH 10.6p2 릴리스 노트
  • Lobsters 커뮤니티 기술 토론