Lạm dụng hạ tầng Go
(reverse.put.as)- Checksum database của Go và public module proxy có thể chấp nhận cả những kho lưu trữ không có mã Go, tạo ra một đường đi để nạp dữ liệu từ kho Git tùy ý vào hạ tầng Go rồi tải xuống lại
- Yêu cầu
sum.golang.org/lookup/$module@$versionsẽ lấy từ máy chủ gốc khi gặp phiên bản module chưa được ghi vào log, và trong quá trình đóproxy.golang.orgcũng sẽ cung cấp file zip của kho lưu trữ tương ứng - Kho Ruby của Homebrew và một fork kho Rust đã xuất hiện trong checksum database; thí nghiệm cũng thành công trong việc đăng ký bằng pseudo-version không chỉ module Go mới mà cả các kho không có file Go
- File zip module bị giới hạn tối đa 500 MiB ở cả dạng nén và giải nén, nhưng vẫn đủ lớn để vượt hạn chế tải xuống trên máy dev/CI/CD, lưu payload hoặc triển khai C2
- Trong khoảng 1,59 triệu đường dẫn duy nhất của
sum.golang.org, có khoảng 1,51 triệu là đường dẫn GitHub, tương đương 95%, cho thấy одновременно sự phụ thuộc của hệ sinh thái Go vào GitHub và khả năng lạm dụng public proxy
Các kho không phải Go được tìm thấy trong Go checksum database
- Khi xem checksum database của Go, người ta phát hiện
github.com/homebrew/homebrew-corexuất hiện rất nhiều trong bảngmodulesgithub.com/homebrew/homebrew-core: 39,438 mụcgithub.com/Homebrew/homebrew-core: 30,896 mụcgithub.com/concourse/concourse: 25,372 mụcgithub.com/openshift/release: 24,065 mụcgithub.com/cilium/cilium: 22,138 mục
- Kho Homebrew được biết là dùng Ruby, và cả trong kho lẫn các file sau khi clone đều không tìm thấy
go.modhay mã nguồn Go - Sự khác biệt chữ hoa/chữ thường được giải thích bằng quy tắc case encoding trong tài liệu của Go
- Chữ hoa được mã hóa thành
!cộng với chữ thường tương ứng, để có thể lưu đồng thờiexample.com/Mvàexample.com/mngay cả trên hệ thống tệp không phân biệt hoa thường
- Chữ hoa được mã hóa thành
github.com/Edu4rdSHL/rust-headless-chromecũng xuất hiện trong checksum database dù đây là một fork kho Rust không liên quan đến Go
Cách /lookup lấy kho lưu trữ
- Theo Go Modules Reference trong tài liệu module Go, khi truy vấn checksum database, lệnh Go trước tiên lấy dữ liệu bản ghi từ endpoint
/lookup - Nếu phiên bản module vẫn chưa được ghi vào log, checksum database sẽ thử lấy module đó từ máy chủ gốc trước khi phản hồi
- Định dạng endpoint là
$base/lookup/$module@$version- Nó trả về số bản ghi log cho
$versioncủa$module, dònggo.sum, và mô tả cây đã ký
- Nó trả về số bản ghi log cho
- Khi truy vấn pseudo-version của
github.com/homebrew/homebrew-core, hệ thống trả về checksum record và hash củago.mod - Nếu kho không có tag phiên bản, quy tắc pseudo-version của Go sẽ được sử dụng
Thử nghiệm đăng ký module Go mới
- Sau khi tạo module Go mới
github.com/gdbinit/fluxmatter, người ta dùng yêu cầulookupđể kiểm tra việc đăng ký - Truy vấn
@latesttrả về lỗi cho biết đây không phải canonical versionbad request: version "latest" is not canonical
- Truy vấn
@v0.0.0trả về lỗi không biết revision đónot found: ... invalid version: unknown revision v0.0.0
- Nhưng sau khi đồng bộ lại checksum database và truy vấn, module đã được đăng ký
github.com/gdbinit/fluxmatter|v0.0.0-20240524163826-a7e64ffd69f2|2024-05-24T16:40:51.203837Z
proxy.golang.org/github.com/gdbinit/fluxmatter/@latesttrả về pseudo-version và thông tin nguồn gốc GitHub; file zip của phiên bản đó cũng có thể tải về và xác minh nén thành công- Ở bước gieo dữ liệu ban đầu, thậm chí không cần chỉ định chính xác phiên bản; chỉ một truy vấn lookup chứa đường dẫn module và giá trị trông giống phiên bản là đã hoạt động
Cả kho không có mã Go cũng được nạp vào public proxy
- Thí nghiệm tương tự cũng thành công với kho
github.com/gdbinit/readmemhoàn toàn không có mã Go - Yêu cầu
lookuptrả về lỗi không biết revisionv0.0.0, nhưng checksum database vẫn đăng ký kho này dưới dạng pseudo-versiongithub.com/gdbinit/readmem|v0.0.0-20131006075740-407cb0a56933|2024-05-24T16:45:35.88456Z
@latesttrênproxy.golang.orgtrả về pseudo-version của kho đó cùng thông tin nguồn gốc Git- Trong file zip tải xuống không có file Go mà có
Entitlements.plist,README, file project Xcode,main.cv.v. - Thí nghiệm này dùng kho GitHub, nhưng nếu VCS hoạt động thì có thể cũng thực hiện được với các nền tảng hosting khác
Mức độ phụ thuộc vào GitHub và giới hạn kích thước
- Số đường dẫn duy nhất trong checksum database là 1,591,375, trong đó các đường dẫn
github.com%là 1,515,957 - Khoảng 95% đường dẫn duy nhất được host trên GitHub {p:95}
- Đây là thống kê thô, chưa loại bỏ fork hay các đối tượng không phải mã Go thật sự
- Các ràng buộc về đường dẫn và kích thước file được áp dụng cho file zip module Go
- File zip module tối đa 500 MiB
- Tổng kích thước file sau giải nén cũng tối đa 500 MiB
- File
go.modtối đa 16 MiB - File
LICENSEcũng tối đa 16 MiB
- Các giới hạn này nhằm giảm thiểu tấn công từ chối dịch vụ đối với người dùng, proxy và các phần khác của hệ sinh thái module
- Xét về khả năng bị lạm dụng, 500 MiB vẫn là mức đủ lớn
Các kịch bản lạm dụng có thể xảy ra
- Public Go proxy có thể được dùng để vượt qua hạn chế tải xuống theo đích đến trên máy dev hoặc máy chủ CI/CD
- Giả định không có
GOPROXYriêng tư - Mã độc có thể tải payload lên kho rồi khi cần sẽ tải xuống từ proxy
- Ngay cả khi mã nguồn gốc biến mất, entry trong checksum database có thể vẫn chỉ còn lại một dấu vết nhỏ
- Giả định không có
- Việc DoS
proxy.golang.orgcó thể không dễ thực hiện- Có thể yêu cầu proxy tải xuống các kho Git tùy ý
- Một kiểu tấn công khả dĩ là thu thập nhiều URL GitHub rồi gửi thật nhiều yêu cầu tới API
lookup - Không rõ triển khai phía máy chủ, nhưng có thể tồn tại giới hạn xử lý song song như work queue
- Cơ chế bảo vệ băng thông từ phía GitHub cũng có thể phát huy tác dụng
- Cũng có thể có khả năng DoS vào không gian lưu trữ, nhưng mới chỉ là suy đoán
- Có thể dễ dàng dựng C2 (command and control) trên
proxy.golang.org- Truy vấn
@latestcó thể dùng để tìm phiên bản mới nhất của một module cụ thể - Payload có thể là file đơn giản, hoặc được giấu trong
go.modhay mã nguồn Go - Nếu muốn tránh dùng một kho duy nhất, có thể dùng module DGA
- Truy vấn
Luồng tải xuống cho C2
- Để nhận lệnh, implant chỉ cần thực hiện các bước sau
- Gửi yêu cầu
https://proxy.golang.org/module_path/@latest - Trích pseudo-version hoặc version từ kết quả JSON
- Gửi yêu cầu
https://proxy.golang.org/module_path/@v/version.zipđể tải file zip - Giải nén nội dung zip rồi phân tích lệnh
- Gửi yêu cầu
- Luồng này đơn giản đến mức có thể triển khai bằng dưới 300 dòng mã Go
Kết luận và các câu hỏi còn bỏ ngỏ
- Checksum database và proxy của Go, theo đúng quy trình được tài liệu mô tả, có thể lấy và lưu các module chưa được ghi log từ máy chủ gốc
- Tình trạng hiện tại có vẻ chưa phải là vấn đề nghiêm trọng của hạ tầng Go, nhưng vẫn có thể bị lạm dụng dễ dàng và còn chỗ để cải thiện
- Có thể có lý do đã được ghi nhận trong tài liệu hoặc chưa công khai cho việc cho phép các kho không phải mã Go được đưa lên proxy và checksum database
- Để xác minh liệu đã có ai lạm dụng điều này hay chưa, cần khảo sát khoảng 1,6 triệu kho duy nhất và khoảng 22 triệu entry theo cơ sở dữ liệu cục bộ mới nhất
- Vì sao một số dự án non-Go hợp lệ lại có trong database vẫn là câu hỏi còn bỏ ngỏ
1 bình luận
Các ý kiến trên Hacker News
Một dịch vụ trực tuyến cho phép người dùng tải dữ liệu lên và dữ liệu đó có vẻ được công khai rốt cuộc sẽ bị dùng cho điều khiển-lệnh, vi phạm bản quyền và hosting CSAM
Điều này đặc biệt đúng với các dịch vụ khó chặn vì ngoài lưu trữ file còn có các công dụng quan trọng khác; chuyện này đã xảy ra với Twitter[1], Telegram[2], hạ tầng khóa PGP[3], chưa nói đến những mục tiêu hiển nhiên như GitHub
[1] https://pentestlab.blog/2017/09/26/command-and-control-twitt...
[2] https://www.blazeinfosec.com/post/leveraging-telegram-as-a-c...
[3] https://torrentfreak.com/openpgp-keyservers-now-store-irremo...
Với Gmail, họ phân phối thông tin xác thực để người khác đăng nhập, rồi đọc các file đính kèm đã tải lên qua IMAP
Tôi từng là Google SAD-SRE (Spam, Abuse, Delivery)
Cũng có thể mã hóa file bằng Base64 rồi nhúng vào chuỗi mã Python
Tôi là Googler, đây là ý kiến cá nhân, và tôi không rành lĩnh vực này
Hy vọng nhóm Go đã phối hợp với phía GCP và Drive, vì hosting file độc hại là vấn đề Google luôn phải xử lý
Nó không khác mấy so với các endpoint khác mà Google đã cho phép người dùng đưa dữ liệu tùy ý vào
Google rất giỏi trong việc để một nhóm trung tâm quản lý hạ tầng và chia sẻ cho toàn công ty. Miễn là không phải ứng dụng nhắn tin thì vậy; và hoàn toàn chỉ suy đoán thôi, nhưng có lẽ nhóm Go đang dùng kho lưu trữ blob nội bộ, và cũng có thể có một nhóm hạ tầng nội bộ tự động xử lý chống lạm dụng và quét file
Trên PyPI cũng có khá nhiều dự án không phải Python
Vì người dùng Python có thể không biên dịch được mã thư viện, nên cần khả năng phân phối wheel là binary đã biên dịch
Loại mã đó thường viết bằng C, nhưng cũng có thể bằng Golang[1]; tôi không tìm được ví dụ, nhưng hình như tôi từng thấy nó được dùng để phân phối cả ứng dụng chứ không chỉ thư viện
Viết app bằng C, đưa lên PyPI rồi bảo người dùng cài bằng
pip installthì khá hay[1] https://github.com/popatam/gopy_build_wheel_example
Chẳng khác nào trên Linux mà
lsđược viết bằng Python, nên có lẽ tốt hơn là đừng chơi trò nàypipyêu cầu phải ở trong một môi trường như venv/virtualenv/pipenv/pyenv thì mới tải package xuốngpip install cmake, và binary độc quyền cũng có thể nhưpip install nvidia-cudnn-cu12Đây là một trong những lý do giúp tôi bỏ hẳn Homebrew
Có thể tôi nghĩ hơi ngây thơ, nhưng tôi không hiểu chuyện này khác gì so với việc tải file lên một repository GitHub
Khác biệt chỉ là GitHub yêu cầu tạo tài khoản thôi sao? GitHub cũng có thể lưu dữ liệu tùy ý, và cũng không có giới hạn 500MB
Hệ thống module của CUE cuối cùng cũng đang được phát hành; MVS tương tự Go nhưng được xây trên hạ tầng OCI
Nếu quan tâm đến hệ thống quản lý phụ thuộc thì đây là vài liên kết tham khảo
proposal: https://github.com/cue-lang/proposal/tree/main/designs/modul...
custom registry: https://cuelang.org/docs/tutorial/working-with-a-custom-modu...
road map: https://github.com/orgs/cue-lang/projects/10/views/8
Module được bật mặc định từ 0.9.0-alpha-5: https://github.com/cue-lang/cue/releases/tag/v0.9.0-alpha.5
Trong Go Sum, dự án Trillian hỗ trợ transparency log: https://github.com/google/trillian
CUE dự định dựa trên các tùy chọn OCI như chứng thực (attestation)
Tôi đã thử nghịch ý tưởng lợi dụng, hay đúng hơn là lạm dụng, golang proxy và sumdb để tạo nhật ký minh bạch miễn phí cho checksum của URL tùy ý
https://getsum.pub/
Nếu chỉ muốn có nhật ký minh bạch công khai thì instance rekor công khai của dự án sigstore phù hợp hơn nhiều
https://www.sigstore.dev/
https://docs.sigstore.dev/logging/overview/
Có thể tôi ngốc, nhưng tôi không hiểu chính xác vấn đề ở đây là gì
Proxy cache các repository không phải Go thì có thể hơi lãng phí, nhưng ngay cả nếu không làm vậy, chẳng phải chỉ cần khiến nó cache một repository Go là cuối cùng vẫn có thể bắt nó lưu dữ liệu tùy ý sao?
Nếu tôi không bỏ sót điều gì thì nghe như chẳng có gì đáng nói cả
Tin mới ở đây dường như chỉ là một proxy công khai không có bảo mật nhận thứ cần proxy rồi cung cấp công khai theo cách không bảo mật
Bài viết nói rằng một số mạng bị giám sát có thể tin URL của golang proxy hơn URL web tùy ý, nên có thể dùng để vượt qua bộ lọc uy tín, v.v.; nhưng đã có nhiều cách như vậy rồi và cách này không có vẻ gì đặc biệt
Hơi lạc đề, nhưng nhìn tên miền thấy cách chơi chữ có điềm không lành nên tôi đã kiểm tra put.as, và nhìn chung đúng như dự đoán
Đây là vấn đề đã được biết đến: https://github.com/golang/go/issues/31866
.modvà.goở thư mục gốc là được sao?Anh ấy và Aaron đã tạo ra Athens, và theo tôi biết Marwan đã viết triển khai giao thức tải xuống Go đầu tiên làm nền tảng cho Athens
Điều thú vị ở issue này là Athens vốn đã dùng lệnh
go mod download -jsonđược nhắc đến như bước kiểm tra trước cho việc xác minh moduleVề cơ bản, nếu repository vượt qua được như một module mà lệnh Go module hiểu, Athens sẽ phục vụ nó
Nói theo cách bị cấm đoán hơn, bạn phải có thể tạo version module, pseudo-version,
+incompatible, và module đó cùng các dependency của nó phải tạo được checksum hợp lệChecksum module hiện liên quan đến việc bao gồm
.mod, tất cả file, và từng dependency một cách đệ quyVì vậy, như tác giả nói, chỉ cần một chương trình Go cơ bản là theo thiết kế đã có nhiều chỗ cho các file tùy ý
W3C đã đặt nền móng để mọi thứ trên web đều có thể được cache mạnh, vậy mà số lượng proxy cache đa dụng lại ít như thế này thì thật lạ
Có phải các nhà xuất bản đang gửi các phản hồi
Cache-Control: max-agequá ngắn hoặcVary: Cookiedù không cần không?Có phải các ISP đang trả quá nhiều chi phí transit so với peering không?
Ví dụ, với các trang không dùng HTTPS, proxy của ISP có thể chèn quảng cáo
Tải xuống phần mềm thường có chữ ký và checksum, nhưng nội dung tùy ý thì hiếm khi có những thứ đó