2 poin oleh GN⁺ 2024-05-26 | 1 komentar | Bagikan ke WhatsApp
  • Checksum database Go dan public module proxy dapat menerima bahkan repositori yang tidak memiliki kode Go, sehingga tercipta jalur untuk memuat data repositori Git arbitrer ke infrastruktur Go lalu mengunduhnya kembali
  • Permintaan sum.golang.org/lookup/$module@$version akan mengambil dari server asal saat menemukan versi modul yang belum tercatat, dan dalam proses ini zip repositori tersebut juga disediakan di proxy.golang.org
  • Repositori Ruby milik Homebrew dan fork repositori Rust ditemukan di checksum database, dan eksperimen mendaftarkan repositori tanpa berkas Go sekalipun dengan pseudo-version berhasil dilakukan
  • Zip modul dibatasi maksimum 500 MiB baik dalam bentuk terkompresi maupun tidak terkompresi, tetapi ukuran ini masih cukup besar untuk melewati batas unduhan di mesin pengembang/CI/CD, menyimpan payload, dan mengimplementasikan C2
  • Dari sekitar 1,59 juta jalur unik di sum.golang.org, sekitar 1,51 juta adalah jalur GitHub atau sekitar 95%, yang sekaligus memperlihatkan ketergantungan ekosistem Go pada GitHub dan potensi penyalahgunaan public proxy

Repositori non-Go yang ditemukan di Go checksum database

  • Saat menelusuri checksum database milik Go, github.com/homebrew/homebrew-core muncul sangat sering di tabel modules
    • github.com/homebrew/homebrew-core: 39.438
    • github.com/Homebrew/homebrew-core: 30.896
    • github.com/concourse/concourse: 25.372
    • github.com/openshift/release: 24.065
    • github.com/cilium/cilium: 22.138
  • Repositori Homebrew dikenal menggunakan Ruby, dan baik di repositori maupun berkas hasil kloning tidak ditemukan go.mod atau berkas sumber Go
  • Perbedaan huruf besar/kecil dijelaskan oleh aturan case encoding di dokumentasi Go
    • Huruf besar dienkode sebagai ! dan huruf kecil terkait, sehingga example.com/M dan example.com/m dapat disimpan bersama bahkan pada sistem berkas yang tidak membedakan huruf besar/kecil
  • github.com/Edu4rdSHL/rust-headless-chrome juga muncul di checksum database meski merupakan fork repositori Rust yang tidak terkait Go

Cara /lookup mengambil repositori

  • Menurut Go Modules Reference dalam dokumentasi modul Go, saat memeriksa checksum database, perintah Go terlebih dahulu mengambil data rekaman dari endpoint /lookup
  • Jika versi modul belum tercatat di log, checksum database akan mencoba mengambil modul tersebut dari server asal sebelum memberikan respons
  • Format endpoint adalah $base/lookup/$module@$version
    • Mengembalikan nomor rekaman log untuk $version dari $module, baris go.sum, dan deskripsi tree yang ditandatangani
  • Saat pseudo-version untuk github.com/homebrew/homebrew-core diminta, checksum record dan hash go.mod dikembalikan
  • Jika repositori tidak memiliki tag versi, aturan pseudo-version milik Go akan digunakan

Eksperimen mendaftarkan modul Go baru

  • Setelah membuat modul Go baru github.com/gdbinit/fluxmatter, status pendaftarannya diperiksa melalui permintaan lookup
  • Pencarian @latest mengembalikan kesalahan bahwa itu bukan canonical version
    • bad request: version "latest" is not canonical
  • Pencarian @v0.0.0 mengembalikan kesalahan bahwa revision tersebut tidak dikenal
    • not found: ... invalid version: unknown revision v0.0.0
  • Namun setelah checksum database disinkronkan lagi dan diperiksa ulang, modul tersebut ternyata sudah terdaftar
    • github.com/gdbinit/fluxmatter|v0.0.0-20240524163826-a7e64ffd69f2|2024-05-24T16:40:51.203837Z
  • proxy.golang.org/github.com/gdbinit/fluxmatter/@latest mengembalikan pseudo-version dan informasi asal GitHub, dan berkas zip untuk versi tersebut juga bisa diunduh serta diverifikasi kompresinya
  • Pada proses seeding awal, tidak perlu menentukan versi yang tepat; cukup dengan kueri lookup yang berisi jalur modul dan nilai yang tampak seperti versi

Repositori tanpa kode Go juga dimuat ke public proxy

  • Eksperimen yang sama berhasil dilakukan dengan repositori github.com/gdbinit/readmem yang sama sekali tidak memiliki kode Go
  • Permintaan lookup memang mengembalikan kesalahan bahwa revision v0.0.0 tidak dikenal, tetapi checksum database tetap mencatatnya sebagai pseudo-version
    • github.com/gdbinit/readmem|v0.0.0-20131006075740-407cb0a56933|2024-05-24T16:45:35.88456Z
  • @latest di proxy.golang.org mengembalikan pseudo-version repositori tersebut dan informasi asal Git
  • Zip yang diunduh tidak berisi berkas Go, melainkan Entitlements.plist, README, berkas proyek Xcode, main.c, dan lainnya
  • Eksperimen ini menggunakan repositori GitHub, tetapi jika VCS lain berfungsi, hal serupa mungkin juga dapat dilakukan di situs hosting lain

Ketergantungan pada GitHub dan batas ukuran

  • Jumlah jalur unik di checksum database adalah 1.591.375, dan di antaranya 1.515.957 jalur berupa github.com%
  • Sekitar 95% dari jalur unik dihosting di GitHub {p:95}
  • Angka ini adalah statistik mentah yang belum menghapus fork atau target yang sebenarnya bukan kode Go
  • Zip modul Go tunduk pada File path and size constraints
    • berkas zip modul maksimal 500 MiB
    • total ukuran berkas dalam bentuk tidak terkompresi juga maksimal 500 MiB
    • berkas go.mod maksimal 16 MiB
    • berkas LICENSE juga maksimal 16 MiB
  • Batas ini ditujukan untuk meredam serangan denial of service terhadap pengguna, proxy, dan bagian lain dari ekosistem modul
  • Namun 500 MiB tetap cukup besar bila mempertimbangkan potensi penyalahgunaan

Skenario penyalahgunaan yang mungkin

  • Public Go proxy dapat dipakai untuk melewati pembatasan tujuan unduhan pada mesin pengembang atau server CI/CD
    • Dengan asumsi tidak ada GOPROXY privat
    • Malware dapat mengunggah payload ke repositori lalu mengunduhnya dari proxy saat dibutuhkan
    • Bahkan jika sumber asli hilang, entri checksum database mungkin hanya menyisakan jejak kecil
  • DoS terhadap proxy.golang.org mungkin tidak mudah dijalankan
    • Proxy dapat dipaksa mengunduh repositori Git arbitrer
    • Salah satu serangan yang mungkin adalah mengumpulkan banyak URL GitHub lalu mengirim banyak permintaan ke API lookup
    • Implementasi server tidak diketahui, tetapi mungkin ada pembatasan paralel seperti work queue
    • Perlindungan bandwidth di sisi GitHub juga mungkin aktif
    • Ada juga kemungkinan DoS yang menargetkan penyimpanan, tetapi ini masih sebatas dugaan
  • C2 (command and control) dapat dibuat dengan mudah di atas proxy.golang.org
    • Kueri @latest dapat digunakan untuk menemukan versi terbaru modul tertentu
    • Payload bisa berupa berkas biasa, atau disembunyikan di dalam go.mod maupun berkas sumber Go
    • Untuk menghindari penggunaan satu repositori saja, dapat digunakan module DGA

Alur unduhan C2

Kesimpulan dan pertanyaan yang tersisa

  • Checksum database dan proxy Go, sesuai prosedur yang terdokumentasi, dapat mengambil dan menyimpan modul yang belum tercatat dari server asal
  • Kondisi saat ini tampaknya belum terlihat sebagai masalah serius bagi infrastruktur Go, tetapi cukup mudah disalahgunakan dan masih ada ruang untuk perbaikan
  • Mungkin ada alasan yang terdokumentasi atau tidak dipublikasikan mengapa repositori non-Go boleh masuk ke proxy dan checksum database
  • Untuk memastikan apakah ini sudah disalahgunakan seseorang, perlu menyelidiki sekitar 1,6 juta repositori unik dan sekitar 22 juta entri berdasarkan database lokal terbaru
  • Mengapa sebagian proyek non-Go yang valid berada di database masih menjadi pertanyaan terbuka

1 komentar

 
GN⁺ 2024-05-26
Komentar Hacker News
  • Layanan online tempat pengguna mengunggah materi dan materi itu terlihat publik pada akhirnya akan dipakai untuk command-and-control, pelanggaran hak cipta, dan hosting CSAM
    Hal ini terutama berlaku untuk layanan yang sulit diblokir karena punya kegunaan penting selain file hosting; ini sudah terjadi di Twitter[1], Telegram[2], dan infrastruktur kunci PGP[3], belum lagi target yang sudah jelas seperti 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...

    • Hal yang sama juga berlaku untuk Gmail, Google Groups, Google Drive, dan Gchat, dan data yang disimpan bahkan tidak perlu bersifat publik
      Untuk Gmail, kredensial dibagikan agar orang bisa login, lalu membaca lampiran yang telah diunggah lewat IMAP
      Saya dulu bekerja di Google SAD-SRE (Spam, Abuse, Delivery)
    • PyPI juga tampak mudah dipakai untuk tujuan seperti ini karena memungkinkan memasukkan file non-Python secara arbitrer ke dalam paket
      File juga bisa di-encode ke Base64 lalu dimasukkan ke dalam string kode Python
    • Saya tidak tahu apakah ini sudah pernah terjadi, tetapi target yang kurang obvious tampaknya adalah HuggingFace
  • Saya seorang Googler dan ini pendapat pribadi; saya tidak terlalu paham bidang ini
    Saya berharap tim Go sudah berkolaborasi dengan pihak GCP dan Drive, karena hosting file berbahaya adalah masalah yang selalu ditangani Google
    Ini tidak terlalu berbeda dari endpoint lain yang sudah diizinkan Google untuk menerima data arbitrer dari orang

    • Saya mantan Googler dan tidak tahu banyak soal tim Go Dev Tools, tetapi di antara perusahaan besar tempat saya pernah bekerja atau yang saya dengar dari teman dekat, Google hampir termasuk yang paling baik dalam kolaborasi internal seperti ini
      Google sangat mahir mengelola infrastruktur lewat tim pusat dan membagikannya ke seluruh perusahaan. Kecuali aplikasi messenger, tentu. Ini murni dugaan, tetapi tim Go mungkin menggunakan blob storage internal, dan mungkin juga ada tim infrastruktur internal yang otomatis menangani mitigasi penyalahgunaan serta pemindaian file
  • Di PyPI juga ada cukup banyak proyek non-Python
    Python membutuhkan kemampuan mendistribusikan wheel, yaitu biner hasil kompilasi, karena pengguna mungkin tidak bisa mengompilasi kode library sendiri
    Kode seperti itu sering ditulis dalam C, tetapi Golang[1] juga bisa, dan meski saya tidak menemukan contohnya, saya rasa saya pernah melihatnya dipakai untuk mendistribusikan aplikasi, bukan library
    Menulis aplikasi dalam C, mengunggahnya ke PyPI, lalu menyuruh pengguna menginstalnya dengan pip install itu cukup keren
    [1] https://github.com/popatam/gopy_build_wheel_example

    • Misalnya pun ada syarat penggunaan Python, orang yang berniat jahat rasanya tinggal menyediakan kode stub Python seminimal mungkin agar lolos
      Di Linux, ls pun pada dasarnya bisa dianggap ditulis dengan Python, jadi mungkin lebih baik tidak memainkan permainan seperti ini
    • Sebelum pip mulai mengharuskan paket diunduh dari dalam lingkungan seperti venv/virtualenv/pipenv/pyenv, kegunaan seperti ini mungkin jauh lebih bermanfaat
    • Ada pip install cmake, dan biner proprietari juga bisa seperti pip install nvidia-cudnn-cu12
    • Akhir-akhir ini saya sering memakai PyPI untuk alat non-Python seperti FFmpeg dan Eigen
      Itu salah satu alasan saya bisa sepenuhnya meninggalkan Homebrew
  • Mungkin ini pemikiran yang naif, tetapi saya tidak tahu apa bedanya dengan mengunggah file ke repositori GitHub
    Apakah bedanya cuma GitHub mewajibkan pembuatan akun? Di GitHub juga bisa menyimpan data arbitrer, dan tidak ada batas 500MB

    • GitHub punya rate limit yang cukup ketat untuk permintaan anonim
  • Sistem modul CUE akhirnya mulai dirilis, dan MVS mirip dengan Go, tetapi dibangun di atas infrastruktur OCI
    Jika Anda tertarik pada sistem manajemen dependensi, ini beberapa tautan yang layak dilihat
    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
    Mulai 0.9.0-alpha-5, modul diaktifkan secara default: https://github.com/cue-lang/cue/releases/tag/v0.9.0-alpha.5
    Di Go Sum, proyek Trillian menopang transparency log: https://github.com/google/trillian
    Rencananya CUE akan memanfaatkan opsi OCI seperti attestation

    • Saya tidak mengerti apa hubungannya dengan artikel yang ditautkan itu
  • Saya sempat bereksperimen dengan ide menumpangi, atau lebih tepatnya menyalahgunakan, golang proxy dan sumdb untuk membuat log transparansi gratis bagi checksum dari URL arbitrer
    https://getsum.pub/

  • Mungkin saya yang bodoh, tapi saya tidak paham persis apa masalahnya di sini
    Memang agak boros jika proxy meng-cache repositori non-Go, tetapi bahkan tanpa itu pun, kalau kita bisa membuatnya meng-cache repositori Go, bukankah pada akhirnya kita tetap bisa memaksanya menyimpan data arbitrer?
    Kalau saya tidak melewatkan sesuatu, ini terdengar seperti bukan masalah besar sama sekali

    • Sepertinya tidak ada yang terlewat
      Hal baru di sini tampaknya hanya bahwa proxy publik yang tidak aman menerima apa yang akan diproksikan lalu menyediakannya secara publik dengan cara yang juga tidak aman
      Tulisan itu mengatakan bahwa beberapa jaringan yang diawasi mungkin lebih memercayai URL golang proxy daripada URL web arbitrer, sehingga ini bisa dipakai untuk melewati filter reputasi dan semacamnya, tetapi cara seperti itu sudah banyak, dan pendekatan ini tidak terlihat istimewa
  • Agak di luar topik, tetapi melihat domainnya membuat saya curiga ada permainan kata yang tidak enak, jadi saya memeriksa put.as, dan isinya kurang lebih seperti yang saya duga

    • https://put.as/ agak NSFW
    • Saya sempat membuat kesalahan dengan membukanya di kantor
    • Itu memang kata bentuk jamak dalam bahasa Spanyol. Tapi…
  • Ini isu yang sudah dikenal: https://github.com/golang/go/issues/31866

    • Perbaikannya mungkin membantu mencegah kesalahan yang tidak disengaja, tetapi kalau seseorang memang sengaja ingin melakukannya, bukankah cukup menambahkan file .mod dan .go di root?
    • Sama sekali tidak mengejutkan kalau Marwan ada di isu itu
      Dia dan Aaron membuat Athens, dan sejauh yang saya tahu Marwan menulis implementasi awal protokol unduhan Go yang kemudian menjadi dasar Athens
      Hal menarik dari isu ini adalah bahwa Athens sudah menggunakan perintah go mod download -json yang disebutkan sebagai pemeriksaan awal untuk validasi modul
      Pada dasarnya, jika sebuah repositori lolos sebagai modul yang dipahami oleh perintah modul Go, Athens akan menyajikannya
      Kalau mau mengatakannya dengan istilah yang lebih terlarang, kita harus bisa membuat versi modul, pseudo-version, dan +incompatible, serta membuat checksum yang valid untuk modul itu dan dependensinya
      Checksum modul saat ini berkaitan dengan penyertaan .mod, semua file, dan tiap dependensi secara rekursif
      Jadi seperti yang dikatakan penulis, hanya dengan program Go yang sangat dasar pun, secara desain kita bisa mendapatkan banyak ruang untuk file arbitrer
  • W3C telah meletakkan dasar agar segala sesuatu di web bisa di-cache secara kuat, jadi aneh kalau cache proxy serbaguna jumlahnya sangat sedikit
    Apakah para penerbit mengirim respons seperti Cache-Control: max-age yang pendek atau Vary: Cookie padahal tidak perlu?
    Apakah ISP membayar biaya transit terlalu mahal dibanding peering?

    • Secara umum tidak ada cara untuk menjamin bahwa cache tidak memodifikasi konten
      Misalnya, pada situs non-HTTPS, proxy ISP bisa menyisipkan iklan
      Unduhan perangkat lunak biasanya punya tanda tangan dan checksum, tetapi konten arbitrer umumnya tidak punya itu