뒤로

2026/10/28

Node2Shell(CVE-2026-111497): 위험에 노출된 시스템과 지금 해야 할 조치

Brotli 취약점 CVE-2026-111497에 실제로 노출되는 시스템, 사례별 긴급도, 그리고 지금 바로 패치·재빌드·재시작·완화하는 방법을 정리합니다.

2026년 10월 27일 업데이트

“Node2Shell” 또는 “HTTP2Shell”이라는 별칭으로 불리는 CVE-2026-111497은 Brotli 압축 해제기(디코더)의 메모리 손상 버그입니다. Brotli는 웹 전반에서 사용되는 br 압축 방식입니다. 이 버그는 Google Brotli 라이브러리 1.0.3부터 1.2.0 버전까지에 존재합니다 [1]. Node.js는 이 라이브러리를 번들로 포함하고 있으며, Node.js 프로젝트는 22.x, 24.x, 26.x 계열에 대해 이 문제의 심각도를 High로 평가했습니다 [2]. 동일한 디코더가 Python 패키지, Linux 시스템 라이브러리, 데스크톱 앱, 개발자 도구에도 포함되어 있으므로 실제 노출 범위는 “Node.js 서버”보다 훨씬 넓습니다.

이 글은 자사 시스템이 노출되어 있는지, 무엇부터 해야 하는지를 신속하게 판단해야 하는 팀을 위한 것입니다.

핵심 요약: 귀사가 통제하지 않는 상대(인터넷상의 클라이언트, 외부 웹사이트, 웹훅 발신자, 업로드된 파일 등)로부터 오는 Brotli 데이터를 압축 해제하는 시스템이 하나라도 있다면 긴급 사안으로 다루십시오. 패치, 재빌드, 재시작을 수행하고, 그동안에는 br을 차단하십시오.

취약점의 동작 방식

디코더는 압축 스트림 안의 잘못된 형식의 “블록 스위치”를 제대로 처리하지 못합니다. 이 오류를 “데이터가 유효하지 않음”이 아니라 “입력이 더 필요함”으로 처리하고, 작은 고정 크기 내부 버퍼에 바이트를 계속 덧붙이다가 결국 버퍼 끝을 넘어서 쓰게 됩니다 [2]. 조작된 Brotli 스트림을 애플리케이션이 압축 해제하도록 만들 수 있는 공격자는 프로세스 메모리를 손상시킬 수 있습니다. 그 결과 다음과 같은 일이 발생할 수 있습니다.

  • 크래시(서비스 거부). 가장 확실하게 발생하는 결과입니다.
  • 프로세스 메모리 유출. 응답이나 로그를 통해 시크릿, 토큰, 다른 사용자 데이터의 일부가 노출될 수 있습니다.
  • 원격 코드 실행 가능성. Node.js는 가능한 영향으로 RCE를 명시하고 있습니다 [2]. 처음 공개된 개념 증명(PoC)은 의도적으로 약화시킨 빌드에서만 코드 실행에 성공했지만, 이후 연구자들은 수정되지 않은 공식 Node.js 24.22.0 빌드에서도 신뢰성은 낮지만 코드 실행이 가능하다고 보고했습니다 [3].

인증은 필요하지 않으며, 공격자는 디코더에 도달하는 바이트만 전달하면 됩니다. 이 때문에 CVSS 4.0 점수가 9.2(Critical)입니다 [1]. 따라서 모든 시스템에 대해 던져야 할 핵심 질문은 “Brotli를 사용하는가?”가 아니라 “신뢰할 수 없는 상대가 우리 시스템에 Brotli 압축 해제를 시킬 수 있는가?”입니다.

대표적인 사용 사례와 위험도

사례 1: 압축된 요청 본문을 받는 인터넷 공개 API (최우선)

일부 Node.js API 서버, BFF, API 게이트웨이, 업로드 엔드포인트는 Content-Encoding: br로 전송된 요청 본문을 압축 해제합니다. 미들웨어를 통해 같은 처리를 하는 경우도 있습니다. 위험: 인터넷상의 익명 클라이언트 누구나 조작된 본문을 보낼 수 있습니다. 영향은 서비스를 반복적으로 다운시키는 저비용 DoS부터 세션 데이터와 API 키를 보유한 프로세스의 메모리 유출까지 다양합니다. 긴급도: 비상. 오늘 패치하고, 패치가 끝날 때까지 엣지에서 br 요청 본문을 차단하십시오.

사례 2: 외부에서 콘텐츠를 가져오는 서비스 (높은 우선순위)

웹훅 수신 서비스, 크롤러와 스크레이퍼, 링크 미리보기 및 RSS 서비스, 결제 또는 SaaS 연동, 웹을 탐색하는 AI 에이전트는 모두 아웃바운드 HTTP 요청을 보냅니다. 많은 HTTP 클라이언트가 Accept-Encoding: br을 보내고 응답을 자동으로 압축 해제합니다. Python에서는 Brotli 또는 brotlicffi 패키지가 설치되어 있으면 코드에서 이를 import하지 않더라도 urllib3와 requests가 이 동작을 수행합니다. aiohttp도 마찬가지이며, httpx는 brotli extra와 함께 설치된 경우 같은 동작을 합니다 [4]. 위험: 악의적이거나 침해되었거나 위장된 원격 서버가 응답을 통해 백엔드를 공격할 수 있습니다. 공격자가 귀사 네트워크에 접근할 필요조차 없습니다. 귀사가 먼저 상대를 호출하기 때문입니다. 긴급도: 높음. 접속 대상을 완전히 신뢰할 수 없는 곳부터 먼저 패치하십시오.

사례 3: 런타임이 내장된 데스크톱 앱, AI 코딩 에이전트, CLI

Electron 앱, Bun 또는 Node 기반 CLI, AI 개발자 도구는 자체 런타임 사본을 포함하고 있습니다. 시스템의 node 패키지를 업데이트해도 이들은 수정되지 않습니다. 디스크의 앱을 업데이트해도 이미 실행 중인 사본은 수정되지 않습니다. 장시간 실행되는 프로세스는 재시작할 때까지 이전 런타임을 메모리에 유지하기 때문입니다. 위험: 이러한 도구는 자격 증명을 보유하고 외부 콘텐츠를 가져오는 경우가 많아, 사례 2에 권한 있는 프로세스가 결합된 형태가 됩니다. 긴급도: 신뢰할 수 없는 콘텐츠를 가져오는 도구는 높음. 업데이트한 다음 반드시 실제로 재시작하십시오. “업데이트했는데도 여전히 이전 코드가 실행 중”인 함정은 HTTP2Shell: 바이너리를 업데이트해도 실행 중인 프로세스가 조치되지 않는 이유에서 자세히 다루었습니다.

사례 4: 컨테이너 이미지와 CI/CD 아티팩트

수정 이전에 FROM node:22, node:24 또는 node:26으로 빌드된 이미지에는 호스트를 패치했더라도 취약한 런타임이 그대로 남아 있습니다. 번들된 Python wheel과 벤더링된 의존성도 마찬가지입니다. 위험: 이미지를 재빌드하기 전까지는 배포와 오토스케일링이 일어날 때마다 취약한 코드가 되살아납니다. 긴급도: 사례 1과 2에 해당하는 서비스의 이미지는 높음. 재시작만 하지 말고 재빌드하십시오.

사례 5: Linux 시스템 라이브러리, 폰트, 이미지 처리

배포판은 디코더를 공유 라이브러리로 제공합니다(예: Ubuntu/Debian의 libbrotli1). 이 라이브러리는 압축이 활성화된 경우 curl과 libcurl에서, WOFF2 웹 폰트를 위해 FreeType에서 사용되며, Pillow 같은 이미지 및 문서 처리 도구에서도 간접적으로 사용됩니다. 위험: 사용자가 업로드한 폰트나 문서를 렌더링하는 시스템, 또는 curl --compressed로 외부 콘텐츠를 가져오는 시스템에는 신뢰할 수 없는 Brotli 데이터가 도달할 수 있습니다. 외부 데이터를 이러한 라이브러리에 전혀 전달하지 않는 서버라면 실질적인 노출은 낮습니다. 긴급도: 대부분의 경우 중간이지만, 문서 렌더링이나 업로드 처리 서비스는 높음. Ubuntu는 22.04, 24.04, 26.04, 26.10용으로 수정된 brotli 패키지를 배포했습니다 [1]. RHEL, Oracle Linux 및 기타 배포판의 경우 상태를 추측하지 말고 brotli 패키지에 대한 벤더 보안 권고를 확인하십시오.

사례 6: 신뢰할 수 있는 입력만 다루는 내부 전용 시스템 (낮은 우선순위)

내부 캐시나 자체 빌드 아티팩트처럼 직접 생성한 데이터만 Brotli로 압축 해제하는 서비스도 있습니다. 이들은 위험이 낮지만 0은 아닙니다. 이미 거점을 확보한 공격자가 이 버그를 이용해 내부로 더 침투할 수 있기 때문입니다. 긴급도: 다음 정기 주기에 패치하십시오.

대응 방법: 단계별 조치

1단계: Brotli가 디코딩되는 곳 찾기 (오늘 중)

  • Node.js: 모든 호스트와 모든 컨테이너 이미지, CI 러너에서 node -v를 확인합니다. 코드와 설정에서 brotliDecompress, createBrotliDecompress, 그리고 br을 압축 해제하는 본문 파싱 미들웨어를 검색합니다.
  • 내장 런타임: 사용 중인 Electron 앱, Bun/Node CLI, AI 에이전트의 목록을 만들고, 앱 버전뿐 아니라 각각이 내장한 런타임 버전을 기록합니다.
  • Python: 각 환경과 이미지에서 pip show Brotli brotlicffi를 실행합니다. 락 파일과 SBOM에서 httpx[brotli] 및 전이적 의존성을 확인합니다.
  • OS 패키지: dpkg -l | grep brotli 또는 rpm -qa | grep brotli를 실행한 다음, 실제로 라이브러리를 로드하는 프로세스를 확인합니다.
  • 위의 사례를 기준으로 우선순위를 정합니다. 신뢰할 수 없는 입력이 도달할 수 있는 모든 것(사례 1, 2, 3, 그리고 업로드를 처리하는 사례 5)이 최우선입니다.

2단계: 영향받는 구성 요소 패치

  • Node.js: 22.23.4, 24.22.1 또는 26.12.1 이상으로 업그레이드합니다 [2].
  • Python: Brotli>=1.2.1 또는 brotlicffi>=1.2.1.0. 락 파일을 다시 생성합니다 [4].
  • 업스트림 Brotli / 벤더링된 C 코드: 브랜치에 따라 1.0.10, 1.1.1 또는 1.2.1 [1].
  • OS 패키지: 배포판에서 제공하는 brotli / libbrotli 보안 업데이트를 적용합니다.
  • 데스크톱 앱과 에이전트: 수정된 런타임이 포함되었다고 명시한 벤더 릴리스로 업데이트합니다. 벤더가 아무것도 공지하지 않았다면 해당 앱을 안전하다고 보지 말고 미검증 상태로 취급하십시오.

3단계: 재빌드, 재배포, 재시작

  • 패치된 베이스 이미지로 컨테이너 이미지를 재빌드하고 재배포합니다. 이전 이미지를 재시작하는 것은 효과가 없습니다.
  • 취약한 라이브러리를 로드했던 장시간 실행 프로세스를 모두 재시작합니다. Node 서비스, 워커, 에이전트 세션, 그리고 시스템 라이브러리에 링크된 모든 것이 대상입니다. 상태를 가진 작업과 AI 에이전트는 대기 중인 이벤트나 승인이 유실되지 않도록 먼저 작업을 드레인하거나 인계하십시오.

4단계: 패치하는 동안 완화 조치 적용

  • 인바운드: 리버스 프록시, 로드 밸런서 또는 WAF에서 Content-Encoding에 br이 포함된 요청 본문을 디코더에 도달하기 전에 거부합니다.
  • 아웃바운드: 악의적인 서버는 어쨌든 br을 보낼 수 있으므로, Accept-Encoding에서 br을 제거하는 것만으로는 충분하지 않습니다. Content-Encoding: br로 도착한 응답도 거부하거나 디코딩을 거부하십시오 [4].
  • Python 대안: 업그레이드를 미뤄야 하는 경우 Brotli와 brotlicffi를 제거하여 클라이언트가 br을 디코딩하지 않도록 합니다. 일부 서버는 그래도 br을 보내므로 먼저 테스트하십시오.

5단계: 검증 및 악용 징후 모니터링

  • 런타임 버전은 패키지 관리자뿐 아니라 실행 중인 프로세스나 컨테이너 내부에서 확인합니다.
  • 이미지와 SBOM을 다시 스캔하여 이전 사본이 전이적으로 여전히 포함되고 있지 않은지 확인합니다.
  • br을 처리하는 서비스에서 원인 불명의 크래시, 세그멘테이션 폴트, 재시작 루프가 있었는지 최근 로그를 검토합니다. 단일 출처에서 크래시가 집중적으로 발생했다면 공격 시도로 보고 조사할 가치가 있습니다.
  • 노출도가 높은 서비스가 패치 전에 예기치 않게 크래시된 적이 있다면, 메모리 유출 가능성을 배제할 수 없으므로 해당 프로세스가 보유했던 시크릿의 교체를 검토하십시오.

요약

상황위험도조치
공개 API가 br 요청 본문을 디코딩함치명적즉시 패치, 엣지에서 br 차단
백엔드가 외부 URL, 웹훅 또는 웹 페이지를 가져옴높음즉시 패치, br 응답 거부
런타임이 내장된 Electron 앱, AI 에이전트 또는 CLI높음업데이트 후 실행 중인 모든 인스턴스 재시작
오래된 컨테이너 이미지와 CI 아티팩트높음패치된 베이스로 재빌드 후 재배포
업로드, 폰트 또는 curl --compressed에 사용되는 시스템 라이브러리중간–높음OS 업데이트 적용, 해당 라이브러리를 로드하는 서비스 재시작
신뢰할 수 있는 내부 데이터만 처리낮음–중간다음 주기에 패치

패키지를 패치하는 것은 작업의 절반에 불과합니다. 실행 중인 프로세스, 이미지, 내장 런타임 중 어느 것도 이전 코드로 신뢰할 수 없는 Brotli를 디코딩할 수 없게 되었을 때 비로소 노출이 해소됩니다.

참고 자료