1 điểm bởi GN⁺ 2024-10-18 | 1 bình luận | Chia sẻ qua WhatsApp
  • 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ạy injectedScript, 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ầu Page.reload cò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 loaderId cho Page.reload, kiểm tra URL trong inspectedWindow.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-5836CVE-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 .exe trong chrome://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 policiesuser 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/test cho 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/test sẽ chuyển hướng sang chrome://policy

Lỗi xác thực setLocalTestPolicies

  • Mã JavaScript của chrome://policy/test dùng sendWithPromise('setLocalTestPolicies', ...) để thiết lập chính sách kiểm thử
    • sendWithPromise() là wrapper của API riêng tư WebUI chrome.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 setLocalTestPolicies từ console của chrome://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ư AllowDinosaurEasterEgg vớ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 HandleSetLocalTestPolicies phía C++ chỉ kiểm tra sự tồn tại của local_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ọi IsPolicyTestingEnabled(nullptr, channel)
    • Đối số đầu tiên pref_servicenull, nên bước kiểm tra PolicyTestPageEnabled bị bỏ qua
    • Kiểm tra còn lại chỉ là release channel có phải CANARY hoặc DEFAULT hay không
  • Trong bản dựng Chromium không gắn thương hiệu, mã GOOGLE_CHROME_BRANDING khô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

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 AlternativeBrowserPathAlternativeBrowserParameters có 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 BrowserSwitcherEnabled thành true
    • Thêm example.com vào BrowserSwitcherUrlList
    • Trên Linux, đặt AlternativeBrowserPath thành /bin/bash
    • Đặt AlternativeBrowserParameters như ["-c", "xcalc # ${url}"]
  • 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
  • Sau khi thiết lập chính sách trong chrome://policy, gọi window.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.scripting
    • chrome.tabs của Manifest v2
    • chrome.debugger
    • chrome.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.devtools phải có trường devtools_page trong 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
  • 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 trang about:blank do WebUI mở, có thể thực thi JavaScript trên trang đặc quyền
    • Bản thân URL about:blank không đặc biệt, nhưng nó kế thừa quyền hạn và origin của trang đã mở nó
    • about:blank do chrome://settings mở là trang đặc quyền có origin chrome://settings
  • 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:blank khó dùng trực tiếp trong exploit chain vì chrome://policy không mở popup about:blank
  • Tuy nhiên, ngay cả trong tình huống inspectedWindow.eval() thất bại, inspectedWindow.reload() vẫn chạy JavaScript trong chrome://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òn reload() thì không có kiểm tra cùng mức

Ổ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ới chrome://policy và 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 debugger hai lần liên tiếp, tab bị crash, và yêu cầu Page.reload cò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.reload là ngoại lệ
    • inspectedWindow.reload() bên trong gửi yêu cầu Page.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.reload có 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 crash debugger nhanh 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 trong chrome://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
  • PoC dùng lệnh mở máy tính theo từng OS
    • Windows: C:\Windows\System32\cmd.execalc.exe
    • Linux: /bin/bashxcalc
    • macOS: /bin/bashopen -na Calculator
  • 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

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.reload hoạ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

 
GN⁺ 2024-10-18
Ý 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 đó trong AlternativeBrowserParameters khô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

    • Tài năng kỹ thuật đáng kinh ngạc, sự bền bỉ, cùng năng lực viết tài liệu và giao tiếp đều xuất sắc
      Đạ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 sống ở Missouri; trước đây tôi từng nhấn F12 một lần và thống đốc đã định bắt tôi
  • Tôi nhớ hồi trước từng dùng cùng API đó để debug shell crosh củ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-3172
    Tuy 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-internals vì cho rằng nó quá phức tạp, rồi lại thêm chrome://policy với hỗ trợ chỉnh sửa JSON mới hoàn thiện một nửa