Vụ thoát sandbox Chrome thông qua DevTools
(ading.dev)- Ranh giới quyền hạn WebUI của Chromium, tính năng kiểm thử chính sách doanh nghiệp và lỗi trong API tiện ích DevTools đã kết hợp với nhau, cho phép một tiện ích Chrome độc hại đạt tới việc thực thi lệnh shell chỉ với một chút tương tác từ người dùng
- Đường tấn công là gọi API kiểm thử chính sách không được tài liệu hóa từ
chrome://policyđể thay đổi chính sách người dùng, rồi lạm dụng đường dẫn và tham số trình duyệt thay thế của Browser Switcher làm lệnh shell chrome.devtools.inspectedWindow.reload()cho phép chạyinjectedScript, và do độ trễ trong việc chặn truy cập tại thời điểm chuyển sang WebUI hoặc do yêu cầuPage.reloadcòn sót lại sau khi crash, mã có thể được thực thi trên WebUI đặc quyền- Google phân loại lỗ hổng là P1/S1 và bổ sung kiểm tra
loaderIdchoPage.reload, kiểm tra URL tronginspectedWindow.reload(), cũng như kiểm tra trạng thái bật kiểm thử chính sách trong handler WebUI - Các lỗ hổng liên quan được gán CVE-2024-5836 và CVE-2024-6778, cả hai đều nhận CVSS 8.8 High, và tiền thưởng cuối cùng là $20,000
Chromium WebUI và ranh giới sandbox
- Chromium chạy mã không đáng tin cậy bên trong sandbox, và JavaScript của tiện ích Chrome cũng chỉ được hoạt động trong phạm vi quyền hạn được cấp và các API có thể truy cập
- Chỉ riêng quyền của tiện ích cũng có thể đánh cắp thông tin đăng nhập hoặc lịch sử trình duyệt, nhưng về nguyên tắc phạm vi ảnh hưởng phải ở lại bên trong trình duyệt
- Một phần GUI của Chromium được triển khai bằng WebUI như
chrome://settings,chrome://history- WebUI được viết bằng HTML, CSS, JavaScript, nhưng vì phải hiển thị và chỉnh sửa thông tin nội bộ của trình duyệt nên có quyền cao hơn trang web thông thường
- JavaScript ở frontend WebUI có thể giao tiếp với mã C++ native của trình duyệt thông qua các API riêng tư
- Nếu có thể thực thi mã trong WebUI, điều đó có thể dẫn tới vượt qua sandbox của Chromium, nên việc ngăn kẻ tấn công chạy JavaScript không đáng tin cậy trên trang
chrome://là rất quan trọng - Ví dụ, nếu nhấp vào một mục tải xuống
.exetrongchrome://downloads, tệp thực thi có thể được mở, vì vậy Chromium kiểm tra xem thao tác mở tệp có thực sự đến từ input của người dùng hay không
Vượt qua tính năng kiểm thử chính sách doanh nghiệp
- Quá trình săn lỗ hổng bắt đầu từ enterprise policy system của Chromium
- Hệ thống này là tính năng để quản trị viên áp đặt một số thiết lập nhất định lên thiết bị thuộc sở hữu công ty hoặc trường học
- Chính sách thường gắn với tài khoản Google và được tải xuống từ máy chủ quản trị của Google
- Chính sách được chia thành device policies và user policies
- device policies quản lý thiết lập của toàn bộ thiết bị Chrome OS
- user policies áp dụng cho người dùng cụ thể hoặc một instance trình duyệt, và có thể dùng trên mọi nền tảng
- Trên Linux, có thể áp dụng user policies cho một instance Google Chrome bằng cách đặt tệp JSON trong
/etc/opt/chrome/policies, nhưng cần quyền root để ghi vào thư mục này
- Các chính sách hiện đang áp dụng trên thiết bị có thể được kiểm tra trong WebUI
chrome://policy- Trang này cung cấp danh sách chính sách đã áp dụng, log dịch vụ chính sách và tính năng xuất JSON
- Thông thường không có cách chỉnh sửa chính sách trên trang này
- Ghi chú phát hành Chrome Enterprise của Chrome v117 có mục nói rằng trang
chrome://policy/testcho phép kiểm thử chính sách trên các kênh Beta, Dev, Canary- Tính năng này không được nhắc đến trong tài liệu Chromium ngoài ghi chú phát hành đó
- Để bật đúng cách, cần chính sách không được tài liệu hóa
PolicyTestPageEnabled - Nếu không có chính sách này,
chrome://policy/testsẽ chuyển hướng sangchrome://policy
Lỗi xác thực setLocalTestPolicies
- Mã JavaScript của
chrome://policy/testdùngsendWithPromise('setLocalTestPolicies', ...)để thiết lập chính sách kiểm thửsendWithPromise()là wrapper của API riêng tư WebUIchrome.send()- Lệnh gọi này gửi yêu cầu tới hàm handler C++, và handler có thể thực hiện tác vụ nội bộ của trình duyệt
- Khi gọi trực tiếp
setLocalTestPoliciestừ console củachrome://policy, ban đầu trình duyệt bị crash, và log để lại thông báo rằng cần một mảng chính sách - Khi truyền một user policy như
AllowDinosaurEasterEggvới định dạng mảng chính sách đúng, chính sách tùy ý đã được thiết lập dù tính năng không được bật rõ ràng - Handler
HandleSetLocalTestPoliciesphía C++ chỉ kiểm tra sự tồn tại củalocal_test_provider, chứ không kiểm tra tính năng kiểm thử chính sách có thực sự được phép hay không LocalTestPolicyProvider::CreateIfAllowed()gọiIsPolicyTestingEnabled(nullptr, channel)- Đối số đầu tiên
pref_servicelànull, nên bước kiểm traPolicyTestPageEnabledbị bỏ qua - Kiểm tra còn lại chỉ là release channel có phải
CANARYhoặcDEFAULThay không
- Đối số đầu tiên
- Trong bản dựng Chromium không gắn thương hiệu, mã
GOOGLE_CHROME_BRANDINGkhông được biên dịch nên channel vẫn làUNKNOWN- Trong enum,
UNKNOWN = 0,DEFAULT = UNKNOWN, vì vậy kiểm tra channel sẽ qua trên Chromium và các bản dựng phái sinh - Trong bản dựng stable Google Chrome có gắn thương hiệu, release channel được thiết lập đúng, nên lỗi này không hoạt động trên stable Google Chrome
- Trong enum,
Thực thi lệnh shell qua Browser Switcher
- Khi có thể thiết lập user policy tùy ý, module Legacy Browser Support trong chính sách doanh nghiệp của Chrome trở thành đường thoát sandbox
- Legacy Browser Support còn được gọi là Browser Switcher, được thiết kế để chạy trình duyệt thay thế khi người dùng truy cập một URL cụ thể trong Chromium
- Đây là tính năng được tạo ra để hỗ trợ người dùng Internet Explorer
- Hành vi được điều khiển bằng chính sách
- Kết hợp các chính sách
AlternativeBrowserPathvàAlternativeBrowserParameterscó thể khiến Chromium chạy lệnh shell tùy ý dưới dạng “trình duyệt thay thế”- Các chính sách Browser Switcher này chỉ tồn tại trên Linux, macOS, Windows
- Luồng ví dụ như sau
- Đặt
BrowserSwitcherEnabledthànhtrue - Thêm
example.comvàoBrowserSwitcherUrlList - Trên Linux, đặt
AlternativeBrowserPaththành/bin/bash - Đặt
AlternativeBrowserParametersnhư["-c", "xcalc # ${url}"]
- Đặt
- Khi trình duyệt chuyển tới
example.com, Browser Switcher hoạt động và một lệnh dạng/bin/bash -c 'xcalc # https://example.com'được thực thi- Giá trị thay thế
${url}được đặt sau#để được xử lý như chú thích shell
- Giá trị thay thế
- Sau khi thiết lập chính sách trong
chrome://policy, gọiwindow.open("https://example.com")sẽ dẫn tới thực thi lệnh shell tùy ý chỉ bằng JavaScript
Đường vượt qua trong API tiện ích DevTools
- Chỉ với các bước trên, nạn nhân phải dán mã độc vào console trình duyệt trong
chrome://policy, nên tính thực dụng thấp - Đường tự động thực thi được tìm thấy thông qua tiện ích Chrome độc hại
- Tiện ích có thể chèn JavaScript vào trang, nhưng lẽ ra không được chạy JavaScript trên các trang WebUI đặc quyền
- Có bốn API chính để tiện ích chạy JavaScript trong trang
chrome.scriptingchrome.tabscủa Manifest v2chrome.debuggerchrome.devtools.inspectedWindow
- Đối tượng điều tra là
chrome.devtools.inspectedWindow, được cho là tương đối ít được gia cố hơn- Tiện ích dùng API
chrome.devtoolsphải có trườngdevtools_pagetrong manifest - Khi người dùng mở DevTools, trang đó được tải dưới dạng iframe, và bên trong có thể dùng API
chrome.devtools
- Tiện ích dùng API
- Báo cáo lỗi trước đây của David Erceg có trường hợp dùng
chrome.devtools.inspectedWindow.eval()để đạt tới thực thi mã trong WebUI- Bình thường, khi trang được kiểm tra chuyển sang WebUI, việc dùng DevTools API phải bị vô hiệu hóa
- Điểm cốt lõi của cách vượt qua là gửi yêu cầu eval trước khi Chrome vô hiệu hóa API, và làm cho yêu cầu đó đến được trang WebUI
inspectedWindow.reload() và tính đặc biệt của about:blank
chrome.devtools.inspectedWindow.reload()cũng có thể chạy JavaScript trong trang được kiểm tra nếu nhận đối sốinjectedScript- Khi gọi
inspectedWindow.reload()trên trangabout:blankdo WebUI mở, có thể thực thi JavaScript trên trang đặc quyền- Bản thân URL
about:blankkhông đặc biệt, nhưng nó kế thừa quyền hạn và origin của trang đã mở nó about:blankdochrome://settingsmở là trang đặc quyền có originchrome://settings
- Bản thân URL
- Mã vô hiệu hóa DevTools API chỉ kiểm tra URL của đối tượng được kiểm tra, không kiểm tra origin
- Ngay cả khi URL trông bình thường, origin vẫn có thể là origin đặc quyền
- Chỉ riêng đường
about:blankkhó dùng trực tiếp trong exploit chain vìchrome://policykhông mở popupabout:blank - Tuy nhiên, ngay cả trong tình huống
inspectedWindow.eval()thất bại,inspectedWindow.reload()vẫn chạy JavaScript trongchrome://settings- Điều này cho thấy
eval()có kiểm tra riêng tương ứng với kiểm tra origin, cònreload()thì không có kiểm tra cùng mức
- Điều này cho thấy
Ổn định hóa từ race condition sang phương pháp dựa trên crash
- Exploit chain đầu tiên liên tục gọi
inspectedWindow.reload(), nhắm vào cửa sổ thời gian ngắn ngay sau khi trang được kiểm tra chuyển sang WebUI nhưng trước khi trang DevTools vô hiệu hóa API- Điều này dựa trên giả định rằng trang được kiểm tra và trang DevTools nằm ở các process khác nhau
- Nếu yêu cầu
reload()đi vào giữa khoảnh khắc chuyển tớichrome://policyvà lúc DevTools API bị vô hiệu hóa, mã sẽ được thực thi trong WebUI
- Cách này hoạt động nhưng độ tin cậy thấp
- Sau khi tinh chỉnh, tỷ lệ thành công khoảng 70%
- Đây là lỗ hổng nghiêm trọng, nhưng tính bất ổn có thể làm giảm severity
- Sau đó, giống cách tiếp cận trước đây của David Erceg, thử nghiệm xem hành vi yêu cầu debugger còn sót lại sau khi tab crash có thể áp dụng cho
inspectedWindow.reload()hay không - Khi kích hoạt câu lệnh
debuggerhai lần liên tiếp, tab bị crash, và yêu cầuPage.reloadcòn trong hàng đợi có thể được thực thi sau khi chuyển sang WebUI- Cách này không cần race condition nữa và hoạt động 100% reliable
- Trong bản vá lỗi trước đó, Google đã sửa để xóa pending debugger requests sau crash, nhưng để
Page.reloadlà ngoại lệinspectedWindow.reload()bên trong gửi yêu cầuPage.reload, nên chịu ảnh hưởng của ngoại lệ này- Bản vá khi đó không chặn được việc
Page.reloadcó thể thực thi script
- Ngoài cách dùng
debugger, cũng có thể làm tab crash bằng cách gây thiếu bộ nhớ, nhưng PoC cuối cùng dùng crashdebuggernhanh hơn
Exploit chain cuối cùng và tương tác người dùng
- PoC cuối cùng hoạt động theo thứ tự sau
- Dùng lỗ hổng
chrome.devtools.inspectedWindow.reload()để chạy payload JavaScript trongchrome://policy - Payload gọi
sendWithPromise("setLocalTestPolicies", policy)để thiết lập chính sách người dùng - Thiết lập
BrowserSwitcherEnabled,BrowserSwitcherUrlList,AlternativeBrowserPath,AlternativeBrowserParameters - Kích hoạt Browser Switcher bằng
window.open()hoặc điều hướng trang để thực thi lệnh shell
- Dùng lỗ hổng
- PoC dùng lệnh mở máy tính theo từng OS
- Windows:
C:\Windows\System32\cmd.exevàcalc.exe - Linux:
/bin/bashvàxcalc - macOS:
/bin/bashvàopen -na Calculator
- Windows:
- Tương tác người dùng chỉ ở mức khiến họ mở DevTools
- “extension install error” trong màn hình ví dụ là thủ thuật để lừa người dùng mở DevTools
- Khi DevTools được mở, chain dẫn tới sandbox escape bắt đầu
Bản vá của Google và việc gán CVE
- Sau khi nhận báo cáo, Google nhanh chóng xác nhận lỗ hổng và phân loại là P1/S1
- P1/S1 có nghĩa là high priority và high severity
- Trong vài tuần sau đó, ba bản sửa chính được áp dụng
- Thêm đối số
loaderIdcho lệnhPage.reloadvà kiểm traloaderIDphía renderer- Làm cho lệnh chỉ hợp lệ trong một origin duy nhất và không hoạt động ngay cả khi vô tình đến trang đặc quyền
- Kiểm tra URL trong hàm
inspectedWindow.reload()- Không còn chỉ dựa vào việc thu hồi quyền truy cập API tiện ích
- Kiểm tra trạng thái bật chính sách kiểm thử trong handler WebUI
- Chặn hoàn toàn chính sách kiểm thử
- Thêm đối số
- Lỗ hổng liên quan đến race condition được gán CVE-2024-5836
- Điểm CVSS severity là 8.8 High
- Lỗ hổng liên quan đến crash của trang được kiểm tra được gán CVE-2024-6778
- Lỗ hổng này cũng nhận điểm CVSS severity 8.8
- Sau khi bản sửa được merge vào release branch, hội đồng Chrome VRP quyết định phần thưởng, và tiền thưởng cuối cùng là $20,000
Lịch công bố và tài liệu
- Timeline như sau
- 16/4: phát hiện lỗi test policies
- 29/4: phát hiện lỗi race condition trong
inspectedWindow.reload() - 1/5: báo cáo lỗi cho Google
- 4/5: Google phân loại là P1/S1
- 5/5: phát hiện lỗi liên quan đến crash của trang được kiểm tra và cập nhật báo cáo
- 6/5: Google yêu cầu báo cáo lỗi riêng cho từng phần của chain
- 8/7: báo cáo lỗi được đánh dấu là fixed
- 13/7: chuyển tới hội đồng Chrome VRP để quyết định phần thưởng
- 17/7: hội đồng VRP quyết định tiền thưởng $20,000
- 15/10: toàn bộ báo cáo lỗi được công khai
- Có thể xem báo cáo lỗi gốc liên quan tại crbug.com/338248595
- PoC cho từng phần lỗ hổng được công khai trong kho GitHub
- Lỗi
inspectedWindow.reloadhoạt động ngược về tới Chrome v45 - Khi triển khai cho mọi người dùng những tính năng không được tài liệu hóa, chưa hoàn thiện và không an toàn, các sai sót đơn giản có thể kết hợp lại thành lỗ hổng có mức độ nghiêm trọng cao
1 bình luận
Ý kiến trên Hacker News
URL của trang được thay bằng
${url}, và nếu đặt nó sau#để không làm hỏng lệnh thì nó sẽ trở thành chú thích; vậy trong chính sách này có logic xác thực nào kiểu URL phải được truyền đến đâu đó trongAlternativeBrowserParameterskhông?Một học sinh trung học quan tâm đến lập trình, phát triển web và an ninh mạng — thật sự rất ấn tượng
Đạo đức nghề nghiệp khi tuân thủ cả quy trình công bố có trách nhiệm cũng rất đáng nể, có vẻ là người sẽ còn tiến xa
Bài viết và công việc đều xuất sắc; cảm giác như được cùng theo dõi quá trình sự phấn khích tăng dần khi các phát hiện nối tiếp nhau
Phần thưởng cũng hoàn toàn xứng đáng
Chuỗi khai thác lỗ hổng rất gọn gàng và bài viết cũng tuyệt vời. Tôi cũng thích cách tác giả tách nhỏ để cho thấy đoạn mã dễ tổn thương hoạt động ra sao
Những mẹo đơn giản như “Nhấn F12 để thử lại” lần nào thấy cũng khiến tôi thán phục, đúng là rất tinh nghịch
Tôi nhớ hồi trước từng dùng cùng API đó để debug shell
croshcủa Chrome OS, qua mặt cơ chế bảo vệ của OS và thậm chí lấy được quyền root trên thiết bị developer. Đó là CVE-2014-3172Tuy vậy, tác giả bài này đã phải vượt qua những rào cản khó hơn nhiều; thật sự là một công trình xuất sắc
Đã quá khuya nên khó đào sâu xem điều gì bị hỏng trong xác thực WebUI, nhưng tôi thích việc tác giả đã lần đến tận cùng để tìm ra
Việc nghi ngờ và không tin tưởng chuỗi công cụ của những thứ chúng ta triển khai là thái độ khá chuẩn, nhưng đồng thời chúng ta lại tin quá nhiều vào các công cụ phát triển tiện lợi như phép màu của những công ty lớn như Google hay Microsoft. Rốt cuộc là vì ta muốn viết và kiểm thử mã của mình, hơn là lo trong Chromium hay VSCode đang ẩn giấu thứ gì
Đây là một trong những bài viết xuất sắc nhất mà tôi từng đọc
Một công việc truy vết thật sự thông minh
Nỗ lực lục lọi mã trình duyệt để tìm ra đến mức này thật đáng nể, và bài viết cũng rất thú vị, chi tiết
Là học sinh trung học ư, wow, quá đỉnh
Dự án Chromium đã gỡ
chrome://net-internalsvì cho rằng nó quá phức tạp, rồi lại thêmchrome://policyvới hỗ trợ chỉnh sửa JSON mới hoàn thiện một nửa