Lỗ hổng nghiêm trọng của React bị tấn công như nào
Chào anh em, Mấy hôm nay chắc anh em nghe phong thanh về vụ React2Shell khiến các đội làm React mất bữa bia buổi chiều.
Đây là lỗ hổng đạt điểm tuyệt đối 10.0/10.0 trên thang CVE, ảnh hưởng trực tiếp đến các ứng dụng dùng React Server Components (RSC) như Next.js.
Bài này mình sẽ mổ xẻ kỹ thuật xem hacker đã lách luật như thế nào. Giờ thì bắt đầu thôi!!!
Nếu thấy chủ đề hay thì cho mình xin một bookmark ở phía bên trái để bài viết đến được nhiều người hơn nha
Chúng mình có tạo Group cho các bạn cùng chia sẻ và học hỏi về frontend nâng cao, các câu phỏng vấn khó nha 😄😄😄
Các bạn tham gia để gây dựng cộng đồng Frontend Việt Nam thật lớn mạnh nhé 😍😍😍
Cộng Đồng Frontend Nâng Cao Việt Nam: https://www.facebook.com/groups/seniorfrontendvietnam
Kênh TikTok: https://www.tiktok.com/@sydexa.com
Kênh youtube: https://www.youtube.com/@sydexa.official
1. Nguồn gốc vấn đề
Để hiểu lỗ hổng, trước tiên phải hiểu React Flight. Đây là giao thức React dùng để truyền dữ liệu giữa Server và Client trong mô hình React server component
Khác với JSON thường chỉ chứa dữ liệu tĩnh, Flight rất mạnh: nó có thể truyền cả một Promise (dữ liệu đang xử lý). Mục đích cốt lõi là để Server có thể trả về khung giao diện ngay lập tức cho người dùng đỡ phải đợi, còn các dữ liệu nặng (như query Database chưa xong) sẽ được stream dần về sau dưới dạng Promise để điền vào chỗ trống.
React Flight giống như việc anh em đi ăn lẩu băng chuyền.
- JSON thường: Chờ bếp làm xong hết 10 món rồi mới phục vụ.
- React Flight: Bếp cứ làm, món khai vị xong trước thì đưa ra cho bạn ăn trước (UI Shell). Món chính (Data nặng) chưa chín thì họ đưa bạn cái vé "Chờ 1 lúc" (Promise). Khi nào chín họ thả trôi theo băng chuyền đến chỗ bạn
Lỗ hổng sinh ra từ chính sự kết hợp giữa giao thức stream mạnh mẽ này và tính chất Dynamic/Duck Typing dễ dãi của JS.

2. Cách lỗ hổng bị khai thác
Payload của cuộc tấn công trông như thế này (đã được tối giản):

Nhìn đống JSON này rối não đúng không? Hãy đi sâu vào 4 bước để chiếm quyền điều khiển hệ thống nhé
Bước 1: Giả mạo "Người nhà" (Internal State Spoofing)
React có một cơ chế nội bộ để theo dõi trạng thái các Promise đang stream. Hacker đã gửi lên một object có status: "resolved_model".
Đây là trạng thái nội bộ của React, lẽ ra người dùng không được phép gửi lên. Nhưng do thiếu kiểm tra dữ liệu đầu vào, React tin rằng object này là dữ liệu của hệ thống đã xử lý xong.
Nó giống như việc bạn mặc bộ đồ bảo vệ, đi vào cổng công ty và nói "Tôi là nhân viên an ninh". Bảo vệ thật nhìn thấy đồng phục thì tin luôn mà không check thẻ nhân viên.

Bước 2: Bẫy "Duck Typing" và Cú lừa Reference
Trong JS, một object được coi là Promise nếu nó có hàm .then(). Đây gọi là Duck Typing (nhìn giống vịt, kêu giống vịt thì là vịt 🥲). Hiểu đơn giản, JS không kiểm tra Class hay Type của đối tượng, mà chỉ quan tâm nó có làm được việc đó không.
Mày có hàm .then? Ok, với tao mày là Promise, bất kể mày xuất thân từ đâu
Đây là bước hacker lợi dụng sự linh hoạt này của JS để mở cánh cửa vào hệ thống.
- Tin tưởng sai lầm: Chính vì React tin tưởng mù quáng vào quy tắc Duck Typing trên, nên khi hacker gửi lên một object giả mạo có chứa
.then, React sẽ lập tức coi đó là Promise và cố gắng thực thi (await) nó. - Kích hoạt: Trong payload
1: "$@0", dấu@ra lệnh cho React: "Cục số 0 là Promise đấy,awaitnó đi!".

- Cú lừa: Khi React ngoan ngoãn
awaitcục số 0, nó sẽ tìm gọi hàm.then. Hacker đã gánthen: "$1:then". Đây là cú pháp tham chiếu của React Flight. Hacker đang bảo React: "Đừng chạy hàm.thencủa tao, hãy chạy cái hàm.thenlấy từ thằng số 1 kia kìa".
Việc trỏ ngược này khiến React vô tình truy cập vào các thuộc tính nội tại (internal properties) của prototype chain thay vì dữ liệu người dùng thông thường.

Hãy tưởng tượng React là một anh bảo vệ đi kiểm tra giấy tờ.
- Quy định: Cứ ai đeo thẻ "Nhân Viên" là được vào.
- Hacker: Đưa một con ma-nơ-canh (Object giả) đeo thẻ "Nhân Viên" đến.
- React: Thấy thẻ, liền hỏi: "Anh cho tôi xem chứng minh thư (gọi hàm
.then)". - Cú lừa: Trên tay con ma-nơ-canh có mẩu giấy ghi: "Chứng minh thư của tôi nằm trong két sắt của Giám đốc (Reference $1:then)".
- Kết quả: React ngây thơ chạy vào mở két sắt Giám đốc để lấy giấy tờ.
Bước 3: Leo thang đặc quyền – Biến .get() vô hại thành eval() nguy hiểm
Tiếp đà từ việc "chui" được vào bên trong prototype ở Bước 2, hacker thực hiện cú leo thang đặc quyền cuối cùng.
- Từ Prototype đến Constructor:
Ở bước 2, chúng ta đã có tham chiếu đến $1:then (là một Function). Mà cha đẻ của mọi hàm trong JS là Function Constructor. Hacker truy cập nó bằng payload: $1:then:constructor.
=> Đây chính là chìa nguy hiểm.
Vì trong JS, gọi new Function('code') tương đương với eval('code').

- Cú tráo đổi (The Switch):
Tại sao hacker lại chèn dòng value: '{"then":"$B"}'. $B là gì?
Trong giao thức Flight, $B\ là ký hiệu viết tắt cho Blob (Binary Large Object) và trong source code của React, khi gặp kiểu dữ liệu là Blob, nó có một đoạn logic rất hồn nhiên như sau:

blob đã kích hoạt dòng code _formData.get(). Đây chính là cái bẫy để dụ React thực thi hàm .get mà hắn sắp đánh tráo.Sau đó hacker đã thay thế hàm .get vô hại bằng cái constructor tử thần vừa tìm thấy ở trên (Payload: get: "$1:then:constructor")
Khi React chạy dòng _formData.get(blobKey), thực chất nó đang thực thi: new Function(blobKey).
Tức là thay vì lấy dữ liệu, nó lại biên dịch và chạy luôn dữ liệu đó dưới dạng code.
Tiếp tục với ví dụ trên, hacker tiếp tục làm các bước:
- Bước 2 (Prototype): Hacker đã lẻn vào phòng điều khiển trung tâm nhờ trick ma-nơ-canh.
- Bước 3 (Swap): Trên bảng điều khiển có nút màu xanh ghi là "LẤY HÀNG" (chính là hàm
.get). Anh công nhân React đinh ninh ấn nút đó sẽ ra hàng hóa. - Thực tế: Hacker đã đấu dây lại nút màu xanh đó nối thẳng vào "Nút tự huỷ của nhà máy" (Function Constructor).
- Kết quả: Anh công nhân ấn nút "Lấy hàng", nhưng thực chất đã tự huỷ cả nhà máy ( thực thi code của hacker)

Bước 4: Kích nổ (Execution)
Code độc hại thực sự nằm ở _prefix: "console.log('☠️')//"
Khi React ấn cái nút đã bị đấu dây sai đó, nó chạy lệnh:

Hacker chèn // ở cuối để comment đi các tham số rác phía sau, giúp code JS hợp lệ. BÙM! Server thực thi code của hacker.
4. Bài học & Cách khắc phục
Lỗ hổng này nghiêm trọng vì nó exploit tính năng cốt lõi của ngôn ngữ (JS dynamic nature) và sự thiếu sót trong validation của thư viện (React).
React team đã thiếu check hasOwnProperty. Họ tin tưởng rằng input đầu vào chỉ chứa các key của user object, chứ không nghĩ user có thể chọc vào các property nội bộ như constructor hay __proto__.
Ngay sau khi phát hiện lỗ hổng, team react đã tạo các bản vá đã thêm check hasOwnProperty để chặn việc truy cập prototype chain.
Đây là bài học kinh điển. Dữ liệu từ client gửi lên server (đặc biệt là dạng serialized object phức tạp) luôn tiềm ẩn nguy cơ.
Lỗi bảo mật này cực kỳ tinh vi vì nó kết hợp sự am hiểu sâu sắc về JS Internals và luồng xử lý của React Flight!
Nếu thấy chủ đề hay thì cho mình xin một bookmark ở phía bên trái để bài viết đến được nhiều người hơn nha
Chúng mình có tạo Group cho các bạn cùng chia sẻ và học hỏi về frontend nâng cao, các câu phỏng vấn khó nha 😄😄😄
Các bạn tham gia để gây dựng cộng đồng Frontend Việt Nam thật lớn mạnh nhé 😍😍😍
Cộng Đồng Frontend Nâng Cao Việt Nam: https://www.facebook.com/groups/seniorfrontendvietnam
Kênh TikTok: https://www.tiktok.com/@sydexa.com
Kênh youtube: https://www.youtube.com/@sydexa.official
All rights reserved
