
Node.js 프로젝트가 런타임에 내장된 Brotli 압축 해제(디코더) 기능에서 발견된 원격 코드 실행(RCE) 취약점을 긴급 패치했습니다.
'Node2Shell'로도 불리는 이번 취약점(CVE-2026-111497)은 최근 공개된 PHP 런타임 취약점 'PHP2Shell'과 마찬가지로 애플리케이션 코드가 아닌 런타임 자체에 존재합니다. Brotli 디코더가 Node.js 런타임에 기본 내장되어 있기 때문에, 애플리케이션 코드에 문제가 없더라도 취약한 버전의 Node.js가 공격자가 조작한 Brotli 데이터를 압축 해제하는 순간 공격에 노출될 수 있습니다.
특히 이번 취약점은 외부에서 들어오는 요청(인바운드)뿐 아니라, 서버가 외부 API나 웹훅을 호출하고 응답을 받는 아웃바운드 통신 경로로도 공격이 가능해 기존 경계 보안 장비만으로는 막기 어렵다는 점에서 주의가 필요합니다.
패치 공개 하루 만인 10월 22일 공개 PoC(개념증명 코드)가 등장했고, 같은 날 보안 연구원들이 패치되지 않은 공식 Node.js 24.22.0 리눅스 arm64 이미지에서 원격 코드 실행에 성공했다고 보고한 만큼, 사용자분들의 신속한 업데이트를 권고 드립니다.
취약점 개요
| CVE | CVE-2026-111497 (별칭: Node2Shell) |
| 취약점 유형 | Brotli 디코더 Out-of-Bounds Write CWE-252(Unchecked Return Value), CWE-787(Out-of-bounds Write) |
| 심각도 | Critical (CVSS v4.0 9.2) - Google Brotli 보안 권고 기준 Node.js 보안 릴리스 자체 분류는 High |
| 영향 대상 | Node.js 22.x / 24.x / 26.x 전 버전(패치 이전), EOL 버전 전체, Bun 1.4.4 미만, Brotli 라이브러리 1.0.3 ~ 1.2.0 |
| PoC 공개 | 공개됨 (2026.10.22) |
| 실제 악용 | 10월 22일 기준 확인된 실제 악용 사례 없음 |
| 조치 | Node.js 22.23.4 / 24.22.1 / 26.12.1 이상으로 즉시 업데이트 |
■ 사용자 권고 사항
node:24.22.0)에 고정되어 있다면 태그를 변경한 뒤 재빌드·재배포합니다.Content-Encoding: br 요청을 차단합니다. (단, 아웃바운드 공격은 막지 못합니다)
취약점 발생 원인
Brotli는 Google이 개발한 압축 알고리즘으로, 웹 서버와 브라우저 간 HTTP 응답 압축(Content-Encoding: br)에 널리 사용됩니다. Node.js는 별도 설치 없이 Brotli 압축 및 압축 해제를 지원하기 위해 Google Brotli 라이브러리를 런타임 내부(deps/brotli)에 번들로 포함하고 있습니다.
취약점은 Brotli 디코더(c/dec/decode.c)의 블록 스위치(Block Switch) 처리 과정에서 발생합니다. 블록 타입과 길이를 해석하는 DecodeBlockTypeAndLength 함수는 실패 시 BROTLI_FALSE를 반환하는데, 이 값이 ① "스트림이 잘못됨(블록 타입이 하나뿐인데 블록 스위치를 시도)"과 ② "입력 데이터가 더 필요함"이라는 서로 다른 두 의미를 함께 나타냈습니다.
호출하는 쪽에서는 이 반환값을 "데이터를 더 받으면 되는" 복구 가능한 상황으로만 처리했고, 리터럴 블록 스위치를 처리하는 DecodeLiteralBlockSwitch는 반환값을 아예 확인하지 않았습니다. 그 결과 잘못된 블록 스위치가 입력되어도 디코딩이 중단되지 않고, 디코더가 비정상 상태로 계속 동작하면서 버퍼 경계를 넘어 인접한 디코더 상태 메모리를 덮어쓰게 됩니다.
| 취약 코드 위치 | Google Brotli 디코더 c/dec/decode.c (DecodeBlockTypeAndLength, DecodeLiteralBlockSwitch) |
| 근본 원인 | 하나의 반환값(BROTLI_FALSE)에 '오류'와 '입력 부족' 두 의미가 겹쳐 있어, 잘못된 스트림을 오류로 처리하지 못함 |
| 결과 | 디코더가 비정상 상태로 계속 진행 → 버퍼 범위를 벗어난 쓰기로 인접한 디코더 상태 메모리 손상 |
| 수정 내용 | Brotli 업스트림 커밋(0ed4268a, 2026년 2월)을 Node.js에 반영. 반환값을 '형식 오류(BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH)'와 '입력 부족(BROTLI_DECODER_NEEDS_MORE_INPUT)'으로 분리해, 잘못된 블록 스위치가 입력되면 즉시 디코딩을 중단 |
| 제보자 | OpenAI Codex Security (Khai Tran, Sylvie Mayer) |
※ Brotli 업스트림에는 2월에 이미 수정이 반영되었으나, Node.js에 번들된 Brotli 코드에는 10월 21일 보안 릴리스에서야 반영되었습니다. 시스템에 설치된 Brotli 라이브러리만 업데이트해서는 Node.js에 내장된 Brotli 코드는 갱신되지 않으므로, 반드시 Node.js 런타임 자체를 업데이트해야 합니다.
영향 및 공격 시나리오
| 공격 조건 | 인증 불필요. 공격자가 조작한 Brotli 데이터를 Node.js가 압축 해제하도록 전달할 수 있으면 공격 가능 |
| 발생 가능한 피해 | Node.js 프로세스 메모리 정보 유출, 프로세스 강제 종료(DoS), Node.js 프로세스 권한으로 임의 코드 실행 |
| 노출되지 않는 환경 | Brotli 데이터를 압축 해제하는 경로(아래 ①~③)가 없는 애플리케이션. 예: Node.js 기본 http 모듈만 사용하고 외부 fetch()나 node:zlib Brotli 함수를 쓰지 않는 서버 |
Node.js 애플리케이션이 공격자가 조작한 Brotli 데이터를 압축 해제하게 되는 경로는 크게 세 가지입니다.
| 경로 | 노출 조건 | 위험도 | 프록시 차단 효과 |
| ① 인바운드 요청 | Express 5, body-parser 2.x 등이 Content-Encoding: br 요청 본문을 자동으로 압축 해제(Node.js 기본 http 모듈만 사용하는 서버는 요청 본문을 자동으로 압축 해제하지 않음) |
높음 | 효과 있음 |
| ② 아웃바운드 응답 | 내장 fetch()가 외부 서버의 Brotli 응답을 자동으로 압축 해제. 웹훅 전송, 사용자 입력 URL 호출(링크 미리보기, 이미지 가져오기 등), 외부 API 연동 기능이 대상 |
높음 | 효과 없음 |
| ③ 직접 호출 | 코드나 라이브러리가 node:zlib의 brotliDecompress(), brotliDecompressSync(), createBrotliDecompress()에 신뢰할 수 없는 데이터를 전달 |
중간 ~ 높음 | 경우에 따라 다름 |
특히 ② 아웃바운드 경로는 공격자가 자신이 통제하는 서버 주소를 웹훅이나 URL 입력란에 등록해 두기만 하면, 서버가 해당 주소로 요청을 보내고 응답을 받는 순간 공격이 성립합니다. 요청이 내부에서 외부로 나가는 정상 통신처럼 보이기 때문에 WAF나 리버스 프록시 같은 인바운드 중심의 경계 보안으로는 막을 수 없습니다.
영향 받는 버전 및 패치 버전
| 제품 / 브랜치 | 영향 받는 버전 | 패치 버전 |
| Node.js 26.x (Current) | 26.12.1 미만 | 26.12.1 이상 |
| Node.js 24.x (LTS) | 24.22.1 미만 | 24.22.1 이상 |
| Node.js 22.x (LTS) | 22.23.4 미만 | 22.23.4 이상 |
| Node.js 25.x, 23.x, 20.x 이하 (EOL) | 전 버전 | 패치 미제공 → 지원 중인 LTS로 업그레이드 |
| Bun | 1.4.4 미만 | 1.4.4 이상 |
| Google Brotli 라이브러리 | 1.0.3 ~ 1.2.0 | 1.2.1 / 1.1.1 / 1.0.10 |
[표 1] CVE-2026-111497 영향 받는 버전 및 패치 버전
■ Node.js 10월 보안 업데이트 전체 내역
이번 Node.js 10월 보안 릴리스에서는 CVE-2026-111497 외에도 TLS 인증서 검증 우회 등 High 등급 3건을 포함해 총 10건의 취약점이 함께 수정되었습니다. 업데이트 시 아래 취약점도 함께 해결됩니다.
| CVE | 내용 | 심각도 | 영향 브랜치 |
| CVE-2026-111497 | Brotli 디코더 Out-of-Bounds Write로 인한 원격 코드 실행 | High | 26/24/22 |
| CVE-2026-105482 | dns.resolve가 NULL 문자가 포함된 도메인명을 잘라서 처리 |
High | 26/24/22 |
| CVE-2026-105483 | 전각 숫자로 표기된 IP에서 TLS 인증서 호스트명 검증 우회 | High | 26/24 |
| CVE-2026-106069 | 호스트명이 IP 주소로 잘못 해석될 때 TLS 인증서 호스트명 검증 우회 | High | 26/24/22 |
| CVE-2026-105481 | Keep-Alive 연결에서 요청 전송 전에 수신된 응답을 정상 응답으로 처리 | Medium | 26/24/22 |
| CVE-2026-106072 | 비표준 형식 IPv4 호스트명 처리 시 DNS SAN 인증서로 IP 엔드포인트 인증 가능 | Medium | 26/24/22 |
| CVE-2026-106074 | crypto.randomUUID(), randomInt() 엔트로피 캐시가 스냅샷/SEA(단일 실행 파일)에 포함되어 모든 프로세스에서 동일한 값 생성 |
Medium | 26/24/22 |
| CVE-2026-106070 | ZipFile이 헤더 검증 실패 후 공격자가 조작한 데이터를 반환 | Low | 26 |
| CVE-2026-106071 | Permission Model 와일드카드 처리 오류로 파일 시스템 접근이 과도하게 허용 | Low | 26 |
| CVE-2026-106073 | fs.realpathSync를 통한 Permission Model 우회 |
Low | 26/24/22 |
[표 2] Node.js 2026년 10월 보안 릴리스 수정 취약점 목록
※ 이 밖에 c-ares, OpenSSL, SQLite, Undici 의존성 업데이트와 eMudhra 중간 CA 개인 키 유출에 따른 루트 인증서 목록 갱신(NSS 3.131.1 기준, emSign 루트 인증서 3종 제거)도 함께 이루어졌습니다.
조치 방법
① 사용 중인 버전 확인
• Node.js 버전 확인 (서버, CI 러너, 개발자 PC)
node --version
• 컨테이너 이미지 내 Node.js 버전 확인
docker run --rm <이미지명:태그> node --version
• Brotli 요청 본문을 자동으로 압축 해제하는 프레임워크 사용 여부 확인
npm ls express body-parser
• 소스 코드 및 의존성에서 Brotli 압축 해제 함수 직접 호출 여부 검색
grep -rnE "brotliDecompress|createBrotliDecompress" --include=*.js --include=*.mjs --include=*.cjs --include=*.ts .
• 리눅스 배포판 Brotli 패키지 버전 확인
# Debian / Ubuntu
dpkg -l | grep -i brotli
# RHEL / Fedora / Rocky
rpm -qa | grep -i brotli
# Alpine
apk info -v | grep -i brotli
버전 확인과 함께, 외부 URL로 fetch()를 호출하는 기능(웹훅, 링크 미리보기, 외부 API 연동, 사용자 입력 URL 처리 등)을 목록화해 우선 점검 대상으로 관리하시기 바랍니다. 자산이 많은 경우 SBOM(소프트웨어 자재명세서)을 활용하면 Node.js 런타임과 의존성 버전을 빠르게 파악할 수 있습니다.
② 보안 업데이트 적용
| 설치 환경 | 업데이트 방법 | 조치 필요 여부 |
| 공식 설치 파일 | Node.js 공식 다운로드 페이지에서 패치 버전 설치 파일을 받아 재설치 | 필요 |
| nvm / fnm 등 버전 관리 도구 |
nvm install 24.22.1 후 nvm alias default 24.22.1 (사용 중인 브랜치에 맞는 패치 버전 지정) |
필요 |
| Docker 컨테이너 | Dockerfile의 베이스 이미지 태그를 패치 버전으로 변경(예: FROM node:24.22.1, FROM node:22.23.4-alpine) → 이미지 재빌드 → 재배포 |
필요 재빌드만으로는 해결 안 됨, 태그 변경 필수 |
| 리눅스 패키지 | 배포판 보안 업데이트 적용 (apt upgrade, dnf upgrade, apk upgrade) 후 Node.js 및 Brotli 패키지 모두 갱신 여부 확인 |
필요 배포판 패치 제공 여부 확인 |
| Bun | bun upgrade로 1.4.4 이상 업데이트 |
필요 |
| 서버리스 / PaaS | 클라우드 서비스 제공업체(CSP)의 런타임 버전 공지를 확인하고, 함수·앱을 재배포해 패치된 런타임이 적용되었는지 확인 | CSP 공지 확인 재배포가 필요할 수 있음 |
업데이트 후에는 실행 중인 Node.js 프로세스(PM2, systemd 서비스 등)를 반드시 재시작해야 새 런타임이 적용됩니다.
③ 즉시 업데이트가 어려운 경우 임시 대응
※ 아래 조치는 업데이트 전까지의 임시 조치이며, 아웃바운드 fetch() 경로의 공격은 막을 수 없습니다. 반드시 런타임 업데이트를 병행하시기 바랍니다.
• 리버스 프록시(Nginx)에서 Brotli 인코딩 요청 차단
if ($http_content_encoding ~* "br") {
return 415;
}
• 애플리케이션 미들웨어(Express)에서 차단
app.use((req, res, next) => {
if (/\bbr\b/i.test(req.headers['content-encoding'] || '')) {
return res.status(415).end();
}
next();
});
※ 미들웨어는 body-parser 등 본문 파싱 미들웨어보다 앞에 등록해야 합니다.
• 기타 조치
- 클라우드 WAF / 로드밸런서 엣지에서 Content-Encoding: br 인바운드 요청 차단 규칙 적용
- 웹훅·URL 호출 기능의 대상 도메인을 허용 목록(Allowlist)으로 제한
- 외부 호출 시 Accept-Encoding 헤더에서 br을 제외해 Brotli 압축 응답 수신 최소화 (서버가 무시할 수 있으므로 완전한 차단 수단은 아님)
- Node.js 프로세스를 최소 권한 계정 또는 격리된 컨테이너에서 실행해 침해 시 피해 범위 최소화
침해 여부 확인 및 조치 방안
| 점검 항목 | 확인 내용 |
| 웹 서버 액세스 로그 | Content-Encoding: br 헤더가 포함된 비정상적인 요청, 동일 IP에서의 반복 요청 |
| 프로세스 이상 종료 | Node.js 프로세스의 원인 불명 크래시(SIGSEGV 등), 잦은 재시작 기록 |
| 자식 프로세스 | node 프로세스가 생성한 sh, bash, curl, wget 등 비정상 하위 프로세스 |
| 네트워크 | Node.js 서버에서 알 수 없는 외부 IP로의 신규 연결, 리버스 셸 의심 통신 |
| 웹훅 / URL 등록 내역 | 최근 등록·변경된 웹훅 주소, 사용자 입력 URL 중 의심 도메인 |
침해가 의심되는 경우 해당 서버를 네트워크에서 격리하고, 프로세스 메모리에 노출되었을 수 있는 API 키, DB 접속 정보, 세션 토큰, 환경 변수에 저장된 시크릿(Secret) 등을 모두 교체하시기 바랍니다. 이후 패치된 런타임으로 재구축한 뒤 재배포하는 것을 권고 드립니다.
취약점 공개 경과
| 일자 | 내용 |
| 2026. 02. 27. | Google Brotli 업스트림에 수정 커밋(0ed4268a) 반영 |
| 2026. 10. 21. | Node.js 22.23.4 / 24.22.1 / 26.12.1 보안 릴리스, Brotli 1.2.1 / 1.1.1 / 1.0.10 배포 |
| 2026. 10. 22. | NVD 등록, Google Brotli 보안 권고(GHSA-9v8h-qqgm-4m35) 공개, CVSS v4.0 9.2(Critical) 부여 |
| 2026. 10. 22. | 공개 PoC 등장 (초기에는 보안 기능을 약화한 테스트 빌드에서만 동작) |
| 2026. 10. 22. | 보안 연구원들이 패치되지 않은 공식 Node.js 24.22.0 리눅스 arm64 이미지에서 원격 코드 실행 성공 보고 (24.22.1은 동일 입력 거부) |
※ 일자는 각 출처의 공개 기준(현지 시각)입니다.
참고 용어
| 용어 | 설명 |
| Node.js | 서버에서 자바스크립트를 실행하는 런타임. 웹 서버, API 서버, 각종 개발 도구에 널리 사용 |
| Brotli | Google이 개발한 무손실 압축 알고리즘. HTTP 응답 압축(Content-Encoding: br)에 주로 사용 |
| Out-of-Bounds Write | 할당된 메모리 영역의 경계를 넘어 데이터를 쓰는 오류. 메모리 손상을 일으켜 코드 실행으로 이어질 수 있음 |
| CWE-252 / CWE-787 | CWE-252(Unchecked Return Value): 함수의 반환값을 확인하지 않아 발생하는 취약점 CWE-787(Out-of-bounds Write): 할당된 메모리 범위를 벗어나 데이터를 쓰는 취약점 |
| RCE | Remote Code Execution. 원격에서 대상 시스템에 임의 코드를 실행하는 공격 |
| PoC | Proof of Concept. 취약점이 실제로 악용 가능함을 보여주는 개념증명 코드 |
| LTS / EOL | LTS(Long Term Support): 장기 지원 버전 / EOL(End of Life): 지원이 종료되어 보안 패치가 제공되지 않는 버전 |
| 웹훅(Webhook) | 특정 이벤트 발생 시 미리 등록된 외부 URL로 HTTP 요청을 보내는 기능 |
| SBOM | Software Bill of Materials. 소프트웨어를 구성하는 컴포넌트와 버전 목록 |
참고 :
https://nodejs.org/en/blog/vulnerability/october-2026-security-releases
| GitLab 정규 표현식 컴파일러 원격 코드 실행 취약점(CVE-2026-110264) 긴급 업데이트 권고 (0) | 2026.10.22 |
|---|---|
| PHP 원격 코드 실행 취약점 2종(CVE-2026-109618, CVE-2026-109619) 긴급 업데이트 권고 (0) | 2026.10.22 |
| 크라임웨어 유통 창구가 된 PyPI, 카딩(Carding) 자동화 도구 'saeka' 사례 (0) | 2026.10.19 |
| GitLab CI/CD 원격 코드 실행 취약점(CVE-2026-97229) 긴급 업데이트 권고 (0) | 2026.10.13 |
| 실제 악용 중인 리눅스 커널 취약점 3종(CVE-2025-39682, CVE-2026-53266, CVE-2025-39964) 긴급 업데이트 권고 (0) | 2026.09.21 |
댓글 영역