본문으로 건너뛰기

"CVE" 태그로 연결된 6개 게시물개의 게시물이 있습니다.

모든 태그 보기

PostgreSQL 18.6 보안

· 약 5분

이번 분기 마이너 릴리스는 조용히 지나갈 수 없는 규모예요. 8월 13일 공지로 PostgreSQL 18.6, 17.11, 16.15, 15.19, 14.24가 한꺼번에 나왔는데, 보안 취약점 수정이 28건입니다. 통상 마이너 릴리스의 보안 수정이 손에 꼽는 수준인 걸 생각하면 이례적인 숫자입니다.

버전 번호부터 눈에 걸립니다. 18 계열의 직전 버전은 18.4인데 이번이 18.6입니다. 18.5는 릴리스 준비 중 회귀(regression)가 발견되어 배포 없이 결번 처리됐습니다. 18.4에서 바로 18.6으로 올라가면 되고, 중간에 놓친 버전은 없습니다.

CVSS 8점대만 12건

28건 전체 중 CVSS 8.0 이상이 12건입니다. 성격별로 묶으면 그림이 보입니다.

계열대표 CVECVSS내용
클라이언트 도구CVE-2026-64648.1psql COPY FROM STDIN, 데이터 줄을 psql 명령으로 처리
클라이언트 도구CVE-2026-184088.8psql \unrestrict, 조작된 덤프 원본에서 임의 코드 실행
클라이언트 도구CVE-2026-193858.8pg_dump heap buffer overflow
heap overflowCVE-2026-146648.8regexp 처리, 임의 코드 실행 가능
heap overflowCVE-2026-146698.8to_char()
heap overflowCVE-2026-146768.8pg_stat_statements
type confusionCVE-2026-146718.8refint plan cache
type confusionCVE-2026-162388.8pg_restore_attribute_stats()
type confusionCVE-2026-162398.8cursor CLOSE + DECLARE
type confusionCVE-2026-146808.8"internal" 인자 처리
기타CVE-2026-146628.8tsvector/tsquery integer wraparound
기타CVE-2026-157428.8fuzzystrmatch, 임의 주소 기록

이 중 DBA가 특히 무겁게 볼 것은 psql 계열입니다. 나머지는 대체로 "DB에 로그인한 공격자가 권한을 넘어서는" 유형이라 접속 통제가 1차 방어선이 되지만, psql 취약점은 방향이 반대입니다. 신뢰할 수 없는 덤프나 SQL 파일을 psql로 읽어들이는 쪽이 피해자가 됩니다.

CVE-2026-6464는 COPY FROM STDIN 처리 중 초기 실패가 발생하면 뒤따르는 데이터 줄을 psql 명령으로 해석하는 문제입니다. 누군가 건네준 덤프 파일을 복원하는 일상 작업이 곧 공격 경로입니다. CVE-2026-18408도 같은 결로, pg_dump 출력에 포함되는 \unrestrict 처리를 악용하면 덤프를 만든 쪽(superuser 권한으로 조작된 서버)이 복원하는 쪽 클라이언트에서 임의 코드를 실행할 수 있습니다. 외부에서 받은 덤프를 복원할 일이 있는 조직이라면 클라이언트 패키지 업데이트를 서버만큼 서둘러야 합니다.

낮은 점수 중에도 운영에 직접 닿는 항목이 있습니다. CVE-2026-14663은 pgcrypto에서 비활성화된 cipher로 암복호화를 요청하면 조용히 평문으로 처리하던 문제입니다. 점수는 6.5지만 "암호화됐다고 믿었는데 평문이었다"는 유형이라 감사 관점에서는 확인해 볼 가치가 있습니다. CVE-2026-14672는 SCRAM 인증에서 scram_iterations 응답 차이로 계정 존재 여부가 노출되는 문제입니다.

업데이트만으로 끝나지 않는 세 가지

이번 릴리스의 함정은 바이너리 교체 후에도 남는 숙제입니다. 릴리스 노트가 세 가지 후속 조치를 명시합니다.

첫째, parallel GIN index build를 쓴 적이 있다면 reltuples 오염을 확인합니다. PostgreSQL 14, 15, 16, 18에서 parallel worker가 초기화되지 않은 row count를 보고해 pg_class.reltuples가 Infinity나 NaN이 되는 버그가 있었습니다. 이 상태가 되면 autovacuum과 autoanalyze가 해당 테이블을 건너뛰고, 자연적으로는 복구되지 않습니다. GIN 인덱스를 가진 테이블을 이 쿼리로 확인합니다.

SELECT DISTINCT t.oid::regclass, t.reltuples
FROM pg_class t
JOIN pg_index i ON t.oid = i.indrelid
JOIN pg_class ic ON i.indexrelid = ic.oid
WHERE t.relhasindex AND ic.relam = 2742; -- 2742 = gin

reltuples가 Infinity, NaN, 혹은 말이 안 되는 값이면 해당 테이블에 ANALYZE를 실행합니다. vacuum이 안 도는 테이블은 wraparound 위험까지 이어지니, GIN을 쓰는 클러스터라면 이 확인을 빼먹지 않는 게 좋습니다.

둘째, btree_gist 인덱스의 REINDEX입니다. float4/float8 컬럼에서 NaN 처리, bit/bit varying 컬럼에서 정렬이 잘못되던 문제가 고쳐졌습니다. 해당 타입 컬럼에 btree_gist 인덱스가 있다면 REINDEX가 필요합니다.

셋째, 큰 ltree 값의 인덱스도 REINDEX 대상입니다. 약 14,653개 이상의 label을 가진 ltree 값에서 비교 연산이 틀려 B-tree 인덱스가 손상될 수 있었습니다. 그 정도 깊이의 ltree를 쓰는 곳은 드물겠지만, 해당된다면 REINDEX 대상입니다.

이 밖에 버그 수정 중에는 standby가 구버전 마이너의 WAL을 재생하다 멈추는 deadlock 수정(14~16), DEFAULT partition이 pruning에서 잘못 제외되던 문제, REINDEX CONCURRENTLY와 deferred unique constraint 조합 오류 같은 굵직한 것들이 포함됐습니다. 전체 목록은 릴리스 노트에 있습니다.

19 Beta 3와 14 EOL

같은 날 PostgreSQL 19 Beta 3도 나왔습니다. 눈에 띄는 변화는 GROUP BY ALL 문법이 revert 된 것입니다. Beta 1에 들어왔던 편의 문법인데 최종 릴리스 전에 빠졌습니다. 그 외 FOR PORTION OF(temporal 문법) 수정 다수, logical replication의 sequence 동기화 race condition 수정 등이 반영됐습니다. 19 신기능을 검증 중이라면 Beta 3 기준으로 다시 확인하는 게 안전합니다.

그리고 PostgreSQL 14의 지원 종료가 2026년 11월 12일로 예고됐습니다. 이번 14.24를 포함해 앞으로 한 번의 릴리스만 남았습니다. 아직 14로 운영 중인 클러스터는 마이그레이션 일정을 지금 세워야 합니다.

지금 해야 할 일

우선순위로 정리하면 이렇습니다.

  1. 서버 바이너리 업데이트 (마이너 업데이트라 dump/reload 불필요, 재시작만)
  2. 클라이언트 패키지(psql, pg_dump)도 같이 업데이트 - 이번 릴리스에서는 클라이언트가 서버만큼 급합니다
  3. GIN 인덱스 테이블의 reltuples 확인, 이상 시 ANALYZE
  4. btree_gist(float/bit 컬럼), 깊은 ltree 인덱스 REINDEX
  5. PostgreSQL 14 사용 중이면 업그레이드 계획 수립

28건이라는 숫자에 놀랐지만, 뒤집어 보면 fuzzing과 코드 감사가 코어에 촘촘히 들어가고 있다는 뜻이기도 합니다. 저는 클라이언트 도구가 공격면이 되는 흐름이 이번 릴리스의 진짜 교훈이라고 봐요. 서버는 잘 잠가 두는데 psql은 아무 파일이나 여는 습관, 이번 기회에 같이 고치면 좋겠습니다.

참고 자료

Oracle 월간 CSPU 전환

· 약 8분

Oracle이 20년 넘게 유지해 온 분기 Critical Patch Update(CPU) 체제 위에 매달 발행하는 Critical Security Patch Update(CSPU)를 얹었습니다. 그 첫 릴리스가 2026년 5월 28일에 나왔습니다.

분기 CPU는 그대로 1, 4, 7, 10월에 누적본으로 나옵니다. CSPU는 그 사이 달에 셋째 화요일마다 끼어드는 소규모/고우선 전용 패치입니다. 첫 CSPU는 CVE 35건을 담았고, 그중 하나는 CVSS 10.0이었습니다. Oracle이 분기 리듬을 깨고 빠른 차선을 새로 낸 이유를 Oracle Security Blog의 공지와 DBA 관점에서 정리합니다.

20년간 이어진 분기 CPU

Oracle의 분기 CPU는 DBA에게 익숙한 행사입니다. 매년 1, 4, 7, 10월 셋째 화요일에, Oracle의 전 제품군에 걸친 보안 수정이 한 묶음으로 떨어집니다. 한 번에 수백 건이 쏟아지는 게 특징입니다. 바로 직전인 2026년 4월 CPU만 해도 450건의 취약점을 한꺼번에 고쳤습니다.

이 모델의 핵심은 누적(cumulative)이라는 점입니다. 가장 최신 CPU 하나에 그 이전의 모든 수정이 포함되므로, DBA는 사실상 최신 CPU 한 개만 적용하면 됩니다. 관리가 단순하다는 장점은 분명합니다.

문제는 주기입니다. 1월에 발표된 취약점을 표준 절차로 막으려면 4월 CPU까지 최대 석 달을 기다려야 합니다. 그 석 달 동안 취약점은 공격자에게 열린 창으로 남습니다.

사이를 메우는 월간 CSPU

CSPU는 그 창을 좁히는 장치입니다. 분기 CPU를 대체하지 않고 사이를 메웁니다.

  • 발행 주기: 분기 CPU가 없는 달의 셋째 화요일 — 2, 3, 5, 6, 8, 9, 11, 12월
  • 성격: 전 제품군을 훑는 게 아니라, 즉시 대응이 필요하다고 Oracle이 판단한 소수 항목만 추린 묶음
  • 사전 예고: 각 CSPU 발행 직전 목요일(T-5)에 미리 공지

분기 CPU 4회와 CSPU 8회를 합치면, DBA는 1년에 12번의 예측 가능한 패치 이벤트를 갖게 됩니다(Oracle). 사실상 월 1회 리듬입니다.

분기 CPU vs 월간 CSPU

둘은 목적이 다릅니다. 나란히 놓으면 역할이 분명해집니다.

항목분기 CPU월간 CSPU
발행 시점1, 4, 7, 10월 셋째 화요일2, 3, 5, 6, 8, 9, 11, 12월 셋째 화요일
연간 횟수4회8회
범위전 제품군 전수고우선 항목만 선별
규모수백 건 (4월 450건)소규모 (5월 35건)
누적 여부누적 (최신본 하나면 됨)비누적 (개별 적용)
사전 예고발행 전 목요일발행 전 목요일

여기서 DBA가 놓치면 안 되는 한 가지가 있습니다. CSPU는 비누적이지만, 다음 분기 CPU가 그동안의 CSPU 수정을 모두 빨아들인다는 점입니다. 즉 CSPU를 적용하지 않고 넘어갔더라도 다음 분기 CPU를 적용하면 그 사이 CSPU 수정이 따라옵니다 — 다만 그때까지 노출 창이 길어질 뿐입니다.

5월 28일, 첫 CSPU의 내용

첫 CSPU는 새 CVE 35건을 담았습니다(Oracle Security Blog). 분기 CPU의 수백 건과 비교하면 의도적으로 작은 묶음입니다. 제품군별 분포는 다음과 같습니다.

제품군패치 수
Oracle E-Business Suite12
Oracle REST Data Services (ORDS)11
Oracle Communications Unified Assurance8
Oracle Database Server3
Oracle Hospitality OPERA 51

심각도가 가볍지 않습니다. 가장 눈에 띄는 건 ORDS의 CVE-2026-46840으로, CVSS 10.0 만점입니다. 기밀성, 무결성, 가용성이 모두 완전히 무너지는 등급입니다. E-Business Suite에도 CVSS 9.8~9.9급이 여러 건 있었고, Hospitality OPERA 5의 CVE-2026-34311은 9.8이었습니다.

집계 기준도 짚어 둘 필요가 있습니다. 새 CVE는 35건이지만, Communications 제품군에 들어간 서드파티 컴포넌트 CVE까지 합치면 이번 CSPU가 처리한 취약점은 모두 77건입니다(SecurityWeek). 그중 상당수는 인증 없이 원격으로 악용할 수 있었습니다. ORDS 7건, Communications 4건, E-Business Suite/Database Server 각 3건이 인증 없는 원격 공격에 그대로 열려 있었습니다.

묶음이 작다고 가벼운 것은 아닙니다. 오히려 분기까지 기다릴 수 없다고 Oracle이 판단한 항목만 골라 담았기 때문에, 평균 심각도는 분기 CPU보다 높다고 봐야 합니다.

20년 리듬을 깬 배경

이 블로그가 주목하는 지점이 여기입니다. Oracle이 20년 넘게 지켜 온 분기 리듬을 굳이 깬 배경에는, 취약점이 발견되는 속도 자체가 달라졌다는 인식이 깔려 있습니다.

Oracle Database 업그레이드를 총괄하는 Mike Dietrich(마이크 디트리히)는 이 변화를 AI 기반 위협에 대한 대응으로 명시했습니다. 그는 AI 모델이 이전과 비교가 안 되는 속도로 취약점을 찾아내고 있으며, Oracle 자체도 Anthropic과 OpenAI의 모델을 취약점 탐지에 활용하고 있다고 밝혔습니다. 발견 속도가 빨라지면 노출 창을 그만큼 줄여야 한다는 게 그의 논리입니다.

이건 지난달 Copy Fail 글에서 다룬 흐름과 정확히 같은 줄기입니다. AI 코드 감사기가 10년 묵은 커널 버그를 한 시간 만에 찾아낸 사건은, 방어자에게 한 가지를 분명히 일러 줬습니다. 오늘 멀쩡해 보이는 코드가 내일도 멀쩡하리라고 가정할 수 없다는 것입니다. 취약점이 더 빨리, 더 많이 드러나는 시대에는 패치 사이클도 그만큼 짧아져야 합니다.

분기에서 월간으로의 전환은 그 압력에 벤더가 내놓은 응답입니다. 발견과 패치 사이의 간격을 구조적으로 좁히려는 시도입니다.

[과거] 분기 모델
취약점 발견 ───────── 최대 3개월 ─────────▶ CPU 적용
(열린 창)

[현재] 월간 + 분기 모델
취약점 발견 ─── 최대 1개월 ───▶ CSPU 적용
(좁힌 창)

DBA가 지금 해야 할 일

패치 주기가 4배로 잦아졌다는 건, 운영 부담도 그만큼 늘었다는 뜻입니다. 12번을 모두 같은 속도로 따라가면 검증과 변경 관리가 무너집니다. 그래서 차선을 나눠 대응해야 합니다.

1) 패치 차선을 risk로 나눈다

모든 CSPU를 같은 속도로 처리할 필요는 없습니다. 인터넷에 노출된 ORDS/E-Business Suite처럼 공격 표면이 넓은 자산은 긴급 차선, 내부망 전용 DB는 표준 차선으로 분류합니다. 5월 CSPU의 ORDS CVSS 10.0 같은 항목은 자산 분류와 무관하게 즉시 차선입니다.

2) T-5 목요일 예고에 계획을 건다

Oracle이 발행 5일 전 목요일에 미리 알려 주므로, 화요일 발행을 기다리지 말고 목요일 예고로 영향 자산을 먼저 추립니다. 발행 당일에 시작하면 이미 늦습니다.

3) 회귀 테스트를 가볍고 결정론적으로 만든다

월 1회 리듬에서는 매번 광범위한 수동 검증을 붙일 여유가 없습니다. 핵심 쿼리, 배치, 연동 지점만 자동으로 돌려 확인하는 최소 세트를 미리 갖춰 둬야 패치 주기를 따라갈 수 있습니다.

4) 우회책에 기대지 않는다

Dietrich의 표현을 빌리면, virtual patching처럼 임시방편에 의존하는 건 보안이 아니라 심리적 위안에 가깝습니다. 결국 정식 패치를 빠르게 적용할 수 있는 체계를 만드는 게 유일한 답입니다.

이 변화가 남기는 신호

이번 변화의 의미는 단순히 "Oracle 패치가 잦아졌다"가 아닙니다. 보안 패치의 기본 단위가 분기에서 월로 바뀌는 흐름이 메이저 DB 벤더에서 시작됐다는 신호입니다.

Microsoft는 오래전부터 매달 둘째 화요일 Patch Tuesday를 돌려 왔습니다. 리눅스 배포판들은 CVE가 뜨면 며칠 안에 안정 커널을 내놓습니다. 분기라는 긴 호흡을 고수하던 Oracle마저 월간 차선을 열었다는 건, AI가 취약점 발견 속도를 끌어올린 환경에서 분기 주기로는 더 이상 버틸 수 없다는 업계의 공통 인식에 합류했다는 뜻입니다.

DBA에게 이 흐름의 결론은 명확합니다. 패치를 분기에 한 번 몰아서 하는 행사가 아니라 상시로 도는 프로세스로 다시 설계해야 합니다. CSPU는 그 전환을 강제하는 첫 신호예요.

정리

  • Oracle이 분기 CPU에 더해 매달 셋째 화요일 CSPU를 발행하기 시작했습니다. 첫 릴리스는 2026년 5월 28일입니다.
  • CSPU는 CPU를 대체하지 않고 사이 달을 메우는 소규모/고우선 패치입니다. 분기 CPU는 누적, CSPU는 비누적입니다.
  • 첫 CSPU는 CVE 35건, ORDS의 CVSS 10.0(CVE-2026-46840)을 포함한 고심각도 항목 중심이었습니다.
  • 전환의 배경은 AI가 끌어올린 취약점 발견 속도 — 발견과 패치 사이 창을 구조적으로 좁히려는 응답입니다.
  • DBA는 패치를 분기 행사가 아니라 상시 프로세스로 재설계하고, risk 기준으로 차선을 나눠야 합니다.

참고

DirtyDecrypt root LPE

· 약 7분

요점부터

Linux 커널 RxGK(rxrpc의 GSS 보안 계층) 디코드 경로에 COW(Copy-on-Write) 가드가 빠져 있어요. 비특권 사용자가 보낸 skb의 복호화 결과가 페이지 캐시의 공유 페이지에 그대로 쓰여요. 그 자리가 /etc/shadow, /etc/sudoers, SUID 바이너리의 페이지라면, 평범한 사용자가 root가 돼요.

별명은 DirtyDecrypt입니다. CVE는 CVE-2026-31635로 묶였지만, NVD에는 CVSS 3.1 7.5 High DoS로만 올라 있습니다. 발견자(Zellic + V12 보안팀)가 PoC로 보여준 실제 영향은 root LPE입니다. 2026-05-18에 PoC가 공개됐고, PoC repo는 GitHub에 그대로 있습니다. 즉시 악용 가능한 상태입니다.

CVE 한 줄, 함수 한 줄, 영향 두 가지

같은 결함이 두 모습으로 나뉘어 있습니다.

NVD / 메인테이너 시각Zellic/V12 PoC 시각
위치rxgk_verify_responserxgk_decrypt_skbrxgk_decrypt_skb
결함 종류길이 검증 반전 (CWE-130)COW 가드 누락
결과skb_to_sgvec()에서 BUG_ON() → 커널 패닉페이지 캐시 임의 위치 쓰기 → root
CVSS7.5 High (DoS)평가 없음 (LPE)
분류DoSLPE (Dirty 시리즈)

메인테이너는 V12의 DirtyDecrypt 보고에 "동일 서브시스템(net/rxrpc)의 같은 모듈군이라 중복 보고"라고 회신했고, DirtyDecrypt에는 별도 CVE가 붙지 않았습니다. 패치 자체는 메인라인에 조용히 머지됐습니다. 운영자 입장에서 중요한 것은 CVE 한 줄로 묶였더라도 영향이 DoS에 그치지는 않는다는 점입니다. NVD 등급만 보고 우선순위를 매기면 위험을 놓칩니다.

Dirty 시리즈 계보

페이지 캐시의 공유 페이지에 비특권 사용자가 결정론적 쓰기를 하는 결함은 이번이 처음이 아닙니다. 한 달도 안 된 Copy Fail (CVE-2026-31431)과도 같은 자리에 섭니다.

Dirty COW (2016)Dirty Pipe (2022)Copy Fail (2026-04)DirtyDecrypt (2026-05)
클래스race conditionflag 초기화 누락직선형 논리 버그COW 가드 누락
성공률확률적거의 결정론적결정론적결정론적
트리거 표면madvise + writeable 매핑splice + vmsplicesplice + AF_ALGrxrpc 소켓
영향 코드 경로mm/memory.cfs/splice.ccrypto/algif_aead.cnet/rxrpc/rxgk_*.c
익스플로이트 전제광범위광범위광범위RxGK 사용

DirtyDecrypt가 다른 셋과 가장 다른 지점은 트리거 표면이 좁다는 것입니다. RxRPC를 쓰는 환경은 사실상 Andrew File System(AFS) 클라이언트 또는 그 GSS 보안 계층 RxGK를 쓰는 환경에 한정됩니다.

영향 범위와 패치

제품영향 받는 버전해결 버전
Linux kernel6.19 이상 6.19.13 미만6.19.13 이상
Linux kernel6.16.1 이상 6.18.23 미만6.18.23 이상
Linux kernel7.0-rc1 ~ 7.0-rc7rc8 이후 메인라인

배포판별 노출 정도는 RxGK가 기본 활성화돼 있는지에 따라 갈립니다.

배포판RxGK 기본 상태노출
Fedora / Arch Linux / openSUSE Tumbleweed활성화즉시 영향
Debian Stable / RHEL / Ubuntu LTS비활성화일반적으로 안전

NVD 데이터는 CVE-2026-31635에, 메인라인 패치 커밋은 다음에 있습니다.

패치의 핵심은 공유 fragment를 가진 skb를 in-place로 복호화하기 전에 사본을 먼저 만든다는 한 줄입니다. 빠져 있던 COW 가드가 그 자리에 들어갑니다.

RxGK 복호화 경로의 메커니즘

RxRPC는 Andrew File System(AFS)의 네트워크 전송 프로토콜입니다. RxGK는 그 위에 GSS-API 기반 보안 계층을 얹습니다. 들어오는 데이터 패킷이 RESPONSE 인증자를 들고 오면, rxgk_verify_response()가 그 길이를 검증하고 rxgk_decrypt_skb()가 복호화를 수행합니다.

문제의 자리는 두 군데입니다.

  1. rxgk_verify_response. 길이 검증 비교 연산이 반전돼 있습니다. 비정상적으로 큰 인증자 길이가 통과해서 skb_to_sgvec()에 도달하고, 거기에서 BUG_ON()이 트리거됩니다. 이것이 NVD에 등록된 DoS 경로입니다.

  2. rxgk_decrypt_skb. 들어온 socket buffer(skb)가 페이지 캐시 페이지를 fragment로 참조하고 있을 때, 그 페이지에 in-place로 복호화 결과를 씁니다. 공유 페이지에 쓰기 전에 사본을 만드는 COW 가드가 빠져 있습니다. 이것이 V12/Zellic의 LPE 경로입니다.

후자가 왜 root로 이어지는지 살펴보겠습니다. 페이지 캐시 페이지는 커널에서 누가 그 페이지를 매핑하고 있는지 구별하지 않습니다. /etc/shadow, /etc/sudoers, /usr/bin/sudo 같은 SUID 바이너리도 동일한 페이지 캐시에 들어와 있습니다. 비특권 프로세스가 그 페이지를 read-only로 매핑해 두고, RxGK 디코드 경로를 통해 같은 페이지에 임의 바이트를 쓰면, 디스크는 그대로지만 메모리상의 권한 파일이 바뀝니다. 이후 execve() 또는 인증 검사 시점에 변조된 페이지가 그대로 사용됩니다.

이 흐름은 결정론적입니다. race condition도, ASLR 우회도 필요 없습니다. PoC는 표준 라이브러리만으로 동작합니다.

임시로 막으려면 모듈 차단

패치를 즉시 못 올리는 환경에서는 영향 모듈을 끄는 방법이 있습니다. RxGK가 의존하는 모듈 4종을 블랙리스트로 묶습니다.

# 사용 여부 확인
lsmod | grep -E '^(rxrpc|af_rxrpc|rxgk|kafs)\s'

# 사용 중이고 외부에서 필요 없다면 블랙리스트
cat <<'EOF' | sudo tee /etc/modprobe.d/rxgk-blacklist.conf
blacklist rxgk
blacklist rxrpc
blacklist af_rxrpc
blacklist kafs
EOF

# 적용 (즉시 unload)
sudo rmmod kafs rxgk rxrpc af_rxrpc 2>/dev/null || true

built-in으로 컴파일된 커널이면 unload가 불가능합니다. 두 가지 선택지가 남습니다.

  • 영향 받지 않는 커널(6.19.13 / 6.18.23 이상)으로 교체 후 재부팅
  • RxGK/RxRPC를 module 또는 disable 옵션으로 다시 컴파일

Fedora, Arch, openSUSE Tumbleweed는 보통 모듈이지만, 자체 빌드 커널은 확인이 필요합니다.

# RxGK 관련 옵션이 모듈인지 built-in인지 확인
zcat /proc/config.gz 2>/dev/null | grep -E 'CONFIG_(AFS|RXRPC|RXGK)'
# 또는
grep -E 'CONFIG_(AFS|RXRPC|RXGK)' /boot/config-$(uname -r)

=m은 모듈(블랙리스트로 처리 가능), =y는 built-in(커널 교체 필요)입니다.

지금 해야 할 일

  1. 커널 버전을 확인합니다. uname -r로 보고, 6.16.1 ~ 6.18.22 또는 6.19 ~ 6.19.12, 7.0-rc1 ~ rc7이면 영향권입니다.
  2. 배포판 노출을 평가합니다. Fedora, Arch, openSUSE Tumbleweed면 RxGK가 켜져 있을 가능성이 높고, RHEL, Ubuntu LTS, Debian Stable이면 보통 비활성화지만 자체 빌드는 확인이 필요합니다.
  3. 모듈 사용 여부를 확인합니다. lsmod | grep -E 'rxrpc|rxgk|kafs'로 적재 여부를 보면 되고, AFS 클라이언트를 안 쓰면 99% 적재돼 있지 않습니다.
  4. 패치를 적용합니다. 6.19.13 / 6.18.23 / 7.0-rc8 이후로 올리고, 배포판 패키지가 따라잡혔는지는 dnf updateinfo/apt changelog linux-image-* 등으로 확인합니다.
  5. 즉시 패치가 불가능하면 모듈을 블랙리스트에 넣습니다. 위의 conf 파일로 처리하되, AFS 클라이언트가 필요한 환경이면 패치를 우선시합니다.
  6. 변조 흔적을 점검합니다. 페이지 캐시 변조는 디스크에 남지 않지만, root 쉘 획득 후 흔적은 /var/log/auth.log, last, who, sudo 로그에 일부 남습니다. PoC 공개일(2026-05-18) 이후 비정상 sudo 호출 패턴을 검토합니다.

미배정 CVE라는 회색지대

DirtyDecrypt는 자체 CVE가 없습니다. CVE-2026-31635에 묶였고, 그 등록은 DoS 등급입니다. 운영자는 다음 두 가지를 함께 떠올려야 합니다.

  • NVD 등급은 영향 모델 중 하나만 반영합니다. 같은 결함이 다른 모델에서 더 무서울 수 있습니다.
  • CVE가 미배정이라고 결함이 작은 것도 아닙니다. 패치 머지 시점, PoC 공개 시점, 발견자 공개 시점이 운영자의 실제 시계입니다.

이번 자리의 시계는 4월 24일 NVD 공개 → 5월 18일 PoC 공개입니다. 한 달이 안 됐습니다. NVD 등급에 안주했다면 6.19.13 패치를 미뤘을 자리에, PoC가 같은 함수에서 root를 가져갔습니다.

정리

항목
별명DirtyDecrypt
CVECVE-2026-31635 (묶임, DoS 등급)
함수rxgk_decrypt_skb / rxgk_verify_response
결함COW 가드 누락 + 길이 검증 반전
영향DoS (NVD), root LPE (PoC)
CVSS3.1 7.5 High (NVD; DoS만)
영향 커널6.16.1 ~ 6.18.22/6.19 ~ 6.19.12/7.0-rc1 ~ rc7
패치6.19.13/6.18.23/7.0-rc8
PoC 공개2026-05-18
발견자Zellic/V12 보안팀
임시 완화rxgk, rxrpc, af_rxrpc, kafs 모듈 블랙리스트

AFS 클라이언트를 쓰지 않는다면 노출 면적은 좁아요. 쓰고 있다면, 또는 자체 빌드 커널에서 CONFIG_RXGK=y라면, 지금이 패치를 올릴 자리예요.

참고

NGINX Rift 힙 오버플로우

· 약 6분

먼저, 무엇이 터지나

rewrite 한 줄에 이름 없는 PCRE 캡처($1, $2)와 대체 문자열의 물음표(?)가 함께 들어가면, 비인증 공격자가 HTTP 요청 한 번으로 워커의 힙을 부숴요. CVSS v4.0 9.2 Critical, 2008년 NGINX 0.6.27부터 18년간 조용히 있었던 결함이에요.

F5와 depthfirst가 2026-05-13에 공동 공개했고, 별명은 NGINX Rift입니다. 사흘 뒤인 5월 16일부터 VulnCheck canary가 실 익스플로이트 시도를 잡기 시작했습니다. 운영자는 즉시 패치 또는 임시 완화 둘 중 하나는 골라야 합니다.

18년 묵은 결함이라는 의미

src/http/ngx_http_script.c에 있는 결함입니다. 2008년 NGINX 0.6.27에 처음 들어간 후 지금까지 모든 빌드가 영향권입니다. 단일 함수 한 줄짜리 회귀가 아니라, 스크립트 엔진의 설계 단계에서 두 단계 호출 사이의 상태 누출이 그대로 굳어 버린 형태입니다.

지금까지 익숙했던 NGINX 취약점은 보통 모듈 단위였고 영향 버전 범위도 1~2년 정도였습니다. Rift는 0.6.27 ~ 1.30.0, NGINX Plus는 R32 ~ R36까지 한 줄로 묶입니다. 같은 코드 경로를 그대로 가져다 쓴 fork와 다운스트림도 똑같이 영향을 받습니다.

영향 받는 버전과 해결 버전

제품영향 받는 버전해결 버전
NGINX PlusR36 P4 미만R36 P4 이상
NGINX PlusR35 / R34 / R33지원 라인업으로 마이그레이션
NGINX PlusR32 P6 미만R32 P6 이상
NGINX Open Source1.30.1 미만 (≥ 0.6.27)1.30.1 (stable) / 1.31.0 (mainline)
NGINX Open Source0.9.7 이하지원 라인업으로 마이그레이션

R35, R34, R33은 별도 패치 빌드가 없습니다. 지원 중인 라인(R36 P4 또는 R32 P6)으로 마이그레이션이 답입니다. F5 advisory는 K000161019, CVE 메타데이터는 NVD 페이지에 있습니다.

발현 조건

세 가지가 한 자리에서 만나야 합니다.

  1. rewrite 지시문이 있습니다.
  2. 그 뒤에 rewrite, if, set 중 하나가 따라옵니다.
  3. 대체 문자열에 이름 없는 PCRE 캡처($1, $2 …)와 물음표(?)가 같이 들어갑니다.

예를 들어 다음 같은 흔한 패턴이 그대로 함정입니다.

location /old/ {
# 취약: 이름 없는 $1 + 물음표 ?
rewrite ^/old/(.*)$ /new/$1?source=legacy break;
if ($http_user_agent ~ "bot") {
return 403;
}
}

이 자리에서 비인증 공격자가 특정 형태의 HTTP 요청을 한 번 보내면 워커의 힙에 경계 너머 쓰기가 일어납니다. ASLR이 꺼져 있으면 임의 코드 실행이 가능해집니다 — 워커 권한이므로 권한 상승은 그다음 단계지만, 컨테이너/구형 임베디드 환경에서는 그것만으로도 충분히 치명적입니다.

두 단계 스크립트 엔진에서 새는 상태 플래그

NGINX의 스크립트 엔진은 같은 rewrite를 두 번 돕니다. 길이 계산 패스(length pass)에서 출력 버퍼 크기를 정하고, 복사 패스(copy pass)에서 실제 값을 씁니다.

문제는 두 패스 사이에서 is_args 상태 플래그가 그대로 남는다는 점입니다. 길이 계산 단계에서 물음표를 만나 is_args = 1로 켜진 플래그가 복사 단계의 다음 캡처 처리에 누출됩니다. 복사 단계의 ngx_escape_uri()는 켜진 플래그를 보고 인자(query string)용 이스케이프 규칙으로 더 많은 바이트를 써넣습니다 — 길이 계산 단계에서는 다른 규칙으로 짧게 잡아 둔 그 버퍼에 말입니다.

그 결과 heap chunk 경계 너머에 의도된 길이만큼 정확히 흘러넘칩니다. 우연이 아니라 결정론적으로 재현되는 경로입니다.

패치(1.30.1 / 1.31.0)는 두 패스 진입 시 is_args를 명시적으로 reset하는 한 줄이 골자입니다. 18년 동안 묻혀 있던 이유는 발현 조건이 좁기 때문입니다. if / set이 뒤따르는 rewrite 체인에서만 두 패스가 같은 캡처를 두 번 보고, 거기에 이름 없는 캡처와 물음표가 동시에 있어야 합니다. 운영 환경에서 충분히 흔한 패턴인데 fuzz 코퍼스가 거기에 닿지 않았습니다.

임시 완화는 named capture로 교체

패치를 당장 못 올리는 환경에서는 F5와 depthfirst 모두 같은 권고를 합니다. 모든 rewrite에서 이름 없는 캡처를 이름 있는 캡처로 바꿉니다.

# Before (취약)
rewrite ^/old/(.*)$ /new/$1?source=legacy break;

# After (안전)
rewrite ^/old/(?<path>.*)$ /new/$path?source=legacy break;

이름 있는 캡처는 NGINX 스크립트 엔진의 다른 코드 경로를 타고, 거기에는 같은 플래그 누출이 없습니다. 동일한 동작을 유지하면서 결함 경로만 회피합니다.

운영 중인 모든 config 파일에서 이름 없는 캡처 패턴을 검색해 둘 자리만 골라냅니다.

grep -rnE 'rewrite[^;]*\$[0-9]' /etc/nginx/

지금 해야 할 일

  1. 버전을 확인합니다. nginx -v로 정확한 빌드를 봅니다. NGINX Plus는 nginx-plus -V에서 R 라인업이 나옵니다.
  2. 노출도를 평가합니다. 위의 grep 한 줄로 이름 없는 캡처 + 물음표 패턴을 가진 rewrite가 있는지 봅니다. 없으면 시간을 벌 수 있습니다.
  3. 패치를 적용합니다. Open Source는 1.30.1 / 1.31.0, NGINX Plus는 R36 P4 / R32 P6입니다. R33~R35는 지원 라인으로 마이그레이션합니다.
  4. 즉시 패치할 수 없으면 named capture로 우회합니다. 임시 패치 검증은 두 단계 — 정상 트래픽 회귀 테스트와 PoC 변형 요청 차단 확인으로 진행합니다.
  5. ASLR을 확인합니다. cat /proc/sys/kernel/randomize_va_space2인지 봅니다. 0이면 임의 코드 실행 위험이 같이 살아 있습니다.
  6. WAF/CDN 룰을 확인합니다. 앞단에 Cloudflare나 F5 BIG-IP 등이 있으면 벤더의 임시 시그니처가 이미 배포돼 있습니다. Cloudflare는 5월 13일 당일에 managed rule을 풀었습니다.
  7. 로그를 회수합니다. error.log에서 워커 재시작 패턴(worker process ... exited on signal 11)을 5월 13일 이후 기간으로 잡고 봅니다. 평소보다 빈도가 높으면 정찰 시도 흔적일 수 있습니다.

실 익스플로이트 현황

PoC는 DepthFirstDisclosures/Nginx-Rift repo에 공개돼 있습니다. VulnCheck의 canary 시스템이 5월 16일부터 인터넷 노출 NGINX를 향한 실 익스플로이트 시도를 잡기 시작했다. 공개에서 실 공격까지 사흘입니다. Ingress NGINX(K8s)로도 같은 결함이 그대로 전파됩니다. 클러스터 운영자 관점에서는 HeroDevs의 분석이 가장 친절합니다.

정리

항목
CVECVE-2026-42945 (NGINX Rift)
분류CWE-122 Heap-based Buffer Overflow
CVSSv4.0 9.2 Critical / v3.1 8.1 High
공개일2026-05-13
발견자depthfirst (with F5)
도입 시점2008년 NGINX 0.6.27
패치NGINX 1.30.1 / 1.31.0/NGINX Plus R36 P4 / R32 P6
임시 완화이름 없는 PCRE 캡처를 이름 있는 캡처로 교체
실 익스플로이트2026-05-16부터 관측 (VulnCheck)

if/set이 따라붙는 rewrite 체인은 어디서나 자주 써요. 운영하는 NGINX가 한 대라도 외부에 노출돼 있고, rewrite config에 $1 같은 표현이 한 줄이라도 들어 있다면, 지금이 1.30.1 패치를 올릴 가장 좋은 순간이에요.

참고

ClaudeBleed 권한 취약점

· 약 9분

무슨 결함인가

Anthropic의 Chrome Extension Claude for Chrome(v1.0.69 이하)에서 권한이 전혀 없는 다른 Extension 하나가, 사용자 동의 한 번 없이 Claude 에이전트를 통째로 빌려 Gmail, Google Drive, GitHub의 데이터를 조회하고 외부로 빼낼 수 있는, 발신자 신원 검증이 빠진 결함이 공개됐어요.

LayerX Security가 2026-05-07에 공개했고, 발견자가 붙인 이름은 ClaudeBleed입니다. 2014년 OpenSSL의 Heartbleed, 2017년 Cloudflare의 Cloudbleed 계보를 잇는 "데이터가 새어 나온다"는 작명을 오마주했습니다. Anthropic은 하루 앞선 5월 6일 v1.0.70으로 부분 패치를 배포했지만, 같은 연구자가 약 3시간 만에 우회 경로를 찾아 함께 공개했습니다. 5월 13일 현재까지 추가 패치는 발표되지 않았으며, CVE 번호도 아직 할당되지 않았습니다.

요약 표

항목내용
취약점 이름ClaudeBleed
영향 제품Claude for Chrome (Chrome 브라우저 Extension)
영향 버전v1.0.69 이하
부분 패치v1.0.70 (2026-05-06, 우회 경로 확인됨)
추가 패치미발표 (2026-05-13 기준)
CVE미할당
발견자 / 공시LayerX Security, 2026-05-07
분류Trust Boundary Violation (CWE-501 계열)
공격 전제사용자가 다른 Chrome Extension 하나를 설치하고 있을 것
사용자 상호작용불필요
권한 요구공격용 Extension에 별도 권한 불필요

externally_connectable은 누구를 신뢰하는가

Chrome Extension 매니페스트의 externally_connectable 키는 "이 Extension의 메시지 채널을 누가 호출해도 되는가"를 선언합니다. 보통 두 가지 형태로 씁니다.

// 형태 A — 다른 Extension ID로 한정
{
"externally_connectable": {
"ids": ["<신뢰하는 Extension ID>"]
}
}

// 형태 B — 특정 웹 출처(origin)로 한정
{
"externally_connectable": {
"matches": ["*://*.claude.ai/*"]
}
}

Claude for Chrome은 두 번째 형태로, claude.ai 출처에서 실행되는 페이지 스크립트가 Extension에 메시지를 보낼 수 있도록 열어 뒀습니다. 사용자 입장에서 Claude 사이드 패널과 claude.ai가 한 몸처럼 동작해야 하니, 설계 의도는 자연스럽습니다.

문제는 origin이 답할 수 있는 질문의 범위입니다. origin은 "이 코드가 어느 페이지 컨텍스트에서 실행되는가"는 알지만, "그 컨텍스트 안에서 누가 그 코드를 실행시켰는가"는 모릅니다. Chrome의 Extension 모델은 임의의 Extension이 자기 content script를 다른 출처의 페이지에 주입할 수 있게 허용하며, 매니페스트의 "world": "MAIN" 옵션을 켜면 그 스크립트는 페이지 자체의 자바스크립트 컨텍스트에서 돕니다.

여기서 권한 0짜리 Extension이란, manifest.json에 permissions(tabs, storage, cookies 등)도 host_permissions(https://mail.google.com/* 같은 사이트 접근 권한)도 적지 않은 Extension을 말합니다. 사용자가 설치할 때 "이 Extension은 ~ 권한이 필요합니다" 같은 경고 화면이 거의 뜨지 않거나 가장 가벼운 형태로만 뜹니다. 그런데 content_scripts 항목은 권한 요청이 아니라 "어떤 페이지에서 이 자바스크립트를 실행해 달라"는 등록이라, 별도 권한 없이도 매니페스트에 적은 origin(여기서는 *://*.claude.ai/*)에 자기 스크립트를 끼워 넣을 수 있습니다.

즉 권한 0짜리 Extension 하나가:

  1. content script로 claude.ai에 스크립트를 한 장 주입하고,
  2. 그 스크립트가 페이지 컨텍스트에서 chrome.runtime.sendMessage("<Claude Extension ID>", {...}) 한 줄을 실행하면,
  3. Claude Extension 입장에서는 "claude.ai에서 들어온 정상 메시지"로 보입니다.

Claude Extension의 ID(fcoeoabgfenejglbffodgkkbkcdhcgfn)는 Chrome Web Store에 그대로 공개돼 있고, Extension ID는 비밀이 아닙니다. 결과적으로 origin 화이트리스트는 ACL이 아니라 누구나 통과시키는 표지판에 가깝습니다.

LayerX는 이 호출로 도달 가능한 핸들러 중 하나로 onboarding_task를 지목했는데, 이 핸들러는 외부에서 받은 텍스트를 그대로 Claude의 작업 프롬프트로 흘려보냅니다. 즉 메시지 한 통이면 임의 프롬프트 실행과 같습니다.

공격 사슬

전체 동작은 한 장으로 정리됩니다.

여기까지가 진입입니다. Extension ID와 origin만 알면 누구든 Claude 에이전트의 작업 큐에 항목을 하나 밀어 넣을 수 있습니다. 그 다음 단계, 즉 "어떻게 사용자 몰래 그 작업을 끝까지 수행하게 만드는가"가 아래 4가지 공격 기법으로 나뉩니다.

4가지 공격 기법

1. Confused Deputy

Claude Extension은 "claude.ai에서 온 메시지면 정상"이라는 전제로 동작합니다. 그러나 실제 메시지를 만든 주체는 사용자가 띄운 claude.ai 페이지가 아니라, 사용자가 설치해 둔 다른 Extension이 페이지 위에 얹어 놓은 스크립트입니다. 정당한 권한을 가진 대리인(Claude Extension)이 자기 권한 밖의 호출자(악성 Extension)를 위해 일하게 되는, 교과서적인 confused deputy 구도입니다.

이 구도가 위험한 이유는 권한 상승의 비용이 0이라는 점입니다. 공격 Extension은 호스트 권한도, 탭 권한도, storage도 필요 없습니다. content script 하나만 선언하면 끝납니다.

2. 승인 루핑(Approval Looping)

Claude는 민감한 작업을 수행하기 전 사용자에게 확인을 받는 단계를 거칩니다. 그런데 그 확인이 "지금 이 특정 행동에 대한 동의"가 아니라 "지금 사용자가 활성화한 모드"로만 검사되기 때문에, 같은 채널로 "Yes, proceed"에 해당하는 메시지를 반복 전송하면 확인 흐름을 그대로 통과시킬 수 있습니다.

토큰이 특정 행동에 바인딩돼 있지 않은 탓입니다. 한 번 받은 동의가 다음 동의가 필요한 자리에서 다시 통하면, 동의 절차는 이름만 남고 실제 통제력을 잃습니다.

3. DOM 라벨 위장

Claude 에이전트는 페이지의 DOM을 읽어 어떤 버튼이 무슨 동작인지 판단합니다. 악성 Extension이 DOM을 살짝 바꿔 두면(예를 들어 "외부에 공유" 버튼의 라벨을 "피드백 요청"으로) 에이전트의 인식과 실제 동작이 어긋납니다. 에이전트는 무해한 작업으로 알고 누르지만, 실제 클릭은 원래 핸들러를 그대로 부릅니다.

이 기법은 ClaudeBleed에만 국한되지 않는, 브라우저 안에서 도는 모든 AI 에이전트의 일반 약점에 가깝습니다. 에이전트가 의미를 DOM에서 끌어내는 한, DOM을 만질 수 있는 쪽이 의미를 다시 정의할 수 있습니다.

4. Privileged Mode 우회

Claude for Chrome은 두 가지 모드를 둡니다. Ask before acting(매 행동마다 확인)과 Act without asking(자율 수행). v1.0.70 패치는 Ask 모드에서 외부 메시지가 임의 행동을 일으키지 못하도록 사이드 패널 안에 명시적 승인 흐름을 추가했습니다.

하지만 LayerX는 공격자가 사이드 패널의 초기화 흐름을 가로채 Act without asking 쪽으로 진입시키는 우회 경로를 약 3시간 만에 확인했습니다. 모드 전환이 사용자에게 통보되지 않기 때문에, 사용자는 자기 Extension이 자율 모드로 돌고 있는지 알 길이 없습니다.

요약하면 패치는 증상인 승인 UI 흐름만 가렸을 뿐, 원인인 메시지 발신자 신원 검증 부재는 손대지 못했습니다. 발신자가 누구인지 확인하지 못하는 한 어느 모드에서든 우회 경로는 다시 열립니다.

이 결함으로 가능한 위협 시나리오

이 결함이 가능하게 만드는 작업은 평범한 사용자 시나리오로 옮기면 그대로 사고로 이어집니다.

  • Gmail의 받은편지함을 요약시켜 외부 주소로 메일 발송
  • Google Drive 비공개 문서에 공유 링크를 발급해 외부 노출
  • GitHub 비공개 리포지토리의 파일 내용을 챗 응답에 끼워 빼내기
  • 사용자 대신 메일 발송, 문서 삭제로 흔적 정리
  • 외부 캘린더 초대, Slack 메시지 발송 같은 OAuth 권한 위임 작업 일반

세 가지가 함께 성립한다는 점이 무겁습니다. 공격 Extension에 별도 권한이 필요 없고, 사용자 상호작용도 필요 없으며, EDR이나 안티바이러스 관점에서는 정상 Extension의 정상 호출처럼 보입니다.

특히 사내 표준 브라우저에 권장 Extension 목록만 통제하는 조직에서는, Claude for Chrome이 그 권장 목록에 있고 사용자가 자율적으로 다른 Extension 하나를 추가했다는 사실만으로 위협 모델이 무너집니다.

공개 타임라인

날짜이벤트
2026-04-22영향 받는 v1.0.69 배포
2026-04-27LayerX, Anthropic에 결함 보고
2026-04-28Anthropic "이전 보고와 중복, 곧 수정 예정" 응답
2026-05-06v1.0.70 부분 패치 배포
2026-05-07LayerX, 약 3시간 만에 우회 경로 확인 후 공시
2026-05-11외신, 추가 패치 부재 후속 보도
2026-05-13본 글 작성 시점 (추가 패치 미발표)

보고에서 패치까지 9일, 패치에서 우회 공개까지 1일입니다. 빠르게 막은 흔적은 있지만, "누가 메시지를 보냈는지 다시 설계하는" 종류의 수정은 그 사이에 들어가지 못한 모양새입니다.

사용자가 오늘 할 일

근본 패치가 발표되기 전까지, 권장 조치는 단순합니다.

1) Claude for Chrome Extension 끄기 또는 제거하기

chrome://extensions 에서 Claude for Chrome 항목의 토글을 끄는 것만으로 공격 표면이 즉시 사라집니다. 자주 안 쓴다면 제거가 가장 깔끔합니다. 데스크톱 클라이언트나 웹 claude.ai 사용에는 영향이 없습니다.

2) 끌 수 없다면 민감 세션과 분리하기

Claude for Chrome을 켜 둔 프로파일에는 Gmail, Google Drive, GitHub 같은 민감 서비스에 로그인하지 않습니다. Chrome 프로파일 분리(다른 사용자) 또는 별도 브라우저(Chrome Canary, Firefox 등)로 일/개인 작업과 AI 에이전트 작업을 분리해 둡니다.

3) 다른 Extension도 함께 점검하기

ClaudeBleed의 트리거는 "다른 Extension 하나"입니다. 사용 중인 Extension의 수와 신뢰도를 같이 봅니다.

chrome://extensions → 세부 정보 → 사이트 액세스 / 권한

설치 출처가 불분명하거나, 더 이상 안 쓰는 Extension은 이 기회에 정리해 둡니다. content script 권한이 광범위한 Extension(예: <all_urls>)일수록 위험 가중치가 높습니다.

4) 조직 단위 통제가 가능한 경우

ChromeOS/Chrome Enterprise 환경에서는 ExtensionInstallBlocklist로 Claude for Chrome Extension ID(fcoeoabgfenejglbffodgkkbkcdhcgfn)를 막거나, ExtensionInstallAllowlist로 허용 Extension 목록을 좁힌 뒤 차차 풀어 가는 방식이 안전합니다. BYOD 환경이면 사용자 공지와 함께 자율 비활성화를 권고합니다.

정리

  • ClaudeBleed의 본질은 코드 한 줄짜리 버그가 아니라 origin을 신원으로 착각한 설계에 있습니다. Chrome Extension 모델이 허용하는 content script + MAIN world 조합 앞에서 origin 화이트리스트는 ACL 노릇을 못 합니다.
  • v1.0.70 패치는 승인 UI 흐름을 추가했지만, 메시지 발신자 신원 검증 부재라는 원인은 그대로 남았습니다. 그래서 Act without asking 모드를 강제 진입시키는 우회 경로가 같은 날 다시 열렸습니다.
  • 에이전트가 강할수록 누가 부르는지를 더 정밀히 따져야 합니다. 발신자 인증(서명 토큰, Extension ID 화이트리스트), 행동-바인딩된 일회성 동의가 합쳐져야 비로소 동의가 동의로 기능합니다.
  • 추가 패치 전까지는 Claude for Chrome 사용을 잠시 멈추는 게 가장 단순하고 확실한 완화입니다. 끌 수 없다면 민감 세션과 프로파일을 분리합니다.

이번 사건이 Anthropic만의 문제로 끝나지 않을 가능성도 함께 짚어 둬요. 브라우저 안에서 도는 AI 에이전트 Extension은 모두 같은 함정(외부 메시지 채널, DOM을 통한 의미 인식, 자율 모드의 UX/보안 트레이드오프) 위에 서 있어요. ClaudeBleed는 그 함정 중 첫 번째 사례로 기록될 공산이 커요.

참고

Copy Fail 취약점

· 약 8분

732바이트짜리 파이썬 스크립트 하나로 2017년 이후 빌드된 거의 모든 주요 리눅스 배포판에서 root를 따낼 수 있습니다. 이번에 공개된 커널 논리 버그 Copy Fail 이야기예요.

표준 라이브러리만 쓰고, race condition도 없고, 배포판별 오프셋도, 컴파일된 셸코드 페이로드도 필요 없습니다. Ubuntu, RHEL, Amazon Linux, SUSE에서 같은 스크립트가 그대로 돕니다. 어제(2026-04-29) 공개됐고, 발견자는 한국 보안업체 Theori의 Taeyang Lee, 도구는 자체 AI 코드 감사기 Xint Code입니다.

Dirty COW/Dirty Pipe와 무엇이 다른가

이 자리는 익숙합니다. 2016년의 Dirty COW, 2022년의 Dirty Pipe도 모두 비특권 사용자가 페이지 캐시에 손을 대서 setuid 바이너리를 갈아치우는 LPE였습니다. 그런데 그 둘과 Copy Fail의 결정적인 차이는 race가 없다는 점입니다.

Dirty COW (2016)Dirty Pipe (2022)Copy Fail (2026)
클래스race conditionflag 초기화 누락직선형 논리 버그
성공률확률적 (수십~수천 회 시도)거의 결정론적첫 시도 결정론적
배포판별 오프셋필요할 때 있음거의 불필요불필요
컴파일된 페이로드보통 필요종종 필요불필요 (Python 표준 라이브러리만)
영향 기간~2007–2016~2020–20222017–패치 적용일

"직선형 논리 버그"라는 표현이 이 결함을 잘 짚습니다. 흐름이 분기 없이 일자로 흐르고, 각 단계가 의도대로 떨어지면 페이지 캐시의 정해진 위치에 정해진 바이트가 쓰입니다. 운영자 입장에서는 가장 무서운 형태의 LPE인데, 패치하기 전까지는 누구나 한 번에 성공하기 때문입니다.

세 가지 변경의 우연한 교차점

Copy Fail은 단일 버그가 아닙니다. 시기가 다른 세 가지 커널 변경이 한 자리에서 만나면서 만들어진 함정입니다.

시기변경원래 의도
2011authencesn 템플릿 추가IPsec ESP의 64비트 Extended Sequence Number 지원. 처음부터 호출자의 destination scatterlist를 임시 저장 공간으로 재사용
~2014AF_ALG 소켓에 AEAD 지원(algif_aead.c) + splice() 경로사용자 공간이 커널 crypto API를 소켓처럼 쓰도록 하기 위함
2017algif_aead에 in-place AEAD 최적화 (commit 72548b093ee3)메모리 절약 목적. 복호화 시 req->src = req->dst로 묶고, tag 페이지는 sg_chain()으로 참조만 연결

각각만 보면 그럴듯한 변경입니다. 문제는 셋이 만나는 지점입니다.

  1. splice()읽기 전용 파일의 페이지 캐시 페이지를 AF_ALG의 TX scatterlist에 그대로 참조로 넣습니다.
  2. 2017년 인-플레이스 최적화가 그 TX scatterlist를 그대로 쓰기 가능한 dst scatterlist로 재사용합니다.
  3. authencesndst[assoclen + cryptlen] 위치에 ESN 임시값(4바이트)을 씁니다. 그 자리가 1번에서 체인된 페이지 캐시 페이지입니다.

그 결과 비특권 사용자가 임의 파일의 페이지 캐시에 결정론적인 4바이트 쓰기를 하게 됩니다. 거의 10년 동안 조용히 익스플로잇 가능한 상태로 있었습니다.

익스플로잇 흐름

PoC는 copy.fail에 공개돼 있고 본문에는 옮기지 않습니다. 동작 원리는 다음과 같습니다.

순서를 풀면 이렇습니다.

  1. AF_ALG 소켓을 열어 authencesn(...) 알고리즘에 bind합니다.
  2. 타깃 setuid 바이너리(/usr/bin/su 등)를 열어 그 페이지 캐시 페이지를 splice()로 소켓의 TX scatterlist에 주입합니다.
  3. 쉘코드를 4바이트 단위로 잘라, 매번 위치 계산을 맞춰 sendmsg() + splice() 페어를 반복합니다.
  4. recv()를 호출하면 복호화 동작이 이뤄지며 authencesn이 ESN 임시값을 dst(즉 손에 들어온 페이지 캐시 페이지)에 4바이트씩 기록합니다.
  5. 마지막으로 execve("/usr/bin/su"). 손상된 페이지 캐시에서 로드된 setuid 바이너리가 쉘코드를 root 권한으로 실행합니다.

PoC가 이렇게 짧은 이유는 모든 단계가 표준 syscall과 표준 라이브러리만 쓰기 때문입니다. 컴파일러도, 외부 페이로드도 필요 없습니다.

왜 이게 무서운가

세 가지가 겹칩니다.

먼저 디스크를 건드리지 않습니다. 손상은 페이지 캐시에서만 일어나기 때문에 디스크 파일의 해시는 그대로입니다. AIDE/Tripwire 같은 무결성 모니터링이 잡지 못합니다. 재부팅하거나 페이지가 evict되면 캐시는 정상 복원되지만, 그 사이에 익스플로잇은 끝납니다.

다음으로 페이지 캐시는 호스트 전체가 공유합니다. 컨테이너는 같은 호스트 커널의 같은 페이지 캐시를 봅니다. 즉 비특권 컨테이너 안에서 일으킨 4바이트 쓰기가 호스트의 setuid 바이너리, 또는 옆 컨테이너의 read-only mount 파일을 손상시킬 수 있습니다. K8s 노드, 멀티테넌트 SaaS, 공유 CI 러너가 1순위 위험군입니다. namespace 격리만 쓰는 컨테이너 모델이 이 한 줄짜리 버그 앞에서 그대로 뚫립니다.

마지막으로 CVSS는 7.8(High)로 매겨졌습니다. Critical이 아닌 이유는 로컬 액세스가 필요해서일 뿐이고, 한번 로컬이 잡히면 결정론적으로 root까지 직행합니다. 실무 영향은 Critical과 다를 게 없습니다.

AI가 한 시간 만에 찾았다는 것의 의미

이 발견의 또 다른 화제는 도구입니다. 발견자는 Theori의 Taeyang Lee, 도구는 자체 개발한 AI 기반 보안 스캐너 Xint Code입니다. 단일 오퍼레이터 프롬프트로 Linux 커널의 crypto/ 서브시스템 전체를 약 한 시간 동안 감사했고, 이 버그가 가장 심각한 항목으로 보고됐습니다.

AI가 사람보다 똑똑해서 찾은 것은 아닙니다. "AF_ALG와 splice가 만나는 자리가 실은 거대한 비특권 공격 표면"이라는 가설을 사람이 정해주면, AI는 그 가설을 전체 코드 경로에 결정론적으로 적용해 보는 일을 사람과 비교가 안 되게 잘합니다. 그렇게 10년 동안 사람 손에 안 잡힌 코드 경로 하나를 끄집어냈습니다.

The Register는 Trend Micro Zero Day Initiative의 Dustin Childs를 인용해 "최근 취약점 제보 폭증의 가장 큰 변수는 AI 기반 버그 발견"이라고 보도했습니다. 이 흐름이 운영자 입장에서 의미하는 건 단순합니다. 패치 사이클이 더 짧아져야 하고, 오늘 멀쩡해 보이는 코드가 내일도 멀쩡하리라 가정하지 말아야 한다는 것입니다.

공개 타임라인

날짜이벤트
2026-03-23Linux 커널 보안팀에 보고
2026-03-25패치 제안
2026-04-01메인라인 커널 커밋 (a664bf3d603d)
2026-04-22CVE-2026-31431 할당
2026-04-29공개 공시

보고에서 메인라인 패치까지 9일이 걸렸습니다. 빠른 편입니다. 그러나 메인라인 패치와 각 배포판의 안정 커널이 사용자 손에 들어오는 시점 사이에는 늘 며칠에서 몇 주의 갭이 있고, 그 사이가 운영자에게 가장 위험한 구간입니다.

운영자 대응 가이드

즉시 완화, 정식 패치, 검증 세 단계로 나눠 봅니다.

1) 즉시 완화: algif_aead 모듈 차단 (재부팅 불필요)

LUKS, kTLS, IPsec은 AF_ALG 사용자 공간 인터페이스가 아닌 다른 경로를 쓰기 때문에 영향이 없습니다. 영향을 받는 건 OpenSSL의 afalg 엔진처럼 AF_ALG를 명시적으로 호출하는 경우뿐입니다. 즉 일반 서버에서는 모듈 차단으로 인한 부작용이 거의 없습니다.

단, 차단이 가능한지부터 확인해야 합니다. RHEL 9/10 일부 빌드는 algif_aead가 LKM이 아니라 커널 빌트인이고, 그러면 modprobe로 막을 수 없습니다.

# 모듈로 로드돼 있는지
lsmod | grep algif_aead

# 빌트인인지 (filename: (builtin) 이면 차단 불가)
modinfo algif_aead 2>/dev/null | head -3

LKM인 경우, modprobe로 차단합니다.

echo 'install algif_aead /bin/true' | sudo tee /etc/modprobe.d/disable-algif_aead.conf
sudo rmmod algif_aead 2>/dev/null || true

/bin/false 대신 /bin/true를 쓰는 이유는 다른 모듈이 의존성으로 algif_aead를 끌어올 때 modprobe가 에러를 뱉지 않도록 하기 위해서입니다. 어느 쪽이든 모듈은 올라오지 않습니다.

빌트인인 경우, modprobe 차단은 효과가 없습니다. 두 가지 우회 중 하나를 씁니다.

  • SELinux/AppArmor로 AF_ALG 사용을 제한합니다. 일반 사용자 도메인에서 socket(AF_ALG, ...) 호출 자체를 막는 정책입니다. 정책 변경이라 운영 검증이 필요합니다.
  • 정식 패치 일정을 앞당깁니다. 빌트인 환경에서는 사실상 이 옵션이 가장 현실적입니다.

2) 정식 패치 (재부팅 필요, kpatch 가능 시 무중단)

RHEL/Rocky/Alma:

sudo dnf updateinfo list cves | grep CVE-2026-31431 # RHSA 발행 확인
sudo dnf update kernel kernel-core kernel-modules
sudo reboot

Ubuntu/Debian:

sudo apt update
apt list --upgradable 2>/dev/null | grep -E '^linux-(image|generic|headers)'
sudo apt install --only-upgrade linux-image-$(uname -r | sed 's/-generic//')-generic
sudo reboot
# Ubuntu Pro의 경우
sudo pro status
canonical-livepatch status # 라이브 패치 발행 여부

라이브 패치(kpatch/canonical-livepatch)가 발행돼 있다면 재부팅 없이 적용 가능합니다. 프로덕션 노드는 우선 라이브 패치로 막은 뒤 다음 정기 점검 창에서 재부팅하는 편이 안전합니다.

3) 검증

uname -r

# RHEL 계열
rpm -q --changelog kernel-core | grep -i 31431 | head

# Debian/Ubuntu 계열
apt changelog linux-image-$(uname -r) 2>/dev/null | grep -i 31431 | head

CHANGELOG에 CVE 번호가 보이면 패치된 빌드입니다. 라이브 패치를 적용한 경우에는 kpatch list 또는 canonical-livepatch status로 적재된 패치 목록을 함께 확인합니다.

배포판 대응 현황

배포판초기 대응비고
Debian즉시 패치안정 채널에 빠르게 반영
Ubuntu즉시 패치Pro 사용자에겐 라이브 패치 동시 발행
SUSE즉시 패치SLES/openSUSE 모두
Red Hat처음 보류 → 입장 변경 후 신속 대응일부 빌드에서 algif_aead builtin이라 즉시 완화 옵션이 제한됨
Amazon Linux즉시 패치AL2023 우선
Arch / Fedora즉시 패치롤링 특성상 가장 빠름

여러 배포판이 초기에 "Moderate"로 분류했다가 PoC 공개와 함께 "High"로 재분류한 점도 이번 사례의 특징입니다. race 없는 LPE의 운영 영향이 통상 분류보다 크다는 걸 확인한 결과입니다.

정리

  • Copy Fail은 race 없는 결정론적 LPE입니다. 패치 전까지는 누구나 한 번에 root를 따냅니다.
  • 단일 버그가 아니라 2011, 2014, 2017년에 들어간 세 변경의 우연한 교차점에서 만들어졌습니다.
  • 페이지 캐시 쓰기 프리미티브라 디스크 무결성 검사로는 잡히지 않고, 컨테이너 격리만으로는 막지 못합니다.
  • 즉시 완화는 algif_aead 모듈 차단이지만 빌트인 환경에서는 무효입니다. 환경부터 확인해야 합니다.
  • 정식 패치는 재부팅이 필요합니다. kpatch나 canonical-livepatch가 가능한 환경이면 무중단 적용을 우선 검토합니다.

다음 글에서는 이 글에서 다루지 않은 두 가지를 따로 봐요. 컨테이너 탈출(user namespace 경계에서의 동작 검증), 그리고 AI 기반 보안 스캐너를 운영 파이프라인에 도입할 때의 트레이드오프예요.

참고