Quay lại
2026/10/26
HTTP2Shell (CVE-2026-111497): cập nhật tệp nhị phân không vá được các tiến trình đang chạy
Lỗ hổng Brotli đằng sau HTTP2Shell ảnh hưởng Node.js, Bun và Python. Cách xác định rủi ro thực trong tiến trình đang chạy và khởi động lại AI agent an toàn.
Ngày 21 tháng 10 năm 2026, Node.js phát hành bản vá cho CVE-2026-111497, lỗi ghi ngoài vùng nhớ (out-of-bounds write) trong bộ giải mã Brotli của Google. Chỉ trong vài ngày sau đó, các khuyến cáo bảo mật cho Bun, các gói Python brotli và brotlicffi, Envoy và Istio lần lượt được công bố, và báo chí bảo mật đặt tên cho lỗi này là Node2Shell, sau đó là HTTP2Shell. Bất chấp biệt danh, đây không phải lỗi của giao thức HTTP/2. Đây là lỗi giải nén, và nó đáng lo ở bất cứ đâu phần mềm giải mã dữ liệu Brotli mà kẻ tấn công có thể tác động.
Bài viết này trình bày những gì đã được xác minh, những gì chỉ là khả năng, và cách kiểm tra hệ thống của chính bạn. Bài cũng chia sẻ một bài học từ quá trình ứng phó của chúng tôi: cài đặt tệp nhị phân đã vá không sửa được một tiến trình vẫn đang chạy bản cũ. Trong môi trường của chúng tôi, các phiên Claude Code chạy dài vẫn giữ runtime nhúng cũ sau khi bản Claude Code đã cài đặt được cập nhật.
Điểm chính
- Bị ảnh hưởng: Brotli upstream từ 1.0.3 đến 1.2.0. Đã vá: 1.0.10, 1.1.1 và 1.2.1. Các sản phẩm đóng gói kèm Brotli cần bản phát hành đã vá của riêng chúng.
- Rủi ro thực sự đòi hỏi một đường đi cụ thể: dữ liệu Brotli do kẻ tấn công tác động đến được bộ giải mã có lỗ hổng. Chỉ tên sản phẩm hay khả năng truy cập mạng thì chưa phải là bằng chứng.
- Kiểm tra các tiến trình đang chạy, không chỉ các gói đã cài. Một tiến trình giữ nguyên runtime mà nó khởi động cùng cho đến khi được khởi động lại.
- Khởi động lại các AI agent có trạng thái lần lượt từng cái một, sau khi đã bàn giao sự kiện, phê duyệt và trạng thái công việc của chúng.
1. Lỗ hổng là gì
Khi gặp một block switch không hợp lệ, bộ giải mã báo “cần thêm dữ liệu đầu vào” thay vì một lỗi định dạng vĩnh viễn. Khi đó, bên gọi dạng streaming tiếp tục nạp thêm byte, và bộ giải mã nối chúng vào một bộ đệm nội bộ 8 byte, ghi tràn qua điểm cuối của bộ đệm. Khuyến cáo của Node.js cho biết kẻ tấn công có thể cung cấp dữ liệu Brotli mà ứng dụng giải nén bằng node:zlib “có thể làm hỏng bộ nhớ tiến trình, dẫn đến sự cố sập, rò rỉ bộ nhớ tiến trình hoặc thực thi mã từ xa”.
Mức độ nghiêm trọng khác nhau tùy nguồn. Node.js đánh giá là High. Khuyến cáo trên GitHub cho các gói Python và khuyến cáo của Envoy đánh giá là Critical, điểm CVSS 4.0 là 9.2. Ubuntu hiện xếp ở mức ưu tiên Medium. Tính đến ngày 26 tháng 10, đánh giá của CISA trên bản ghi CVE ghi nhận tình trạng khai thác là “none”, và CVE này không có trong danh mục Known Exploited Vulnerabilities của CISA. Khuyến cáo của Node.js vẫn nêu thực thi mã từ xa là một hậu quả có thể xảy ra, vì vậy đừng coi việc chưa có báo cáo tấn công là lý do để chờ đợi.
2. Điều kiện tiên quyết: khi nào bạn thực sự bị phơi nhiễm
Cả ba điều kiện sau phải cùng thỏa mãn:
- Bộ giải mã có lỗ hổng được nạp trong tiến trình: một thư viện Brotli đóng gói kèm hoặc của hệ thống nằm trong dải phiên bản bị ảnh hưởng mà chưa có bản vá hay backport.
- Có thành phần gọi đến nó: giải nén tường minh, hoặc tự động giải mã
Content-Encoding: br. - Dữ liệu đầu vào có thể bị kẻ tấn công tác động: phản hồi từ URL tùy ý, tệp được tải lên, nội dung yêu cầu gửi đến, hoặc lưu lượng HTTP không mã hóa trên một mạng thù địch.
Giải mã tự động là đường đi mà các nhóm hay bỏ sót nhất. Khuyến cáo của Bun lưu ý rằng fetch mặc định gửi Accept-Encoding: br và giải mã phản hồi, nên mọi chương trình Bun tải URL không tin cậy đều có đường đi này. Trong Python, khi đã cài brotli hoặc brotlicffi, urllib3 tự động giải mã các phản hồi Content-Encoding: br, và requests cũng khai báo hỗ trợ br trong các yêu cầu của nó. Điều này xảy ra ngay cả khi mã của bạn không hề nhắc đến Brotli. Envoy và Istio chỉ bị phơi nhiễm khi có cấu hình bộ giải nén Brotli.
3. Đã xác nhận bị ảnh hưởng so với chỉ là có khả năng
Tình trạng tính đến ngày 26 tháng 10 năm 2026. Hãy kiểm tra các khuyến cáo được liên kết, vì tình hình đang thay đổi nhanh.
| Thành phần | Tình trạng | Đã vá trong |
|---|---|---|
| Brotli upstream (C) | Đã xác nhận: 1.0.3 đến 1.2.0 | 1.0.10, 1.1.1, 1.2.1 |
| Node.js 22, 24, 26 | Đã xác nhận | 22.23.4, 24.22.1, 26.12.1 |
| Node.js 20 trở về trước (hết vòng đời) | Chưa đánh giá; Node.js luôn coi các dòng hết vòng đời là bị ảnh hưởng | Chuyển sang dòng còn được hỗ trợ |
| Bun (bao gồm tệp thực thi đã biên dịch) | Đã xác nhận: 1.0.21 đến 1.4.3 | 1.4.4, và biên dịch lại các ứng dụng đã biên dịch |
| Python brotli / brotlicffi | Đã xác nhận: từ 1.0.3 | 1.2.1 / 1.2.1.0 |
| Envoy (có cấu hình bộ giải nén Brotli) | Đã xác nhận | 1.40.0, 1.39.3, 1.38.6, 1.37.8 (1.36.12 trên nhánh 1.36 không còn được hỗ trợ) |
| Istio (bật giải nén Brotli) | Đã xác nhận | 1.31.2, 1.30.6, 1.29.9 (bản phát hành cuối cùng của 1.29) |
| Gói brotli của Ubuntu | Cần đánh giá (mọi phiên bản phát hành) | Chưa có gói đã vá |
| Gói nodejs của Ubuntu | Có lỗ hổng trên 20.04 (Brotli đóng gói kèm). Từ 22.04 trở đi dùng Brotli của hệ thống, nên tình trạng theo hàng phía trên | Xem trình theo dõi của Ubuntu |
| Gói brotli của Debian | Đã vá trong bookworm và trixie; forky thì chưa | 1.0.9-2+deb12u1, 1.1.0-2+deb13u1 |
| Chrome, Electron và các ứng dụng Electron | Chưa rõ: chưa tìm thấy tuyên bố của nhà cung cấp | Theo dõi ghi chú phát hành của nhà cung cấp |
Về hàng cuối cùng: Chrome và Electron đi kèm bản sao Brotli riêng, và một ứng dụng Electron còn nhúng cả runtime Node.js. Tại thời điểm viết bài, các bản phát hành ổn định mới nhất của Electron (45.0.0, 44.9.3 và 43.8.3, đều ngày 21 tháng 10) ghi Node.js 24.22.0, không phải 24.22.1, và ghi chú phát hành không nhắc đến CVE này. Các bản tiền phát hành của Electron từ ngày 23 tháng 10 mang Node.js 24.22.1, nên nhiều khả năng sẽ có bản vá ổn định, nhưng nó chưa được phát hành. Do đó, một ứng dụng desktop xây dựng trên Electron có thể bị phơi nhiễm, chứ chưa được xác nhận là có lỗ hổng. Điều tương tự áp dụng cho các ứng dụng chat và công cụ AI trên desktop: hãy kiểm tra tuyên bố của từng nhà cung cấp trước khi kết luận chúng bị ảnh hưởng hay an toàn.
Thêm một cái bẫy nữa: phiên bản thư viện được báo cáo không chứng minh được điều gì theo cả hai chiều. Node.js 22.23.4 đã vá vẫn báo process.versions.brotli là 1.1.0, vì bản vá được backport. Các bản phân phối cũng làm như vậy. Hãy đánh giá dựa trên phiên bản đã vá của sản phẩm hoặc tình trạng bảo mật của bản phân phối, không dựa vào chuỗi phiên bản Brotli.
4. Những gì chúng tôi tìm thấy trong môi trường của mình
Chúng tôi đã kiểm kê ở chế độ chỉ đọc trước khi thay đổi bất cứ điều gì. Dưới đây là những gì chúng tôi quan sát được, đã ẩn danh hóa, với các phát hiện đã xác nhận được tách riêng khỏi những rủi ro chúng tôi không thể xác minh.
Quan sát đã xác nhận
- Bản Claude Code đã cài đặt đã được cập nhật, nhưng khoảng một chục phiên chạy dài vẫn đang chạy các bản build cũ hơn được khởi động từ vài tuần trước. Chúng chưa từng được khởi động lại.
- Lần đếm đầu tiên của chúng tôi bỏ sót ba phiên. Chúng chạy dưới một tiến trình trợ giúp khác, nên tìm kiếm theo dòng lệnh không khớp. Tìm kiếm theo tệp thực thi thì phát hiện ra chúng.
- Các tệp của bản build cũ đã bị trình cập nhật xóa khỏi ổ đĩa, nên không thể đọc được phiên bản Bun nhúng của các tiến trình đó.
- Một bản cài đặt sót lại qua trình quản lý gói của một bản build Claude Code cũ hơn có nhúng Bun 1.4.0. Không có gì dùng đến nó trong vận hành bình thường, nhưng một đường dự phòng (fallback) thì có thể. Chúng tôi đã gỡ bỏ nó sau khi chứng minh được không có gì phụ thuộc vào nó.
- Một runtime Node.js đã hết vòng đời và không được dùng vẫn còn cài trên một máy chủ. Chúng tôi gỡ bỏ nó theo cùng cách.
- Bản build Claude Code hiện tại mà chúng tôi nâng cấp lên (2.1.310) chứa Bun 1.4.4. Chúng tôi xác nhận điều này bằng cách kiểm tra tệp nhị phân. Changelog của Anthropic không nhắc đến CVE này, vì vậy hãy kiểm tra bản build của bạn theo cùng cách.
Chưa xác minh, được coi là rủi ro
- Liệu các phiên cũ có thực sự có lỗ hổng hay không. Không đọc được phiên bản Bun của chúng. Bun 1.4.4 đã vá chỉ xuất hiện vào khoảng ngày 22 tháng 10, sau các bản build đó, nên chúng tôi coi chúng là bị ảnh hưởng.
- Liệu kẻ tấn công có thể tiếp cận bộ giải mã thông qua các phiên đó hay không, chẳng hạn bằng cách tải một URL độc hại. Đường đi này hợp lý dựa trên hành vi
fetchđã được Bun mô tả trong tài liệu, nhưng chúng tôi chưa chứng minh được. - Liệu các phần mềm khác đóng gói kèm bản sao Brotli riêng có bị ảnh hưởng hay không. Khi không có tuyên bố của nhà cung cấp, chúng tôi theo dõi sản phẩm theo các bản phát hành của nhà cung cấp thay vì giả định nó an toàn hay có lỗ hổng.
Chúng tôi không tìm thấy bằng chứng nào về việc bị khai thác. Chúng tôi cũng không coi đó là bằng chứng của sự an toàn: cách khắc phục là loại bỏ điểm phơi nhiễm, không phải lập luận để gạt nó đi.
5. Danh sách kiểm tra phát hiện và khắc phục
Tìm các phụ thuộc
Tìm Brotli ở những nơi nó được đóng gói kèm, không chỉ ở nơi nó được cài dưới dạng gói. Kiểm tra SBOM và các container image để tìm Node.js, Bun, các gói Python, Envoy và Brotli liên kết tĩnh. Kiểm tra cả các phụ thuộc bắc cầu của Python, vì Brotli thường đi kèm dưới dạng tùy chọn bổ sung (extra).
# Node.js: compare with 22.23.4 / 24.22.1 / 26.12.1
node -p process.version
# Python packages: run once per interpreter and virtual environment
python -m pip list 2>/dev/null | grep -iE '^(brotli|brotlicffi) '
pipdeptree -r -p brotli # what pulled it in
# Debian / Ubuntu system library (then check the distro tracker)
dpkg-query -W -f='${db:Status-Abbrev} ${Package} ${Version}\n' 'libbrotli*'
# Container images, with an SBOM scanner such as Syft
syft <image> | grep -iE 'brotli|node|bun'
Tìm các runtime nhúng
Các chương trình được build bằng bun build --compile mang theo bản sao Bun riêng. Khuyến cáo của Bun cho biết chúng vẫn bị ảnh hưởng cho đến khi được build lại với 1.4.4. Nâng cấp Bun trên máy build không thay đổi các tệp nhị phân đã được triển khai.
# The Bun version inside a compiled executable
grep -aoE 'Bun v1\.[0-9]+\.[0-9]+' /path/to/binary | sort -u
Tìm các tiến trình đang chạy
Hãy kiểm tra những gì đang chạy, không phải những gì đã cài. Trên Linux, một tiến trình có tệp thực thi đã bị thay thế hoặc xóa sẽ hiển thị (deleted). Đó là dấu hiệu rõ ràng cho thấy nó vẫn đang chạy một bản build cũ.
# Processes still running a deleted (replaced) executable (run as root to see all users)
for p in /proc/[0-9]*; do
e=$(readlink "$p/exe" 2>/dev/null) || continue
case "$e" in *"(deleted)") echo "${p#/proc/} $e";; esac
done
# Processes older than the fix
pgrep -af 'node|bun|claude' # then: ps -o pid,lstart,args -p <pid>
Trên macOS, lsof -p <pid> hiển thị tệp thực thi mà một tiến trình đã nạp. Hãy so khớp theo tệp thực thi, không theo tên tiến trình, nếu không bạn sẽ bỏ sót các tiến trình trợ giúp và worker chạy nền.
Vá, build lại, triển khai lại
- Nâng cấp lên các phiên bản đã vá trong bảng. Dùng gói đã vá của bản phân phối ở những nơi có backport.
- Build lại các container image và tệp thực thi đã biên dịch, sau đó triển khai lại. Một base image đã vá không có tác dụng gì với image mà bạn chưa build lại.
- Gỡ bỏ các runtime không dùng và các bản cài đặt sót lại, sau khi đã chứng minh không có dịch vụ, tác vụ hay script nào phụ thuộc vào chúng. Luôn giữ phương án rollback.
- Ở những nơi chưa có bản vá, hãy tắt việc giải mã Brotli đối với dữ liệu đầu vào không tin cậy nếu làm vậy là an toàn. Với Bun,
fetch(url, { decompress: false })có tác dụng theo từng yêu cầu. Hãy giao cho mỗi biện pháp giảm thiểu một người phụ trách và một ngày kết thúc.
Khởi động lại và xác minh
Sau khi khởi động lại, hãy kiểm tra tệp thực thi và phiên bản runtime thực tế của tiến trình mới. Sau đó kiểm tra kết quả nghiệp vụ: hàng đợi đã được xử lý hết, các tác vụ theo lịch chạy thành công, cảnh báo giám sát vẫn đến. Một lệnh nâng cấp thành công chỉ chứng minh rằng lệnh đó đã chạy.
6. Khởi động lại AI agent chạy liên tục một cách an toàn
Các AI agent chạy dài nắm giữ trạng thái: các lệnh gọi công cụ đang chờ, các phê duyệt, tin nhắn trong hàng đợi và các công việc đã nhận. Khởi động lại tất cả cùng lúc có thể làm mất trạng thái đó. Trình tự sau đã hiệu quả với chúng tôi:
- Kiểm kê từng phiên: phiên bản tệp thực thi, trạng thái (rảnh, đang chờ hoặc đang bận), hoạt động gần nhất, công việc đang chờ, các phê duyệt mà phiên đang chờ hoặc đang giữ, các sự kiện chưa được xác nhận và cách chúng sẽ được khôi phục.
- Đưa các phê duyệt ra khỏi bộ nhớ. Một phê duyệt chỉ tồn tại trong cuộc hội thoại của một phiên sẽ mất khi khởi động lại. Trước khi khởi động lại, hãy ghi lại từng phê duyệt đang chờ (đã phê duyệt điều gì, bởi ai, cho thay đổi cụ thể nào) ở một nơi lưu trữ bền vững, chẳng hạn ticket hoặc pull request. Sau đó, xác nhận rằng phiên vẫn nhìn thấy nó. Nếu còn nghi ngờ, hãy xin phê duyệt lại thay vì tự giả định.
- Chứng minh phương án thay thế trước. Xác nhận rằng các phiên trên bản build đã vá đã hoạt động, và một trình theo dõi sự kiện độc lập với các phiên cũ đang chạy, để không có sự kiện nào phụ thuộc vào một phiên đang được khởi động lại.
- Khởi động lại các phiên đang rảnh trước, lần lượt từng phiên. Sau mỗi phiên, xác minh tiến trình cũ đã dừng, tiến trình mới chạy tệp thực thi đã vá, lịch sử hội thoại còn nguyên vẹn và mọi kết nối từ xa đã được khôi phục. Sau đó mới làm phiên tiếp theo.
- Khởi động lại phiên đảm nhận nhiều việc nhất sau cùng, giữa hai tác vụ. Yêu cầu nó viết ghi chú bàn giao, chỉ dừng trình theo dõi của nó khi không còn công việc đang chờ và không còn sự kiện chưa xác nhận, khởi động lại, rồi xác nhận trình theo dõi đã tiếp tục từ vị trí đã lưu mà không bỏ sót gì.
- Lường trước dữ liệu đầu vào trong hàng đợi khi kết nối lại. Một trong các phiên chúng tôi khởi động lại đã nhận được một tin nhắn đã chờ suốt mười ngày và hành động theo nó. Hãy kiểm tra hộp thư đến của từng phiên trước khi khởi động lại, và thông báo cho người phụ trách phiên đó.
- Dừng lại và báo cáo lên cấp trên nếu bất kỳ bước nào không chứng minh được công việc đã được khôi phục. Đừng quay về cách tắt hết mọi thứ.
Xác định thế nào là “hoàn thành” trước khi bắt đầu. Hoàn thành nghĩa là không còn tiến trình nào chạy trên bản build bị ảnh hưởng, mọi ngoại lệ đều được ghi lại kèm người phụ trách và ngày kiểm tra lại, và các bước kiểm tra nghiệp vụ đều đạt. “Gói đã được nâng cấp” chưa phải là hoàn thành.
Nguồn tham khảo
- Bản ghi CVE: CVE-2026-111497
- Các bản phát hành bảo mật tháng 10 năm 2026 của Node.js
- Khuyến cáo của Bun GHSA-663w-cv8g-jj2p
- Khuyến cáo cho Python brotli / brotlicffi GHSA-9v8h-qqgm-4m35
- Khuyến cáo của Envoy GHSA-3wvj-j6qm-62mr
- Bản tin bảo mật Istio ISTIO-SECURITY-2026-007
- Trình theo dõi CVE của Ubuntu: CVE-2026-111497
- Trình theo dõi bảo mật Debian: CVE-2026-111497
- Các bản phát hành Electron
Global
Japan
Korea
China
Taiwan
Thailand
Indonesia
Portugal
Spain
France
Germany
Egypt
Russia