요약
압축 형식 Brotli의 디코더 결함이 Node.js를 넘어 파이썬과 여러 HTTP 도구로 번졌습니다.
보안 업체 아이키도 시큐리티가 Node.js가 Node2Shell로 공개한 CVE-2026-111497의 영향 범위를 정리했습니다. 구글 Brotli 디코더의 메모리 범위 밖 쓰기 결함으로, GitHub은 CVSS 9.2(치명)로 평가했습니다.
왜 중요한가
- 코드에서 Brotli를 직접 쓰지 않아도 설치만 돼 있으면 requests, httpx, aiohttp 같은 HTTP 클라이언트가 자동으로 씁니다.
- 악성 클라이언트는 서버를, 악성 서버는 압축 응답을 받는 클라이언트를 노릴 수 있어 공격 방향이 양쪽으로 열려 있습니다.
- 10월 23일 기준 pip-audit, Dependabot, Safety가 취약한 PyPI 버전을 잡지 못해 의존성 스캐너만 믿을 수 없습니다.
핵심 내용
- 블록 전환을 처리하는 함수가 형식 오류와 입력 부족을 구분하지 못해, 조작된 스트림이 낡은 상태로 디코딩을 이어 가게 만듭니다.
- 수정판은 Brotli 1.2.1, brotlicffi 1.2.1.0, Node.js 24.22.1 등이며 Bun, Envoy, Istio도 패치를 냈습니다.
- 웹훅, URL 미리보기, 크롤러처럼 외부 서버에 요청하는 서비스와 압축된 요청 본문을 받는 서비스부터 고치라고 권했습니다.
- 패치가 늦어지면 프록시에서 Content-Encoding에 br이 붙은 요청을 막고, 외부 호출에서는 br 응답을 디코딩 전에 거부하라고 했습니다.
- 오픈AI는 2월에 이 버그를 찾았다고 밝혔지만, 당시 고친 원인은 보안 문제로 인식되지 않아 이번에야 릴리스에 실렸습니다.
HN 반응
- HTTP2Shell이라는 이름이 지나치게 넓고 선정적이라는 비판이 많았고, HTTP 없이 Brotli를 풀어도 터지니 Broccoli2Shell이 낫다는 의견도 나왔습니다.
- 요청 본문을 Brotli로 받는 서버가 실제로 드물다는 반론에, 서버가 이를 조용히 지원하는 경우가 있어 리버스 프록시에서 막았다는 경험담이 맞섰습니다.
HTTP2Shell이라는 이름을 지은 사람은 인공지능이든 진짜 지능이든 지능이라곤 하나도 안 썼나 보네요.
너무 무의미하고 범위도 넓은 데다, 실제 문제를 전혀 설명하지 못해요. 뒤에 아무것도 없는 HTTP 서버는 아마 영향이 없을 텐데, 왜 HTTP가 욕을 먹어야 하죠?
차라리 Exploit Pro Max라고 부르지 그랬어요...
당연히 Brotli2Shell이 됐어야죠. 그런데 구글이 기막히게도 그 화살을 피해 갔어요. 구글보다 NodeJS가 먼저 수정본을 내놓게 만들었거든요(누군가는 이걸 우연한 노출이라고 부르겠지만요). 그러고 나서 마침내 구글이 입을 열었을 때도 더 넓은 커뮤니티에는 거의 알리지 않았고요. 여기 사건 타임라인[0]과, 구글의 태도에 대해 제가 더 폭넓게 불평한 글[1]이 있어요.
[0] https://www.bugsink.com/blog/http2shell-timeline/
[1] https://news.ycombinator.com/item?id=50230362
LLM이 초반의 이해를 바탕으로 세션, 이슈, PR 이름을 짓고는 나중에 갱신하지 않는 경우, 다들 한 번쯤 보셨을 겁니다.
AI는 제목을 검토해 달라는 요청을 받지 않고, 경험도 없고 생각도 하지 않으니 당연히 그걸 짚어 내지 못합니다.
요즘은 사람이 아무것도 검토하지 않으니 당연히 아무도 잡아내지 못하고요.
추가: AI는 "글쓰기"를 아무리 잘해도, 글쓰기 자체는 형편없고 특히 이름 짓기는 더 형편없습니다. 한번은 AI가 클라이언트 이름을 OpenAICompatibleEmbeddingClient로 지었어요. "야아아... "compatible"은 당연히 깔려 있는 거 아냐? 누가 이런 식으로 이름을 지어?!"
OpenAI 자체 API뿐 아니라 OpenAI 호환 임베딩 API를 제공하는 다른 서비스도 지원한다는 뜻이잖아요. 합리적인 이름 같은데요!
OpenAICompatible은 꽤 많은 엔드포인트가 지원하는 OpenAI API의 일부분 또는 한 버전을 가리키는 표현으로도 널리 쓰입니다.저는 2년쯤 전부터 그런 설명으로 쓰이는 걸 봐 왔습니다.
다행히 저는 AI에게 변명을 요구하지 않았고, 그래서 자폐 스펙트럼에 있는 깐깐한 주니어처럼 변명을 늘어놓지도 않았죠.
왜 그렇게 했냐고 물으면 AI는 기꺼이 그렇게 합니다. 100% "사실 그건 말이죠" 하는 최고의 연기를 보여 줄 거예요.
LLM 기반 AI는 사람이 관찰한 행동을 종합한 결과물입니다.
근데 그게 우리의 현재 패러다임과 시너지를 낼 수 있나요?
서술적인 타입 이름은 좋은 겁니다. 저는 그 이름이 그렇게 나쁘지 않다고 보지만, LLM이 가끔 도를 넘는 건 맞습니다.
더 나은 이름은 Konduktsemmel입니다. 오스트리아에서 장례식(Kondukt) 때 먹는 빵(Semmel)이라는 뜻이에요. Brotli랑도 어울리지 않나요?
(사실 장례식에서는 케이크를 먹고 커피를 마시지만, 오스트리아 일부 지역에서는 여전히 그렇게 부릅니다.)
더 나은 이름은 Fred입니다. 왜냐하면
저는 같은 취약점을 다룬 Broccoli2Shell이라는 이름과 사이트가 훨씬 좋습니다: https://broccoli2shell.com/ . 우선, Brotli를 직접 디코딩하는 경우라면 HTTP 없이도 이 취약점을 건드릴 수 있을 거라고 거의 확신합니다. HTTP는 그게 가장 흔히 나오는 경우일 뿐이고요.
요즘은 왜 심각한 취약점마다 귀여운 이름과 웹사이트가 붙죠? 그 사이트 비용은 누가 내는 건가요?
10년도 넘은 일입니다. 이런 흐름을 제대로 만든 건 아마 Heartbleed였을 거예요.
작은 정적 웹사이트는 사실상 공짜예요(Cloudflare, GCP 무료 구간 등).
도메인은 공짜가 아니죠.
그렇긴 한데, 도메인은 의미가 있는 1년 동안 10~20달러 넘게 들지는 않잖아요.
\o/
아뇨, 공짜 맞습니다. 원하는 도메인이 공짜가 아닐 뿐이에요.
GitHub.io 도메인, workers.dev 도메인 같은 건 전부 무료 도메인입니다.
일부 스타트업의 가입 혜택을 영리하게 (또는 악용해서) 쓰면 나중에 다른 곳으로 이전할 수 있는 무료 도메인을 받을 수도 있는데, 요즘은 그런 곳 찾기가 더 어렵습니다.
진지하게 말해서, 악당들은 이런 걸 항상 알아냅니다.
대가를 바라지 않고 도움이 되는 웹사이트를 만들어 준 사심 없는 분께 저는 불만이 전혀 없는데요!
그 사람들은 명성을 바라고, 실제보다 상황을 더 나쁘게 보이게 만들기도 합니다.
정확히 어떻게 "실제보다 더 나쁘게 보이게" 한다는 거죠? 문제가 뭔지... 보여 줘서요?
이 링크를 콕 집어서 한 말은 아니고, 보통 심각도를 실제보다 더 심각하게 부풀린다는 얘기입니다. 일종의 선정주의가 흔히 쓰이거든요.
아니요, 그렇지 않아요. 사이트에는 작성자가 적혀 있지 않으니 명성은 애초에 해당이 없고, 이 버그가 어느 정도로 평가됐는지("GitHub는 9.2 Critical(CVSS 4.0), Node.js는 High로 평가")도 맨 위에 정확히 밝혀 두었습니다.
맨 끝에 이름이 나와 있고 "우리"라는 표현도 있어요. 하지만 말씀드렸듯이 이 페이지 하나를 두고 한 말이 아니라 일반론이었습니다. 이런 걸 만드는 대부분보다는 훨씬 낫습니다.
아이러니하게 느껴집니다. 구글은 한동안 라이브러리의 취약점이 소프트웨어를 위태롭게 할 수 있다는 정당한 우려를 들어 Chrome에 JPEG XL을 넣지 않았습니다. 그런데 JPEG XL 논란보다 10년 전에 Brotli 데이터 압축 포맷을 만든 곳이 바로 구글이고, 공격 표면을 넓히는 건 마찬가지인데도 Chrome에 기꺼이 넣고 다른 곳에서도 이 라이브러리를 쓰라고 권했습니다.
사람은 시간이 지나면 생각이 바뀝니다.
JXL은 Brotli보다 훨씬 복잡해서 취약한 구현이 나올 위험도 더 큽니다.
Brotli 라이브러리에도 더 엄격한 기준을 요구했어야 합니다. Chromium이 쓰는 수많은 다른 의존성도 마찬가지고요. 하지만 아이러니는 없습니다.
Brotli는 연산 비용을 줄여 주고 JPEG XL은 저장 공간을 줄여 주는데, 적어도 지금까지는 저장 공간이 연산보다 싸서 그런 거 아닐까요?
저는 위험한 RCE 버그랑 무해한 RCE 버그를 맨날 헷갈린다니까요.
2월에 보고되고 수정까지 됐는데 아무도 중요성을 못 알아본 게 어떻게 된 건지 궁금하네요. AI 자동화에 운전대를 잡은 사람이 아무도 없었던 경우일까요?
OpenAI가 2월에 이걸 발견했다는 주장의 출처를 못 찾겠어요. 기사가 링크한 출처(OpenAI의 트윗)에는 그런 말이 없습니다.