Reddit

PYTHON Packages NOW Vulnerable to Node2Shell

Node2Shell 취약점, Python의 Brotli·brotlicffi 패키지에도 영향

Node2Shell(CVE-2026-111497)은 Node.js만의 문제가 아니라 공유 Brotli C 디코더의 취약점입니다. PyPI의 Brotli와 brotlicffi도 영향을 받으며, 글은 패치 버전과 의존 환경의 확인·업데이트 방법을 제시합니다.

에디터 노트

패키지 취약점 자체의 CVE가 없다는 표현과 취약점에 CVE가 없다는 표현은 구분해야 합니다. 본문은 CVE-2026-111497이 이미 Google Brotli를 대상으로 등록됐지만, 해당 기록과 당시 자문 데이터베이스가 PyPI의 Brotli 및 brotlicffi를 별도 패키지로 연결하지 않았다고 설명합니다. Reddit 댓글의 지적도 이 차이를 짚습니다. 따라서 탐지 공백은 CVE의 부재가 아니라 패키지 생태계와 자문 데이터베이스의 매핑 문제입니다. Brotli 1.2.0과 1.2.1을 대상으로 한 재현 결과는 잘못된 스트림 처리 차이를 구체적으로 보여줍니다. 반면 brotlicffi는 같은 소스를 포함한다는 근거와 업데이트 시점에 의존하며, 글도 배포본을 직접 비교하거나 퍼징하지 않았다고 밝혔습니다. 또 Python 패키지에서 메모리 손상이나 코드 실행을 확인한 것은 아니므로, 본문이 입증한 범위는 디코더의 취약 동작과 수정 버전까지입니다. 업데이트 권고는 명확하지만 패키지별 검증 강도는 다릅니다.

AI 요약

Node2Shell(CVE-2026-111497)은 Node.js의 취약점으로 알려졌지만, 원인은 Google Brotli의 C 디코더입니다. OpenSourceMalware는 같은 디코더를 포함한 PyPI 패키지 Brotli와 brotlicffi도 취약하다고 분석하고, 영향을 받는 버전과 대응 방법을 정리합니다.

취약점의 원인

Brotli 디코더의 DecodeBlockTypeAndLength()는 스트림이 유효하지 않은 경우와 입력이 더 필요한 경우를 모두 같은 불리언 값으로 반환합니다. 안전 경로에서는 이 값을 처리하지만, 빠른 디코딩 경로는 반환값을 버립니다. 블록 유형이 하나뿐인 스트림에서 블록 길이가 소진되면 유효하지 않은 블록 전환을 요청하게 되고, 디코더가 오류를 멈추지 않은 채 잘못된 상태로 계속 처리하면서 링 버퍼 밖에 쓰는 문제가 생깁니다. 취약점 기록은 이를 반환값 미확인(CWE-252)과 범위 밖 쓰기(CWE-787)로 설명합니다.

상류 수정은 함수가 BrotliDecoderErrorCode를 반환하도록 바꾸고, 잘못된 블록 전환을 나타내는 BROTLI_DECODER_ERROR_FORMAT_BLOCK_SWITCH 오류를 추가했습니다. 블록 전환이 일어나는 세 지점도 안전 경로와 빠른 경로 모두 형식 오류를 처리하도록 고쳤습니다. 글은 CVSS 4.0 점수 9.2를 제시합니다.

Python 패키지 영향과 검증

PyPI의 Brotli는 Google의 CPython 확장으로, 소스 배포본에 C 디코더를 포함하고 이를 컴파일합니다. 글은 Brotli 1.2.0과 1.2.1의 배포본을 비교해 decode.c와 decode.h가 취약점 수정 커밋과 일치하게 달라진 점을 확인했습니다. Brotli 1.2.0 이하가 영향받고 1.2.1이 수정 버전입니다. 이전 버전도 확인한 범위에서는 같은 문제가 있었으며, 작성자는 안전한 구버전으로 되돌릴 선택지가 없다고 설명합니다.

brotlicffi도 같은 C 소스를 포함하는 별도 패키지입니다. 1.2.0.1과 1.2.0.2는 취약한 1.2.0 디코더를 포함하며, 2026년 10월 23일 공개된 1.2.1.0이 수정 버전입니다. 다만 글은 brotlicffi 배포본을 직접 비교하거나 퍼징하지 않았다고 밝힙니다.

검증 과정에서 작성자는 Brotli 1.2.0과 1.2.1을 AddressSanitizer로 빌드한 뒤, 블록 전환 오류를 유도하는 스트림을 C 하네스와 Python 모듈에 입력했습니다. 1.2.0은 약 23MB 입력을 모두 소비한 뒤 NEEDS_MORE_INPUT을 반환했고, 입력을 1, 13, 4096, 65536바이트 단위로 나눠 넣어도 같은 응답을 보였습니다. 1.2.1은 각 방식에서 오류 코드 -17, FORMAT_BLOCK_SWITCH를 반환했습니다. Python의 brotli.decompress()는 오류 코드를 구분해 보여주지 않으며, 1.2.0의 Decompressor()는 입력을 조금씩 받으면 완료되지 않는다고 설명합니다. Python 확장에서 메모리 손상이나 코드 실행까지 확인하지는 않았습니다.

노출 경로와 대응

Brotli가 설치되면 urllib3와 이를 사용하는 requests가 Brotli 응답을 자동으로 풀 수 있습니다. httpx는 brotli 추가 기능을 설치한 경우 영향을 받고, aiohttp와 Scrapy도 Brotli 설치 여부에 따라 관련될 수 있습니다. 따라서 외부 서버에서 데이터를 가져오는 서비스나 Brotli로 인코딩한 요청 본문을 푸는 서버가 공격자 제어 입력을 취약한 디코더에 전달할 수 있다고 글은 설명합니다.

작성자는 pip install -U "Brotli>=1.2.1" "brotlicffi>=1.2.1.0"으로 업데이트하고, 해당 패키지를 포함한 컨테이너 이미지와 휠도 다시 빌드하라고 권합니다. pip show로 각 환경의 설치 버전을 확인할 수 있습니다. 즉시 업데이트할 수 없다면 두 패키지를 제거하거나, 외부 요청에서 br 인코딩을 광고하지 않고 들어오는 Content-Encoding: br 요청을 거부하는 방법을 제시합니다. Brotli를 제거하면 일부 HTTP 클라이언트는 gzip이나 deflate로 대체합니다.

글이 확인한 2026년 10월 23일 기준으로 CVE 기록은 github.com/google/brotli를 대상으로 하지만 PyPI 패키지 이름은 명시하지 않았습니다. OSV와 GitHub Advisory Database에도 PyPI/Brotli 및 PyPI/brotlicffi 항목이 없었고, 작성자는 패키지 자문 데이터베이스에 기록을 제출할 예정이라고 밝혔습니다. 따라서 글은 당시 일반적인 취약점 스캐너가 두 패키지를 찾아내지 못할 수 있다고 지적합니다.

Reddit 반응

  • @One_Paint8721 — Node2Shell이 Node뿐 아니라 Python 패키지 Brotli와 brotlicffi에도 영향을 주며, 아직 이 취약점에 대한 CVE가 없다고 합니다.
  • @bi-nary — Node.js 보안 권고에 이미 포함된 내용입니다. Brotli를 집중적으로 확인했고, 취약하다는 점을 확인한 뒤 패치하기 시작했습니다. CVE-2026-111497도 분명히 존재합니다.
    • @Morlaix — 이 내용은 Python에 관한 것입니다.
    • @bi-nary — Python이나 Node뿐 아니라 Brotli를 사용하거나 포함하는 모든 소프트웨어에 관한 내용입니다.
    • @Morlaix — 고맙습니다. 이제 이해했습니다.
  • @Morlaix — 이 패키지들이 설치된 주요 소프트웨어가 있나요?

원문: OpenSourceMalware / 번역·요약: Trawling

뉴스 타래

전체 보기