오늘 밤엔 서버 하나라도 굴리고 계신 분이라면 꼭 한 번 보셔야 할 소식을 가져왔어요. 리눅스 서버에 원격 접속할 때 거의 모두가 쓰는 OpenSSH에서, 비밀번호를 아예 모르는데도 256자보다 긴 아무 문자열만 넣으면 로그인되는 구멍이 발견됐습니다. 별명은 SSHole(에스에스홀), 번호는 CVE-2026-123158(CVSS 8.1)이에요.
다행히 모든 리눅스가 다 위험한 건 아니에요. Alpine Linux처럼 musl이라는 C 라이브러리를 쓰고, PAM(로그인 확인 모듈)을 끈 채로 sshd를 돌리는 경우에만 해당합니다. 우분투·데비안·페도라 같은 glibc 계열, 그리고 공유기에 흔한 Dropbear는 해당이 없어요. 문제는 Alpine이 도커 이미지와 클라우드 소형 서버에서 정말 많이 쓰인다는 점, 그리고 공개 후 9시간도 안 돼 실제 공격 시도와 허니팟 root 탈취가 관측됐다는 점입니다.

한눈에 보는 핵심
| 항목 | 내용 |
|---|---|
| 이름 / 번호 | SSHole (sshole) / CVE-2026-123158, CVSS 8.1 |
| 무엇이 문제? | 비밀번호가 없는 계정(비밀번호 칸이 *)에 257~1024바이트짜리 아무 비밀번호를 넣으면 로그인 통과 |
| 누가 위험? | musl 기반 리눅스 + PAM 미사용 sshd + 비밀번호 로그인 허용 (대표: Alpine 기본 openssh-server) |
| 안전한 쪽 | glibc 계열(우분투·데비안·페도라 등), PAM 사용(UsePAM yes), Dropbear, OpenBSD |
| 고친 버전 | OpenSSH 10.6p2(11월 5일 공개), Alpine 각 브랜치 패치 패키지 |
| 실제 공격 | Previdian: 공개 9시간 안에 첫 시도, Alpine 허니팟에서 root 접근 확인(관측 8건, 단일 IP) |
| CISA KEV | 글 작성 시점 보도 기준 미등재 |
| 가장 중요한 한 줄 | 업데이트 후 sshd를 반드시 재시작해야 구멍이 닫힘 |
어떻게 이런 일이? 별표(*) 하나가 두 가지 뜻이었어요
쉽게 비유해 볼게요. 아파트 경비실에 입주민 명부가 있고, 열쇠만 쓰는 세대는 비밀번호 칸에 “*”(비밀번호 없음)이라고 적어 둡니다. 그런데 경비원이 비밀번호를 확인하는 계산기는 너무 긴 입력을 받으면 고장 표시로 똑같이 “*”를 띄워요. 경비원은 계산기 화면의 “*”와 명부의 “*”를 비교해서 “어, 같네? 들어가세요” 해 버린 거죠.
| 단계 | 실제로 일어나는 일 | 비유 |
|---|---|---|
| 1 | 키 전용 계정은 /etc/shadow의 비밀번호 칸이 * |
명부에 “비밀번호 없음(*)” 기재 |
| 2 | 2016년 사용자 이름 추측 공격(CVE-2016-6210)을 막으려고, sshd는 해시가 없는 계정도 다른 계정의 소금(salt)을 빌려 일부러 계산함 | 없는 세대여도 똑같이 계산하는 척(시간 차로 정체가 드러나지 않게) |
| 3 | musl의 crypt()는 2012년부터 256바이트를 넘는 SHA-256/512 비밀번호를 거부하고 실패 표시로 *를 돌려줌 |
계산기가 너무 긴 입력에 “*” 표시 |
| 4 | sshd가 결과 *와 저장된 *를 비교하니 일치 |
“*”=”*” → 통과 |
그래서 조건 중에 “$5$나 $6$ 해시를 가진 다른 계정이 하나라도 있을 것”이 들어가요. 빌려 올 소금이 있어야 이 계산 과정이 돌아가거든요. Alpine은 “비밀번호가 있는 사용자 한 명만 있어도 비밀번호 없는 모든 계정이 열린다”고 설명했습니다.
OpenSSH 개발자 Damien Miller는 원인이 sshd 바깥(라이브러리)에 있다고 보면서도 sshd 쪽에서 막을 수 있어 10.6p2를 냈고, “AI 에이전트가 몇 시간 만에 같은 버그를 다시 찾아내는 경우가 늘어 긴 비공개 기간이 오히려 위험하다”며 하루 만에 서둘러 공개했다고 밝혔어요. 발견자인 Wakefield의 Özgün Kültekin도 여러 빌드에 1만 7600여 가지 이상한 로그인 시도를 던져 보는 과정에서 에이전트를 많이 썼다고 했고요.

우리 서버도 해당될까? 3가지 조건 체크
| 조건 | 해당되는 경우 | 확인 방법 |
|---|---|---|
| ① musl + PAM 없음 | Alpine 기본 openssh-server, 또는 openssh-server-pam인데 UsePAM no |
cat /etc/os-release, apk info | grep openssh |
| ② 비밀번호 로그인 허용 | PasswordAuthentication yes(OpenSSH 기본값) |
sshd -T | grep -i passwordauthentication |
| ③ ‘*’ 계정 + 해시 계정 | 키 전용 사용자, root:*, 디렉터리 서비스 사용자, 클라우드 이미지의 alpine 사용자 + 비밀번호 있는 계정 1개 이상 |
cut -d: -f1,2 /etc/shadow(root 권한)에서 *와 $5$/$6$ 확인 |
세 가지가 모두 겹쳐야 위험해요. 반대로 하나라도 빠지면 이번 구멍으로는 못 들어옵니다.
“root는 기본 설정이 막아 준다던데요?”
맞아요, OpenSSH 기본값 PermitRootLogin prohibit-password 덕분에 root로 바로 비밀번호 로그인은 안 됩니다. 그런데 다른 계정으로 들어간 뒤 root가 될 수 있다면 이야기가 달라져요. 발견자는 Alpine 공식 AWS·Azure 이미지(tiny-cloud)의 기본 alpine 사용자가 비밀번호 없이 doas(sudo 비슷한 도구)를 쓸 수 있어 “결국 root”라고 설명했어요.
| 계정 유형 | 위험도 | 이유 |
|---|---|---|
root:* (Alpine 3.16 무렵부터 기본) |
보통 | PermitRootLogin yes로 바꿨다면 직접 위험 |
클라우드 이미지 alpine 사용자 |
높음 | 비밀번호 없는 doas로 root 상승 가능(보도 기준 tiny-cloud 3.18.11~3.24.3) |
| 배포용 키 전용 사용자 | 높음 | 배포 키·앱 비밀값·연결된 인프라 접근으로 번질 수 있음 |
| LDAP 등 디렉터리 사용자(musl-nscd) | 높음 | shadow 항목이 없어 비밀번호 없음으로 취급 |
잠긴 계정(user:!:) |
해당 없음 | 비밀번호·키 모두 로그인 불가 |
도커 쓰는 분들은 걱정해야 하나요?
많이들 헷갈리실 부분이라 따로 정리했어요. Alpine은 도커 이미지 기본 바탕으로 정말 많이 쓰이지만, 컨테이너 안에서 sshd를 돌리지 않으면 해당이 없어요. 대부분의 앱 컨테이너는 sshd가 없으니 괜찮습니다.
| 상황 | 판단 |
|---|---|
| Alpine 기반 웹앱 컨테이너(sshd 없음) | 해당 없음 |
| Alpine 컨테이너 안에 sshd를 넣어 원격 접속(개발용 SSH 컨테이너 등) | 점검 필요 |
| Alpine VM / 클라우드 소형 인스턴스에 SSH로 접속 | 점검 필요(가장 흔한 위험 상황) |
| 집 공유기(OpenWrt 기본 Dropbear) | 기본 설정이면 해당 없음, OpenSSH로 바꿨다면 점검 |
| Buildroot·Yocto로 만든 임베디드 기기 | 제조사 공지 확인 |

타임라인: 패치보다 공격이 더 빨랐어요
| 시점 | 일어난 일 |
|---|---|
| 2012년 | musl crypt()가 256바이트 초과 입력을 거부하고 *를 돌려주도록 됨 |
| 2016년 | OpenSSH가 CVE-2016-6210 대응으로 ‘소금 빌려 계산하기’ 도입 |
| 11월 4일 무렵 | OpenSSH 측이 제보를 받음(Miller: “어제 알았다”) |
| 11월 5일 | OpenSSH 10.6p2 공개, Alpine 긴급 공지와 3.16~edge 패치 패키지 배포 |
| 공개 후 9시간 이내 | Previdian 센서에 첫 공격 시도, 허니팟에서 root 접근 확인 |
| 11월 6일 | THN·Help Net Security 등 실제 악용 보도, GitHub에 검증 안 된 PoC 등장 |
Previdian에 따르면 공격 IP는 긴 비밀번호로 들어온 뒤 id, uname, hostname 같은 정찰 명령을 실행했어요. 아직 관측된 규모는 작지만, 공격이 워낙 간단해서 늘어나는 건 시간문제로 보는 게 안전합니다. 참고로 GitHub에 올라온 PoC는 검증되지 않았고 악성코드가 섞여 있을 수 있다는 경고도 있으니 함부로 내려받아 실행하지 마세요.
지금 바로 하는 법: 업데이트하고 꼭 재시작
Alpine 공지에서 가장 강조한 말은 이거예요. “패키지를 올려도 이미 돌고 있는 sshd는 바뀌지 않는다.” 새 열쇠를 받아 놓고 문에 안 다는 것과 같아요.
# 1) 업데이트
apk upgrade -U -a
# 2) sshd 재시작 (OpenRC 기준)
rc-service sshd restart
# 3) 지금 버전 확인
apk info -v openssh
| Alpine 브랜치 | 고친 openssh 버전 |
|---|---|
| edge | 10.6_p2-r0 |
| 3.24 | 10.3_p1-r2 |
| 3.23 | 10.2_p1-r1 |
| 3.22 | 10.0_p1-r11 |
| 3.21 | 9.9_p2-r1 |
| 3.20 | 9.7_p1-r6 |
| 3.19 | 9.6_p1-r3 |
| 3.18 | 9.3_p2-r4 |
| 3.17 | 9.1_p1-r7 |
| 3.16 | 9.0_p1-r6 |
Alpine 3.21.10, 3.22.8, 3.23.8, 3.24.4 포인트 릴리스에도 고친 패키지가 들어 있어요. 3.16~3.20은 정기 업데이트가 끝난 버전인데도 이번엔 패치가 나왔으니, 이번 기회에 지원 중인 버전으로 옮기는 계획도 같이 세워 두시면 좋아요.
당장 업데이트가 어렵다면: 비밀번호 로그인 끄기
Alpine과 OpenSSH 모두 “업데이트 또는 비밀번호 로그인 끄기, 둘 중 하나만 해도 구멍이 닫힌다”고 했어요. SSH 키로 접속하고 계신다면 이 설정은 사실 원래 꺼 두는 게 좋은 습관이기도 합니다.
# /etc/ssh/sshd_config
PasswordAuthentication no
KbdInteractiveAuthentication no
# 설정 문법 확인 후 재시작
sshd -t && rc-service sshd restart
⚠️ 원격으로 작업 중이라면 지금 접속한 창은 닫지 말고, 새 창에서 키로 다시 로그인되는지 먼저 확인한 뒤 끊으세요. 설정 실수로 스스로 문을 잠그는 일이 생각보다 흔해요.
| 방법 | 효과 | 주의 |
|---|---|---|
| 패키지 업데이트 + sshd 재시작 | 근본 해결 | 재시작 빠뜨리면 효과 없음 |
PasswordAuthentication no |
즉시 차단 | 비밀번호로만 접속하던 사람은 키 먼저 등록 |
openssh-server-pam + UsePAM yes |
해당 경로 차단 | PAM 설정을 이해하고 적용 |
| SSH 포트를 VPN·방화벽 안쪽으로 | 공격 표면 축소 | 근본 패치를 대신하진 않음 |
이미 들어왔는지 확인하는 법
인터넷에 SSH 포트가 열려 있던 Alpine 서버라면, 패치 전에 누가 들어왔는지도 봐야 해요. Alpine이 안내한 핵심은 “비밀번호가 없어야 할 계정에 ‘Accepted password for’ 기록이 있는가”입니다.
# 비밀번호 없는 계정(alpine, root, 키 전용 사용자)에 비밀번호 로그인이 성공했는지
grep "Accepted password for" /var/log/messages
# 최근 생긴 사용자, authorized_keys 변경 확인
tail -n 20 /etc/passwd
find / -name authorized_keys -mtime -7 2>/dev/null
| 볼 곳 | 의심 신호 |
|---|---|
/var/log/messages |
alpine·root·배포 계정에 “Accepted password for” |
| doas 기록 | 평소 안 쓰던 시간대의 권한 상승 |
/etc/passwd |
모르는 새 계정 |
~/.ssh/authorized_keys |
내가 넣지 않은 공개키 |
| 프로세스·네트워크 | 정체 모를 채굴·프록시 프로그램, 높은 CPU |
침입 흔적이 있거나 “로그인이 없었다”고 확신할 수 없다면 서버를 침해된 것으로 보고 새로 구축하는 쪽이 안전해요. 그 서버에 있던 배포 키, API 키, 클라우드 자격 증명도 함께 바꿔 주세요. 패치는 문을 닫을 뿐, 이미 들어와 숨겨 둔 것을 치워 주지는 않거든요.

상황별 오늘 밤 할 일
| 나는… | 할 일 |
|---|---|
| 우분투·데비안 서버만 쓴다 | 이번 건은 해당 없음. 그래도 평소처럼 보안 업데이트는 유지 |
| Alpine VM/클라우드에 SSH로 접속한다 | 업데이트 + sshd 재시작 + 비밀번호 로그인 끄기 + 로그 확인 |
| Alpine 컨테이너를 쓰지만 sshd는 없다 | 해당 없음. 베이스 이미지는 다음 빌드 때 최신으로 |
| 개발용 SSH 컨테이너를 쓴다 | 이미지 재빌드, 비밀번호 로그인 끄기, 외부 노출 포트 점검 |
| 회사 인프라 담당이다 | musl 기반 자산 목록 뽑기, 인터넷 노출 SSH 우선 패치, 침해 판단 기준 공유 |
정리하면
SSHole은 “별표(*) 하나가 ‘비밀번호 없음’과 ‘계산 실패’라는 두 뜻을 동시에 가졌던” 아주 작은 어긋남에서 시작됐어요. 2012년과 2016년에 각각 따로 보면 합리적이었던 두 결정이 10년 넘게 지나 맞물리면서 문이 열린 거죠. 그리고 이제는 그런 틈을 사람과 AI가 몇 시간 만에 찾아내 공격까지 해 보는 시대라는 것도 보여 줬고요.
Alpine 서버가 하나라도 있다면 오늘 밤 딱 세 가지만 기억해 주세요. ① apk upgrade ② sshd 재시작 ③ PasswordAuthentication no. 그리고 SSH 포트가 인터넷에 열려 있었다면 로그 한 번 꼭 들여다보시고요. 오늘도 안전한 밤 되세요! 🔐