이름을 http2shell이 아니라 googlebrotli2shell로 붙여야 한다고 봄. Google도 평판에 타격을 좀 받아야 함. wuffs를 만들고, 메모리 안전성을 위해 Rust를 장려하고, Carbon까지 보유한 조직에서 나온 기본적인 메모리 안전성 위반임. 비슷한 시기에 나온 zstd에는 고품질 Rust 구현도 있음.
게다가 Brotli 라이브러리는 거의 1년 동안 릴리스가 없었고, 이번 취약점 수정은 이미 2월에 커밋됐음. Google은 모든 프로젝트를 main에서 빌드하니 영향을 받지 않았지만, 오픈소스 릴리스는 뒷전임. 각종 수정 커밋 100개 이상을 묵혀 두다가 보안 사고가 터져야 릴리스하는 셈임.
압축 해제 쪽은 맞지만, 제대로 된 첫 zstd Rust 압축 구현은 어제 발표됐음. 그전까지 Rust 지원은 zstd보다 Brotli가 나았음.
그래도 요즘 압축 해제 라이브러리는 대부분 메모리 안전해야 한다는 데 동의함. 압축 해제 쪽이 구현도 쉽고 취약점에 노출될 가능성도 더 크기 때문임.
메모리 버그마다 귀엽고 자극적인 이름을 붙여 마케팅까지 하는 건 그만 봤으면 함.
허리케인처럼 이름을 붙이면 어떨까? NIST가 알파벳순 이름 목록을 만들고 CVE 점수 9.0 이상에 이름을 배정하는 식임. 예를 들면 ‘취약점 Alex, CVE 9.4’임.
이미 오래전에 그렇게 굳어졌음. 내가 기억하는 첫 사례는 Rowhammer임.
적어도 서버에서는 이번에도 위험이 과장된 듯함. Node, Python, C#을 쓰는 조직의 GitHub 저장소 200개 이상을 확인했는데 취약한 곳은 없었음. 실제로 Brotli 디코더를 호출하는 곳이 없었기 때문임. 해당 플랫폼들은 서버 측에서 이를 기본 활성화하지 않음. 특정 조건이 충족돼야 취약해지는 PHP 버그와 비슷함.
클라이언트는 위험이 더 크니 패치해야 함. 하지만 클라이언트가 공격자에게 접속하지 않는 한 공격받지는 않으니, 밤을 새워 고칠 일은 아니라고 봄. .NET도 해당됨. Microsoft의 Brotli 모듈은 Google의 취약한 참조 구현을 감싼 바인딩이기 때문임.
Node의 fetch 는 br 응답에 자동으로 디코더를 호출하지 않나? 그렇다면 서버가 가져오는 콘텐츠를 공격자가 통제할 수 있을 때 취약해질 수 있지 않나?
어떻게 확인했나? 실행할 수 있는 PoC나 명령어가 있나?
PHP 취약점은 이미 악용 가능함. 특별한 조건도 필요하지 않으니 잘못된 정보를 퍼뜨리지 말아야 함.
어제 하루만 이 패키지로 만들어진 Python 서버 420만 대는 동의하지 않을 것 같은데? https://pypistats.org/packages/brotli.
선택 의존성의 다운로드 수는 찾지 못했지만, 별것 아닌 듯 보이는 aiohttp[speedups] 에도 들어가고 urllib3[brotli]에도 포함됨. 특히 에이전트 시대에는 라이브러리 이름조차 모른 채 이런 서버를 운영하는 사람이 아주 많을 것임.
내가 뭔가 놓쳤을 수도 있지만, 왜 서버와 클라이언트 구분이 중요한지 모르겠음. 프런트엔드 앱이 백엔드는 지원하지 않는 프로토콜로 통신할 이유가 있나?
수요일에 직접 가지고 놀아 봤음. Node 릴리스 이후에도 패치된 Brotli 패키지가 PyPI에 올라오기까지 하루 정도 걸렸음.
Python에서는 httpx.get(URL) 한 번으로 쉽게 재현할 수 있었지만, 그 환경에서 ASLR을 우회하는 방법은 찾지 못했음. 충돌을 일으키기는 쉽고 LLM 도움으로 일부 읽기·쓰기 수단도 확보했지만, 필요한 위치의 함수 포인터까지 연결하지는 못했음. 힙 조작이 필요할 듯했지만 거기서 흥미를 잃었음.
실제 원격 코드 실행(RCE) PoC가 나오기를 기대함. 가중치 공개 모델로도 대부분의 크롤러를 쫓아낼 ‘회귀 테스트’를 쉽게 만들 수 있음.
왜 RCE PoC를 바라는 건가? 호기심 때문인가, 아니면 공격에 쓰고 싶은 건가? 그런 PoC의 존재와 공개는 세상을 더 위험하게 만듦.
보통의 보안 문제는 크게 주목받지 못하는 듯함. PHP와 eMudhra 문제는 심각해 보였고 널리 알려질 만했지만, 이번 같은 경우는 잘 모르겠음. 정말 심각한 취약점인가, 아니면 요즘 웹 보안에 관심이 몰려서 뉴스가 된 흔한 문제인가?
지금은 업계 전체가 긴장해 있고 CVE 수가 엄청나게 폭증했음. 사고가 터질 때까지 라이브러리를 몇 달, 몇 년씩 방치하던 방식보다, 모든 문제를 중요하게 보고 즉시 업데이트하는 쪽으로 신중하게 대응하는 듯함.
코드가 HTTP URL에 접속하기만 해도 상대 서버가 해킹할 수 있음. 경우에 따라 클라이언트에서도 공격할 수 있음. PHP 문제에 견줄 만함.
상당히 심각하다고 봄. 인터넷에 노출된 꽤 넓은 범주의 서버에서 비교적 간단하게 원격 코드 실행이 가능함. 실제 취약한 서버 수는 흔히 부풀려지는 규모보다 훨씬 작지만, Node/Express 서버를 운영한다면 지금은 정말 비상 대응해야 함.
확인해 보니 Rust 서버는 괜찮을 듯함. brotli 크레이트는 C 구현의 바인딩이 아니기 때문임.
C 라이브러리 바인딩은 없지만, 일부 호환성 코드와 설계 결정을 공유하는 것으로 보임. 즉, 명세만 보고 독립적으로 만든 구현이 아니라 이식본임. 이번 RCE는 범위를 벗어난 메모리 접근으로 발생하므로 해당 크레이트까지 이어졌을 가능성은 낮지만, 이런 차이는 염두에 둘 필요가 있음.
그렇지 않음. cargo에 ldd를 실행하면 libcurl을 통해 libbrotlidec.so.1에 연결돼 있음. 빌드 도구인 cargo가 제멋대로 네트워크에 접속해 주니 참 반가운 일임.
이런 식으로 Brotli를 끌어들이는 크레이트가 753개임. https://lib.rs/crates/curl/rev.
특히 치명적인 건 oath 크레이트임. 사실상 OAuth를 쓰는 모든 Rust 프로젝트가 취약함. crates.io의 398개 프로젝트에 더해, 그곳에 게시하지 않은 수많은 실행 파일 프로젝트도 있음.
또 하나 우려되는 건 ‘실용적이고 즐겁게 쓸 수 있는 HTTP 클라이언트’인 isahc임. https://lib.rs/crates/isahc sequoia를 포함해 317개 크레이트가 사용하니, 개인 키 탈취까지 걱정해야 할 판임.
정확히 무엇을 고쳤는지 분석한 글은 없는 듯함. 그래서 문제를 해결한 Chromium 커밋이 이것이라고 추측함. https://source.chromium.org/chromium/chromium/src/+/224140d4....
이 커밋에는 BROTLI_BOOL을 BrotliDecoderErrorCode로 바꾸는 리팩터링이 들어 있음. 기존에는 어떤 오류 조건에서 단순히 false를 반환했지만, 안전하게 처리를 이어 가려면 더 자세한 오류 정보가 필요했던 것 같음. if (prev_ix < cur_ix)를 if (prev_ix < cur_ix && cached_backward <= max_backward)로 바꾼 부분도 수상함. 이번 문제와 관련된 방어 강화일 수도 있고, 별개일 수도 있음.
취약점이 계속 나오는 건 멈추지 않을 듯함.
인터넷에서 접근 가능한 C 코드가 남아 있는 한 계속될 것임.
멈추지 않을 테니 일상으로 받아들여야 함. 적어도 인간에게 사고 대응이라는 일자리는 남아 있음.