Node2Shell: 압축 데이터 하나로 서버 장악까지 (CVE-2026-111497)

Node2Shell: 압축 데이터 하나로 서버 장악까지 (CVE-2026-111497)

개요

2026년 10월에 공개된 CVE-2026-111497은 Node.js에 포함된 Brotli 디코더에서 발생하는 범위 밖 쓰기(out-of-bounds write) 취약점이다. Brotli는 구글에서 개발한 압축 알고리즘으로, 주로 웹 전송 데이터를 압축하거나 전송된 압축 데이터를 해제하는 데 사용된다. 웹 애플리케이션이 Brotli로 압축된 데이터를 전달받으면 Brotli 디코더가 이를 압축 해제한다. 그러나 공격자가 비정상적으로 구성한 압축 데이터를 전달할 경우, 취약한 Brotli 디코더는 이를 오류로 판단하지 못한 채 계속 데이터를 처리하려는 과정에서 취약점이 발생한다. 공격자는 해당 취약점을 악용하여 Node.js 프로세스를 종료시키거나 메모리 정보를 노출할 수 있으며, 나아가 원격 코드 실행을 유발할 수 있다.

이처럼 서비스 장애뿐만 아니라 메모리 정보 노출과 원격 코드 실행으로 이어질 수 있다는 점에서, Node.js 프로젝트는 이 취약점의 심각도를 High로 평가했다. 또한 Google이 등록한 Brotli 라이브러리의 CVE 레코드에는 CVSS v4.0 기준 9.2점이 기재되어 있으며, Imperva Red Team은 이 취약점을 ‘Node2Shell’로 명명했다. 본 글에서는 Brotli의 블록 전환 방식과 디코더의 오류 처리 과정을 분석하고, 취약한 디코더가 Node.js 애플리케이션에서 호출되는 경로와 대응 방안을 살펴본다.

 

기술 배경

Brotli 압축 기술

웹에서 데이터를 주고받을 때는 전송할 양을 줄이기 위해 원래 내용을 압축해서 보내기도 한다. Brotli는 이때 사용할 수 있는 압축 방식 중 하나로, 데이터를 받는 쪽에서는 Brotli로 압축된 데이터를 다시 원래 내용으로 풀어야 사용할 수 있다. 이때 압축 해제 작업을 맡는 것이 Brotli 디코더다.

디코더는 압축 데이터를 읽으면서 원래 내용을 복원하는데, 이때 데이터를 블록이라는 단위로 차례대로 처리한다. 압축 데이터에는 현재 블록에서 처리할 분량과 앞으로 풀어야 할 내용에 관한 지시가 들어 있으므로, 디코더는 현재 블록의 분량을 다 처리했더라도 풀 내용이 남았다면 다음 블록으로 넘어가 작업을 이어 간다.

 

CVE-2026-111497

취약점 발생 원인

문제는 공격자가 앞뒤가 맞지 않게 만든 압축 데이터를 디코더에 전달했을 때 시작된다. 압축 데이터에 적힌 지시를 보면 아직 풀어야 할 내용이 남아 있지만, 디코더가 지금 처리 중인 블록에서 풀 수 있는 분량은 이미 끝난 상태다. 그래서 디코더는 남은 내용을 풀기 위해 다음 블록으로 넘어가려고 한다.

[그림 1] 오류를 입력 부족으로 오인했을 때 범위 밖 쓰기로 이어지는 과정

그런데 막상 다음으로 넘어가려 하니 전환할 블록의 정보가 없다. 쉽게 말해 더 풀어야 한다고 해 놓고, 이어서 처리할 블록은 없는 상태인 것이다. 압축 데이터의 내용이 앞뒤가 맞지 않으므로 디코더는 여기서 오류로 판단하고 멈춰야 하며, 데이터를 더 받는다고 해결되는 상황도 아니다.

하지만 취약한 디코더는 어떤 경우에 이 오류를 “압축 데이터가 더 들어오면 되겠구나”라고 잘못 받아들인다. 새로 들어온 데이터를 작은 내부 저장 공간에 담아 같은 작업을 다시 시도하지만, 애초에 데이터가 부족해서 생긴 문제가 아니므로 아무리 더 받아도 전환할 수 없는 상태는 바뀌지 않는다. 이 과정이 반복되면 결국 내부 저장 공간의 범위를 넘어 데이터가 기록될 수 있는데, 이를 범위 밖 쓰기라고 한다.

또 다른 경우에는 블록 전환에 실패한 사실을 확인하지 않고 압축 해제 작업을 이어 가는 문제도 있었다. 다만 앞에서 설명한 내부 저장 공간 초과는 오류를 데이터 부족으로 오인한 경우에 해당한다. 수정된 디코더는 이처럼 전환할 수 없는 상태와 실제로 데이터가 부족한 상태를 구분해, 잘못된 압축 데이터라면 즉시 해제를 중단한다.

취약점으로 발생할 수 있는 영향

범위 밖 쓰기(out-of-bounds write) 취약점은 프로그램이 정해진 저장 공간을 벗어나 다른 메모리 영역에 데이터를 기록하는 문제다. 이렇게 Node.js 프로세스의 메모리가 손상되면 서비스가 중단되거나 메모리 정보가 노출될 수 있으며, 조건에 따라서는 공격자가 서버에서 코드를 실행하는 원격 코드 실행으로도 이어질 수 있다.

물론 조작된 압축 데이터를 보냈다고 모든 환경에서 곧바로 원격 코드 실행이 일어나는 것은 아니다. 공개된 재현에서는 특정 Node.js 빌드와 애플리케이션 구성에서 코드 실행이 확인됐고, 수정 버전에서는 같은 입력이 거부됐다.

 

공격 흐름 분석

그렇다면 실제 공격은 어떤 순서로 진행될까? 공격자는 먼저 취약한 버전의 Node.js를 사용하는 서비스를 찾는다. 하지만 버전만 취약하다고 공격이 가능한 것은 아니다. 그 서비스가 외부에서 받은 Brotli 압축 데이터를 실제로 압축 해제하는 과정이 있어야 하고, 공격자가 조작한 데이터를 그 압축 해제 과정까지 전달할 수 있어야 한다.

공격자가 조작한 Brotli 데이터를 전달할 수 있는 지점은 서비스의 기능에 따라 크게 세 가지로 나눠 볼 수 있다.

  • 클라이언트가 보내는 HTTP POST 요청 본문에 포함되는 경우
  • Node.js 애플리케이션이 외부 서버로부터 받은 응답에 포함되는 경우
  • Node.js 애플리케이션이 Brotli 데이터나 파일을 직접 처리하는 경우

대표적인 유입 경로는 클라이언트가 서버로 보내는 HTTP 요청이다. 요청 본문이 Brotli 방식으로 압축되어 있으면, 이를 지원하는 애플리케이션은 본문의 내용을 사용하기 전에 압축을 해제한다. 따라서 공격자가 조작한 압축 데이터를 요청 본문에 포함해 전송할 경우, 해당 데이터가 Brotli 디코더에 전달될 수 있다.

Node.js 애플리케이션이 외부 서버에서 받은 응답을 처리하는 과정에서도 조작된 Brotli 데이터가 유입될 수 있다. 공격자가 외부 서버의 응답을 제어하거나, 애플리케이션이 공격자가 지정한 주소에서 데이터를 가져오도록 유도하면 해당 응답에 조작된 데이터를 담을 수 있다.

조작된 데이터가 반드시 웹 요청으로 직접 전달되어야 하는 것은 아니다. 애플리케이션이 압축 파일이나 다른 시스템에서 전달된 메시지를 직접 압축 해제하는 경우에도 같은 문제가 발생할 수 있다. 파일은 웹 요청을 통해 업로드될 수 있으며, 메시지는 별도의 시스템이나 메시지 큐를 통해 전달될 수 있다. 유입 경로와 관계없이 애플리케이션이 해당 데이터를 실제로 압축 해제하면 취약한 Brotli 디코더에 도달한다.

취약한 디코더가 조작된 압축 데이터를 처리하면, 존재하지 않는 다음 블록으로 전환하려는 잘못된 동작이 발생한다. 디코더는 이를 형식 오류로 판단해 작업을 중단하지 않고, 입력이 더 필요한 상황으로 잘못 판단한다. 그 결과 정해진 저장 공간보다 많은 데이터가 기록되면서 Node.js 프로세스의 메모리가 손상된다. 아래 그림은 공격자가 조작된 Brotli 데이터를 전달한 시점부터 메모리 손상과 원격 코드 실행에 이르는 전체 흐름을 보여준다.

[그림 2] 공격자 제어 Brotli 입력부터 디코더 메모리 손상까지의 흐름

메모리가 손상되면 서비스가 중단되거나 메모리에 저장된 정보가 노출될 수 있다. 또한 메모리 구조와 실행 환경 등 여러 조건이 충족되면 공격자가 의도한 코드를 서버에서 실행하는 원격 코드 실행으로 이어질 수도 있다. 아래 두 영상은 취약한 Node.js 환경에서 DoS 공격과 RCE 공격을 실제로 재현한 결과를 보여준다. 공격 성공에 필요한 조건은 실행 환경마다 다르므로, 실제 공격 결과도 상황에 따라 달라질 수 있다.

[영상 1] Node2Shell을 이용한 DoS 시연 영상

[영상 2] Node2Shell을 이용한 RCE 시연 영상

 

대응 방안

수정된 버전으로 패치 적용

가장 확실한 대응 방법은 서비스에서 실제로 사용 중인 Node.js를 취약점이 수정된 버전 이상으로 업데이트하는 것이다. Node.js 프로젝트가 2026년 10월 21일에 발표한 계열별 첫 수정 버전은 아래와 같다.

Node.js 계열

취약 버전

수정 버전

22.x

22.23.4 미만

22.23.4 이상

24.x

24.22.1 미만

24.22.1 이상

26.x

26.12.1 미만

26.12.1 이상

[표 1] Node.js 프로젝트가 발표한 계열별 수정 버전

업데이트할 때는 서버에 설치된 Node.js 버전만 확인해서는 안 된다. 실제 서비스가 사용하는 실행 파일이 컨테이너 이미지나 배포 파일에 별도로 포함돼 있을 수 있으므로, 해당 파일까지 교체한 뒤 관련 프로세스를 다시 시작해야 한다. 이후 실행 중인 서비스에 수정 버전이 적용됐는지 확인하고, Node.js 외의 제품이 Brotli 라이브러리를 별도로 사용한다면 그 제품의 패치 적용 여부도 따로 점검해야 한다.

패치 버전을 적용할 수 없는 경우

여러 서버를 차례로 업데이트해야 하거나 서비스에 문제가 없는지 먼저 확인해야 하는 경우에는 수정 버전으로 즉시 업데이트하기 어려울 수 있다. 제품에 Node.js가 포함되어 있어 제조사의 업데이트를 기다려야 하는 경우도 있다. 이때는 먼저 서비스에서 외부의 Brotli 데이터가 들어와 압축 해제되는 경로를 확인하고, 업데이트가 완료될 때까지 해당 경로를 일시적으로 제한해야 한다.

예를 들어 웹 요청 중에서 Brotli 압축 데이터를 처리하는 기능이 있다면, 업데이트 전까지 해당 요청을 제한할 수 있다. 프록시나 웹 방화벽을 이용해 Brotli 압축 요청을 차단하는 방법도 있다. 외부 서버에서 Brotli로 압축된 응답을 가져오는 기능은 허용된 서버에서만 응답을 받도록 제한하거나, 필요한 경우 해당 기능을 잠시 중단할 수 있다.

이러한 제한은 공격 가능성을 낮추기 위한 임시 조치이며, 정상적인 웹 요청이나 외부 연동 기능에도 영향을 줄 수 있다. 따라서 서비스에 필요한 기능인지 확인한 후 적용하고, 업데이트가 완료될 때까지만 유지하는 것이 적절하다.

 

결론

Node2Shell은 일반적인 압축 데이터 처리 과정에서도 런타임 내부의 메모리 문제가 공격에 악용될 수 있음을 보여준다. 애플리케이션이 Brotli 압축 해제 기능을 직접 사용하지 않더라도, 웹 요청의 본문이나 외부 서버에서 받은 응답을 처리하는 과정에서 Brotli 디코더가 호출될 수 있다. 따라서 서버에 설치된 Node.js 버전이나 외부에 공개된 주소만 확인해서는 실제 공격 경로를 놓칠 수 있으므로 추가적인 주의가 필요하다.

대응을 위해서는 현재 운영 중인 Node.js 프로세스가 실제로 사용하는 실행 파일과 버전을 확인하고, 취약점이 수정된 버전으로 우선 업데이트해야 한다. 이와 함께 웹 요청 본문, 외부 서버의 응답, 파일 및 메시지 큐 등을 통해 들어오는 Brotli 압축 데이터 중 공격자가 조작할 수 있는 데이터가 서비스 내부에서 어떻게 처리되는지 확인해야 한다. 즉시 업데이트하기 어려운 환경에서는 불필요한 Brotli 압축 해제 기능을 제한하고, 업데이트 이후에는 실제 서비스에서 비정상적으로 조작된 압축 데이터가 거부되는지 점검하는 것이 중요하다.

이번 사례는 애플리케이션에 직접 구현된 기능뿐만 아니라, 프레임워크나 런타임이 내부적으로 수행하는 처리 과정도 공격에 노출될 수 있음을 보여준다. 따라서 유사한 취약점에 대응할 때는 사용 중인 구성 요소의 버전만 확인하기보다, 해당 구성 요소가 서비스의 어떤 기능에서 호출되는지까지 파악할 필요가 있다.

 

출처

  1. https://nodejs.org/en/blog/vulnerability/october-2026-security-releases
  2. https://nodejs.org/en/blog/release/v22.23.4
  3. https://nodejs.org/en/blog/release/v24.22.1
  4. https://nodejs.org/en/blog/release/v26.12.1
  5. https://github.com/CVEProject/cvelistV5/blob/main/cves/2026/111xxx/CVE-2026-111497.json
  6. https://github.com/google/brotli/commit/0ed4268a4401abcb44720963
  7. https://github.com/nodejs/node/commit/49dff6f57a
  8. https://www.rfc-editor.org/rfc/rfc7932
  9. https://expressjs.com/en/guide/migrating-5/
  10. https://expressjs.com/en/resources/middleware/body-parser/
  11. https://www.imperva.com/blog/node2shell-from-brotli-data-to-remote-code-execution/

AhnLab TIP를 구독하시면 연관 IOC 및 상세 분석 정보를 추가적으로 확인하실 수 있습니다. 자세한 내용은 아래 배너를 클릭하여 확인해보세요.