1 điểm bởi GN⁺ 3 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • gokrazy/rsync là một triển khai rsync tối giản viết bằng Go, được rà soát dựa trên 12 lỗ hổng rsync được công bố vào tháng 1 năm 2025 và tháng 5 năm 2026
  • Kiểm tra biên của Go và việc khởi tạo 0 biến các lỗi tràn heap và rò rỉ thông tin ngăn xếp thành panic hoặc giá trị vô hại, nhưng panic vẫn có thể dẫn đến từ chối dịch vụ
  • Các vấn đề kiểu path traversal và TOCTOU lấy API tệp chống traversal như os.Root của Go 1.24 làm tuyến phòng thủ chủ chốt, và gokrazy/rsync đã chuyển đổi hoàn toàn sang đó
  • Chiến lược triển khai tối giản giúp tránh các lỗ hổng liên quan đến những tính năng chưa được triển khai như --inc-recursive, nén, --safe-links, proxy, ACL tên máy chủ, từ đó giảm bề mặt tấn công
  • Phần lớn lỗ hổng xuất phát từ thiếu kiểm tra hợp lệ và độ phức tạp quá mức, và với các trường hợp sử dụng đơn giản thì một triển khai đơn giản có thể phù hợp hơn

Bối cảnh và phạm vi

  • Vào tháng 1 năm 2025, nhiều nhà nghiên cứu bảo mật đã công bố tổng cộng 6 lỗ hổng bảo mật trong upstream Samba rsync, trong đó một số cho phép thực thi mã tùy ý và rò rỉ tệp
  • gokrazy/rsync, một triển khai tương thích và tối giản viết bằng Go, là đối tượng được xem xét chính để đánh giá liệu có thực sự tránh được các nhóm lỗ hổng này hay không
  • Đối tượng phân tích là 12 lỗ hổng gộp từ đợt tháng 1 năm 2025 và đợt tháng 5 năm 2026
  • Nếu đang chạy upstream rsync trong môi trường production, bạn cần nâng cấp lên 3.4.3 trở lên, và gokrazy/rsync cần được nâng lên v0.3.3 trở lên
  • gokrazy/rsync được viết để cung cấp các gói phần mềm của dự án nghiên cứu bản phân phối Linux distri trên router7, và router7 dựa trên nền tảng appliance Go gokrazy
  • Ban đầu chỉ có rsync server, nhưng hiện nay đã hỗ trợ mọi hướng và đang được dùng trên nhiều máy chủ gokrazy/rsync như chức năng rsync cơ bản có thể liên kết vào chương trình Go

Các lỗ hổng tháng 1 năm 2025

  • CVE-2024-12084: Tràn bộ đệm heap, mức độ nghiêm trọng 9.8

    • rsync đã so sánh độ dài checksum nhận từ mạng với MAX_DIGEST_LEN, nhưng cấu trúc dữ liệu nội bộ luôn dùng bộ đệm 16 byte char sum2[SUM_LENGTH]
    • MAX_DIGEST_LEN có thể lớn hơn khi biên dịch với hỗ trợ checksum SHA256 hoặc SHA512, nên kẻ tấn công có thể ghi tràn quá giới hạn bộ đệm sum2 tới 48 byte
    • Vấn đề được đưa vào từ commit ae16850 tháng 9 năm 2022, khi bổ sung hỗ trợ checksum SHA256/SHA512
    • Bản sửa upstream thay sum2 bằng sum2_array được cấp phát động, và sửa để cấp phát/kiểm tra theo xfer_sum_len, tức độ dài checksum của thuật toán truyền tải
    • Trong Go, việc kiểm tra biên sai sẽ không dẫn đến tràn bộ đệm heap mà gây panic do kiểm tra biên ở runtime
    • gokrazy/rsync cũng thiếu bước xác thực sum header, nhưng không phải nhầm lẫn kích thước; nếu đổi ChecksumLength thành 512 thì Go runtime sẽ phát sinh panic: runtime error: slice bounds out of range [:512] with length 16
    • Việc làm sập toàn bộ server không phải là chế độ lỗi mong muốn, nên đã bổ sung kiểm tra biên còn thiếu để đổi panic thành error
  • CVE-2024-12085: Rò rỉ thông tin trên stack dẫn đến vượt ASLR, mức độ nghiêm trọng 7.5

    • Do cùng thiếu sót xác thực như CVE-2024-12084, kẻ tấn công có thể chọn thuật toán checksum ngắn rồi tuyên bố đã gửi checksum dài hơn
    • Theo Google Security report, khi kết hợp tràn bộ đệm heap và rò rỉ thông tin, một client chỉ có quyền đọc ẩn danh cũng có thể thực thi mã tùy ý trên máy chủ rsync
    • hash_search() tạo digest chunk trong char sum2[MAX_DIGEST_LEN] trên stack rồi so sánh bằng memcmp(), nhưng bộ đệm stack cục bộ sum2 không được khởi tạo nên các byte còn dư có thể chứa nội dung stack
    • Client độc hại có thể rò rỉ 1 byte cho mỗi lần tải tệp, và dữ liệu rò rỉ có thể gồm con trỏ đối tượng heap, stack cookie, biến cục bộ, con trỏ biến toàn cục và return pointer
    • “Some checksum buffer fixes” đảm bảo s->s2length do kẻ tấn công điều khiển không thể lớn hơn độ dài checksum truyền tải, còn “prevent information leak off the stack” khởi tạo bộ nhớ sum2 về 0
    • Go khởi tạo mọi biến bằng zero value, nên không bị ảnh hưởng bởi lỗ hổng này; ngoài ra gokrazy/rsync triển khai giao thức phiên bản 27 chứ không phải phiên bản 30, nơi việc chọn checksum ngoài MD4 được đưa vào
  • CVE-2024-12087: Path traversal qua symbolic link, mức độ nghiêm trọng 7.5

    • Theo Google Security report, khi đồng bộ symbolic link được bật bằng -l hoặc -a (--archive), server độc hại có thể khiến client ghi tệp tùy ý ra ngoài thư mục đích
    • Cách tấn công là gửi nhiều danh sách tệp trong chế độ --inc-recursive; ở danh sách đầu, symlink được gửi như một thư mục, còn ở danh sách sau, cùng tên đó bị đổi thành symbolic link trỏ ra ngoài thư mục đích
    • Bản thân Go không thể ngăn lỗ hổng này, vì nguyên nhân là lỗi logic do không xác thực lại sau khi gộp nhiều danh sách tệp
    • Bản sửa upstream bổ sung bước xác thực còn thiếu
    • gokrazy/rsync không triển khai chế độ đệ quy tăng dần (--inc-recursive), nên không bị ảnh hưởng
    • incremental recursion cho phép xử lý theo kiểu “windowed” mà không cần quét toàn bộ tập tệp trước khi bắt đầu truyền, nhưng có sự đánh đổi giữa độ phức tạp triển khai và mức sử dụng tài nguyên
  • CVE-2024-12088: Vượt --safe-links, mức độ nghiêm trọng 7.5

    • Theo Google Security report, --safe-links là tính năng xác thực rằng symbolic link nhận từ server trỏ vào bên trong thư mục đích
    • Cách vượt qua xuất phát từ việc không xét đến khả năng có symbolic link khác nằm giữa đường dẫn đích của symbolic link
    • Ví dụ, nếu có {DESTINATION}/a -> .{DESTINATION}/foo -> a/a/a/a/a/a/../../ thì foo thực tế trỏ ra ngoài thư mục đích, nhưng unsafe_symlink() lại giả định a/ là thư mục nên kết luận là an toàn
    • Bản sửa upstream làm unsafe_symlink() nghiêm ngặt hơn bằng cách không cho phép ../ ở các vị trí ngoài phần đầu đường dẫn
    • Bản thân Go không thể ngăn một hàm xác thực sai, và gokrazy/rsync hiện chưa triển khai tính năng --safe-links nên không bị ảnh hưởng
  • CVE-2024-12086: Rò rỉ tệp tùy ý, mức độ nghiêm trọng 6.8

    • rsync receiver ở chế độ client đã không chuẩn hóa tên tệp do rsync sender cung cấp, và không ngăn được việc mở tệp ngoài cây thư mục đích
    • Sender độc hại có thể chỉ đạo receiver so sánh checksum của tệp tùy ý ngoài cây thư mục đích, rồi quan sát phản hồi của receiver với checksum 1 byte để rò rỉ tệp tùy ý
    • Theo Google Security report, khi client kết nối tới server độc hại, server có thể rò rỉ nội dung của các tệp tùy ý trên máy của client
    • Bản sửa upstream xác thực đường dẫn do sender cung cấp để ngăn mở tệp ngoài cây thư mục đích
    • Go có API có thể ngăn điều này, và biện pháp phòng vệ liên quan được nêu ra là os.Root của Go
    • gokrazy/rsync không dễ bị ảnh hưởng vì triển khai giao thức phiên bản 27 chứ không phải phiên bản 29, nơi tính năng fuzzy matching được đưa vào
  • CVE-2024-12747: Điều kiện tranh chấp symbolic link, mức độ nghiêm trọng 5.6

    • Theo Red Hat Security Advisory, đây là lỗi phát sinh từ điều kiện tranh chấp trong quá trình xử lý symbolic link của rsync
    • Hành vi mặc định của rsync là bỏ qua khi gặp symbolic link, nhưng kẻ tấn công có thể thay một tệp thường thành symbolic link đúng thời điểm để vượt qua hành vi mặc định và khiến symbolic link được duyệt qua
  • Bản sửa upstream thay đổi để sender của rsync sử dụng tùy chọn O_NOFOLLOW trong lời gọi open()

    • gokrazy/rsync dễ bị ảnh hưởng cho đến commit 1b1fbf6, và commit này đưa vào biện pháp giảm thiểu O_NOFOLLOW giống như rsync upstream
    • Damien Neil cho rằng bản vá CVE-2024-12747 của gokrazy là chưa đủ, và nhận định rằng O_NOFOLLOW chỉ ngăn việc đi theo symbolic link ở thành phần đường dẫn cuối cùng
    • Ví dụ, với os.Open("dir/passwd"), nếu thành phần đường dẫn trước đó là dir bị thay bằng symbolic link trỏ tới /etc thì vẫn có thể vượt qua
    • Vấn đề này đã được báo cho đầu mối bảo mật của rsync vào tháng 4/2025, và dẫn tới CVE-2026-29518 được công bố vào 2026-05-20

Các lỗ hổng tháng 5 năm 2026

  • CVE-2026-29518: Điều kiện tranh chấp symbolic link, mức độ nghiêm trọng 7.0

    • Theo mục NEWS của rsync 3.4.3, đây là điều kiện tranh chấp symbolic link kiểu TOCTOU cho phép leo thang đặc quyền cục bộ trong daemon mode khi không dùng chroot
    • Daemon rsync được cấu hình use chroot = no sẽ bị phơi nhiễm với tranh chấp time-of-check/time-of-use trên thành phần đường dẫn cha
    • Kẻ tấn công cục bộ có quyền ghi vào mô-đun có thể thay thành phần thư mục cha bằng symbolic link giữa lúc receiver kiểm tra và lúc open(), gây lộ basis-file khi đọc và ghi đè tệp ngoài module khi ghi
    • Mặc định use chroot = yes không bị ảnh hưởng
    • Bản sửa upstream dùng secure_relative_open(), tương tự API os.Root của Go
    • gokrazy/rsync từng dễ bị ảnh hưởng cho đến khi chuyển senderreceiver sang API os.Root chống traversal
  • CVE-2026-43618: Tràn số nguyên gây rò rỉ bộ nhớ từ xa, mức độ nghiêm trọng 8.1

    • Bộ giải mã token nén của rsync receiver cộng dồn bộ đếm signed 32-bit mà không kiểm tra tràn, cho phép sender độc hại làm lộ nội dung bộ nhớ tiến trình
    • Dữ liệu rò rỉ có thể gồm biến môi trường, mật khẩu, con trỏ heap và thư viện, làm suy yếu ASLR và giúp các khai thác tiếp theo dễ thực hiện hơn
    • Phạm vi ảnh hưởng là các kết nối daemon đã xác thực khi nén được bật; với giao thức từ phiên bản 30 trở lên, tính năng này được bật mặc định nếu cả hai peer đều quảng bá hỗ trợ nén
    • Cách giảm thiểu là tắt nén daemon trong rsyncd.conf bằng refuse options = compress
    • Bản sửa upstream bổ sung các kiểm tra còn thiếu
    • gokrazy/rsync không triển khai nén nên không bị ảnh hưởng; dù hỗ trợ nén có vẻ đơn giản, các lý do không hề tầm thường đã được tổng hợp trong gokrazy/rsync issue #35
  • CVE-2026-43620: Từ đọc ngoài phạm vi đến từ chối dịch vụ, mức độ nghiêm trọng 6.5

    • Sự cố này xảy ra vì guard parent_ndx<0 được thêm vào send_files() trong năm 2025 nhưng không được áp dụng cho khối recv_files() trông giống hệt về mặt thị giác
    • Máy chủ rsync độc hại có thể gửi cờ tương thích CF_INC_RECURSE cùng flist được tạo sai, khiến receiver đọc và dereference dir_flist->files[-1], dẫn tới SIGSEGV có thể tái hiện chắc chắn
    • Phạm vi ảnh hưởng là mọi client rsync thực hiện pull thông thường từ URL do kẻ tấn công kiểm soát; không cần tùy chọn đặc biệt phía nạn nhân do inc_recurse là mặc định từ giao thức 30 trở lên
    • Biện pháp giảm thiểu ở client là dùng --no-inc-recursive, và bản sửa upstream thêm guard parent_ndx<0 vào cả recv_files()
    • gokrazy/rsync không triển khai chế độ đệ quy tăng dần --inc-recursive nên cũng như CVE-2024-12087, nó không bị ảnh hưởng
  • CVE-2026-43619: Điều kiện tranh chấp symbolic link bổ sung, mức độ nghiêm trọng 6.3

    • Bản vá cho điều kiện tranh chấp symbolic link ở lời gọi open() của receiver (CVE-2026-29518) không được áp dụng cho các system call dựa trên đường dẫn khác như chmod, lchown, utimes, rename, unlink, mkdir, symlink, mknod, link, rmdir, lstat
    • Trên daemon rsync được cấu hình use chroot = no, kẻ tấn công cục bộ có thể thay thành phần thư mục cha bằng symbolic link giữa lúc receiver kiểm tra và lúc system call diễn ra, để chuyển hướng ra ngoài module được export
    • Mặc định use chroot = yes không bị ảnh hưởng
    • Bản sửa upstream xử lý các system call dựa trên đường dẫn bị ảnh hưởng thông qua dirfd của thư mục cha đã mở, dưới các ràng buộc do kernel cưỡng chế như openat2 trên Linux 5.6+, O_RESOLVE_BENEATH trên FreeBSD 13+ và macOS 15+, hoặc traversal từng thành phần với O_NOFOLLOW ở các môi trường khác
    • gokrazy/rsync không bị ảnh hưởng vì dùng xuyên suốt API os.Root của Go
  • CVE-2026-43617: Vượt ACL dựa trên hostname, mức độ nghiêm trọng 4.8

    • Trong daemon rsync có thiết lập rsyncd.conf toàn cục daemon chroot = /X, tra cứu DNS ngược của client kết nối được thực hiện sau khi daemon đã chroot vào /X
    • Nếu trong /X không có /etc/resolv.conf, /etc/nsswitch.conf, /etc/hosts và các mô-đun dịch vụ NSS cần cho việc phân giải của glibc, việc tra cứu sẽ thất bại và hostname kết nối bị đặt thành "UNKNOWN"
    • Các quy tắc deny dựa trên hostname như hosts deny = *.evil.example sẽ không khớp, cho phép kẻ tấn công kiểm soát bản ghi PTR kết nối từ hostname mà quản trị viên định chặn
    • ACL dựa trên IP không bị ảnh hưởng, và thiết lập use chroot theo từng module không liên quan tới lỗ hổng này
    • Bản sửa upstream chuyển tra cứu DNS lên sớm hơn trong giao thức
    • gokrazy/rsync không bị ảnh hưởng vì không triển khai danh sách allow/deny dựa trên hostname mà chỉ triển khai allow/deny dựa trên IP
  • CVE-2026-45232: Ghi ngoài phạm vi stack, mức độ nghiêm trọng 3.1

    • Hỗ trợ proxy HTTP CONNECT của client rsync có lỗi ghi ngoài phạm vi stack kiểu off-by-one trong establish_proxy_connection()
    • Nếu proxy hoặc kẻ tấn công trung gian trả về hơn 1023 byte mà không có '\n' ở dòng phản hồi đầu tiên, đoạn mã tiếp theo có thể ghi '\0' ngay sau buffer 1024 byte, làm hỏng vùng stack liền kề
    • AddressSanitizer báo cáo stack-buffer-overflow tại socket.c:95 trong frame establish_proxy_connection
    • Bản sửa upstream xác thực dữ liệu do kẻ tấn công cung cấp
    • gokrazy/rsync không triển khai hỗ trợ proxy như vậy nên không dễ bị ảnh hưởng

Go và gokrazy/rsync thực sự đã ngăn chặn được gì

  • Kiểm tra biên của runtime Go biến các vấn đề bảo mật nghiêm trọng hơn thành panic; panic vẫn là rủi ro từ chối dịch vụ nhưng được xem là chế độ lỗi tốt hơn
  • Go khởi tạo bộ nhớ về 0, khiến các rò rỉ thông tin như CVE-2024-12085 trở nên bất khả thi
  • API os.Root của Go ngăn chặn phần lớn các lỗ hổng còn lại
  • Trong 12 lỗ hổng, chỉ có CVE-2026-43617 được phân loại là lỗi logic ứng dụng mà việc dùng Go không thể ngăn chặn
  • Khác biệt cốt lõi giữa gokrazy/rsync và rsync upstream chính thức, ngoài việc được viết bằng Go, còn là triển khai được tối giản hóa
  • gokrazy/rsync không triển khai nhiều tính năng từng gây ra vấn đề như --inc-recursive, --safe-links, nén, proxy, ACL tên máy chủ, nên tránh được nhiều lỗ hổng
  • Giống như mọi triển khai rsync tương thích wire protocol, gokrazy/rsync nhắm tới giao thức phiên bản 27; các phiên bản giao thức sau đó đưa vào độ phức tạp đáng kể
  • Trạng thái của gokrazy/rsync theo từng CVE tại thời điểm bài viết được đăng:
    • 2024-12084: có triển khai và gây panic
    • 2024-12085: là tính năng của giao thức 30 nên không triển khai, không bị ảnh hưởng
    • 2024-12086: là tính năng của giao thức 29 nên không triển khai, không bị ảnh hưởng
    • 2024-12087: không triển khai inc-rec, không bị ảnh hưởng
    • 2024-12088: không triển khai safe-links, không bị ảnh hưởng
    • 2024-12747: có triển khai và từng bị ảnh hưởng
    • 2026-29518: có triển khai và đã được vá
    • 2026-43617: không triển khai danh sách deny máy chủ, không bị ảnh hưởng
    • 2026-43618: không triển khai nén, không bị ảnh hưởng
    • 2026-43619: có triển khai và đã được vá
    • 2026-43620: không triển khai inc-rec, không bị ảnh hưởng
    • 2026-45232: không triển khai proxy, không bị ảnh hưởng
  • Tất cả lỗ hổng đã biết của gokrazy/rsync đều đã được sửa; trạng thái trên phản ánh tình trạng tại thời điểm từng CVE được công bố
  • Vào thời điểm công bố lỗ hổng tháng 1/2025, gokrazy/rsync gây panic ở CVE-2024-12084 và dễ bị ảnh hưởng bởi điều kiện tranh chấp TOCTOU của CVE-2024-12747
  • Trong quá trình sửa lỗi TOCTOU, CVE-2026-29518 được phát hiện và đã được sửa trong gokrazy/rsync trước khi CVE đó được công bố
  • CVE-2026-43619 được phát hiện muộn hơn, nhưng đã được khắc phục sẵn trong gokrazy/rsync bằng cùng bản sửa là sử dụng toàn diện os.Root của Go

Phân biệt vai trò và cách đặt tên lỗ hổng

  • Nhiều báo cáo lỗ hổng dùng các thuật ngữ “server” và “client”, nhưng trong một lần truyền rsync, cả phía client lẫn server của rsync đều có thể đóng vai trò người gửi (sender, tải tệp lên) hoặc người nhận (receiver, tải tệp xuống)
  • Ở chế độ daemon, có thể giới hạn quyền truy cập hệ thống tệp vào đường dẫn mô-đun được cấu hình trước, nhưng ở command mode thì không thể
  • Có thể xem 4 cấu hình cùng các lớp vai trò/giao thức trong sơ đồ rsync combinations
  • Tiêu đề gốc của lỗ hổng Arbitrary File Leak (CVE-2024-12086), “Server leaks arbitrary client files”, rất dễ gây hiểu nhầm
  • Cách diễn đạt chính xác hơn là bên nhận rsync làm rò rỉ tệp tùy ý cho bên gửi độc hại
  • Một client độc hại ở vai trò sender có thể, trong các môi trường như command mode qua SSH, buộc rsync từ xa chưa vá mở các tệp nằm ngoài cây đích, ví dụ cơ sở dữ liệu mật khẩu hệ thống /etc/shadow
  • Trong chế độ daemon, server bật thêm chuẩn hóa đường dẫn (path sanitization) để chặn cuộc tấn công này
  • Lỗ hổng Symlink Path Traversal (CVE-2024-12087) cũng thường được mô tả là do “malicious server”, nhưng chính xác hơn thì đó là bên gửi độc hại, có thể là client hoặc server

So sánh với OpenBSD openrsync

  • Dự án OpenBSD nổi tiếng với trọng tâm bảo mật; openrsync xác thực độ dài checksum và chỉ hỗ trợ MD4 cho kích thước/thuật toán checksum, nên không bị ảnh hưởng bởi Heap Buffer Overflow (CVE-2024-12084) và Stack Info Leak (CVE-2024-12085)
  • openrsync, giống như gokrazy/rsync, không triển khai các tính năng liên quan nên không bị ảnh hưởng bởi CVE-2024-12086, CVE-2024-12087 và CVE-2024-12088
  • Ngay cả khi có bị ảnh hưởng, khi chạy trên OpenBSD thì phòng thủ theo chiều sâu nhờ OpenBSD unveil(2)pledge(2) để hạn chế truy cập hệ thống tệp có thể đã ngăn việc khai thác thành công
  • openrsync không bị ảnh hưởng bởi CVE-2024-12747 vì ngay từ lúc triển khai hỗ trợ symbolic link, nó đã dùng O_NOFOLLOW
  • Tuy nhiên, O_NOFOLLOW không phải là bản sửa đủ cho vấn đề này, nên openrsync vẫn bị ảnh hưởng bởi CVE-2026-29518
  • Gói lỗ hổng tháng 5/2026 cũng tương tự ở chỗ phần lớn không liên quan vì các tính năng tương ứng không được triển khai
  • openrsync tránh được tác động của phần lớn các lỗ hổng được báo cáo nhờ triển khai xác thực cẩn thận, giới hạn bề mặt tấn công và áp dụng phòng thủ theo chiều sâu

Phòng thủ theo chiều sâu và os.Root

  • Linux mount namespace

    • Chỉ vài tuần sau khi bắt đầu dự án gokrazy/rsync, đã bổ sung tính năng hỗ trợ hạ quyền và sử dụng mount/pid namespace trên Linux để hạn chế quyền truy cập tới các đối tượng hệ thống tệp
    • Cách tiếp cận này hoạt động tốt để giảm thiểu tấn công duyệt đường dẫn, nhưng cần đặc quyền nên phải chạy bằng root hoặc chạy trong Linux user namespace nếu được bản phân phối/hệ thống bật
    • mount namespace phù hợp cho cấu hình máy chủ, nhưng thường không dùng được cho các lần truyền tương tác một lần chạy dưới tài khoản người dùng thông thường
  • Hardening với systemd

    • Cùng commit đã đưa vào hỗ trợ mount/pid namespace trên Linux cũng bao gồm tệp dịch vụ systemd giới hạn quyền truy cập hệ thống tệp vào thư mục home, và README khuyến nghị tiếp tục siết chặt quyền truy cập hệ thống tệp tùy theo trường hợp sử dụng
    • Nếu các giới hạn hệ thống tệp này được cấu hình đúng, chúng sẽ giảm thiểu các lỗ hổng File Leak (CVE-2024-12086) và Path Traversal (CVE-2024-12087)
    • Symlink Race Condition (CVE-2024-12747) phụ thuộc vào việc leo thang đặc quyền thông qua tiến trình rsync, nhưng nhờ tính năng DynamicUser, tiến trình này có ít quyền hơn các người dùng khác
    • Tương tự mount namespace, cách này tốt cho cấu hình máy chủ nhưng khá phiền khi thiết lập cho nhu cầu tương tác, dùng một lần
  • Linux Landlock

    • Bài viết Porting OpenBSD pledge() to Linux (2022) gợi nhắc rằng Linux cũng có Landlock API, một cơ chế kiểm soát truy cập theo tiến trình, không cần đặc quyền, tương tự unveil(2) của OpenBSD
    • Ý tưởng cơ bản là khi chương trình đã biết thư mục sẽ làm việc, nó có thể gọi như unveil("/home/michael/backups", "rw"); để không còn truy cập được tới các vị trí hệ thống tệp ngoài đường dẫn đó
    • Tháng 3 năm 2025, đã triển khai hỗ trợ Landlock để hạn chế quyền truy cập hệ thống tệp
    • Sau khi cấu hình xong, ngay cả các lần chạy gokrazy/rsync không đặc quyền cũng có thể giới hạn truyền rsync chỉ đọc ở nguồn và đọc-ghi ở thư mục đích
    • Nhược điểm là Landlock hoạt động ở cấp tiến trình
    • Chính sách Landlock cũng phải bao gồm các tệp mà chương trình cần; ví dụ gokrazy/rsync phải đọc được /etc/passwd để tra cứu ID người dùng, nên nếu kẻ tấn công nhắm vào /etc/passwd thì Landlock sẽ không giúp ích
  • os.Root của Go

    • Tháng 2 năm 2025, Go 1.24 giới thiệu API os.Root có khả năng chống duyệt đường dẫn; phần bối cảnh liên quan được Damien Neil tổng hợp trong The Go Blog: Traversal-resistant file APIs
    • So với Landlock, os.Root cung cấp khả năng kiểm soát chi tiết hơn theo từng thao tác hệ thống tệp
    • Go 1.25, phát hành vào tháng 8 năm 2025, bổ sung thêm nhiều phương thức cho os.Root, khiến nó trở thành lựa chọn thuận tiện cho phần lớn nhu cầu dùng hệ thống tệp
    • Mọi chỗ sử dụng hệ thống tệp trong gokrazy/rsync đã được chuyển sang os.Root, rất phù hợp với mô hình trong đó người dùng cấu hình thư mục đầu vào/đầu ra nhưng tên tệp nhận qua mạng thì không thể tin cậy
    • Ngay cả các system call tưởng như không thể tạo trực tiếp qua API, như mknod(2), cũng có thể được dùng an toàn
    • Damien Neil giải thích rằng có thể mở thư mục cha của đích bằng os.Root.OpenFile, lấy file descriptor của thư mục đó qua File.Fd, rồi tạo tệp bằng golang.org/x/sys/unix#Mknodat
    • Ví dụ dùng thực tế có trong internal/receiver/generatormknod_linux.go, dòng 15-29
    • Linux có mknodat(2), nhưng tính đến Linux 7.0 thì chưa có bindat tương ứng với bind(2)
    • Lennart Poettering đề xuất rằng khi không có bindat, có thể bind vào /proc/self/<fd>/foobar như một mẹo để bỏ qua việc phân giải đường dẫn
    • Nếu sau /proc/self/<fd> an toàn đã biết, bạn chỉ chỉ định basename, tức thành phần cuối cùng của đường dẫn, chứ không phải cả đường dẫn, thì việc phân giải đường dẫn sẽ bị bỏ qua; mã liên quan nằm ở dòng 49-56
    • Nhờ hai mẹo này, từ gokrazy/rsync v0.3.1 trở lên đã dùng os.Root một cách hoàn chỉnh, và mọi truy cập hệ thống tệp đều có tính an toàn trước duyệt đường dẫn

Bài học cốt lõi

  • Ngoại trừ các lỗ hổng TOCTOU (CVE-2024-12747, CVE-2026-29518, CVE-2026-43619), mọi lỗ hổng còn lại đều bắt nguồn từ việc thiếu kiểm tra đầu vào hoặc kiểm tra đầu vào sai cách
  • Trong ba trường hợp hoàn toàn không có kiểm tra, còn CVE-2024-12088 là trường hợp chủ đề phân giải đường dẫn hệ thống tệp quá phức tạp khiến phần kiểm tra hiện có không bao quát hết mọi trường hợp biên
  • Bản sửa ở mức cấu trúc có giá trị nhất là cung cấp các cơ chế kiểm tra luôn bật như kiểm tra biên, cùng các API an toàn theo mặc định như os.Root của Go
  • Một số lỗ hổng phát sinh trong quá trình tiến hóa của giao thức rsync, khi tính năng mới được thêm vào đoạn mã vốn trước đó kiểm tra đầy đủ nhưng phần kiểm tra không được cập nhật đúng cách
  • Đàm phán thuật toán checksum và đệ quy gia tăng được thêm vào ở phiên bản giao thức 30, nhưng phần kiểm tra cũ không được cập nhật để phù hợp với cách xử lý của các tính năng mới
  • gokrazy/rsync và openrsync không bị ảnh hưởng bởi 8 trong số 12 lỗ hổng bảo mật chỉ vì chúng không triển khai các tính năng dễ tổn thương đó
  • Những tính năng này đã được thêm vào rsync vì ở một thời điểm nào đó chúng có giá trị với ai đó; điều đó không có nghĩa là nên ngừng phát triển phần mềm
  • Lựa chọn lý tưởng là dùng một implementation có độ phức tạp phù hợp và tương xứng với độ phức tạp của trường hợp sử dụng: chọn implementation đơn giản cho nhu cầu đơn giản, và chỉ chọn implementation đầy đủ tính năng khi thực sự cần

1 bình luận

 
Ý kiến trên Lobste.rs
  • Bài viết rất hay. Hay đến mức mọi ngôn ngữ đều nên đưa API như os.Root vào thư viện chuẩn
    Sau khi gặp vài lần lỗ hổng path traversal trong SecureDrop, tôi đã dùng một phiên bản được đơn giản hóa rất nhiều, và giờ nếu dùng đúng API thì có thể loại bỏ trọn vẹn cả một lớp lỗ hổng
    • Đúng vậy. Nhân vật chính của bài blog này dường như không phải Go mà là os.Root