- Commit
0226b56pada rhboot/shim memperbaiki CVE-2023-40547, yang terjadi karena shim memercayai begitu saja nilai ukuran pada header HTTP saat menerima file - Jika header yang dimanipulasi menetapkan ukuran yang lebih kecil daripada data yang benar-benar diterima, shim dapat mengalokasikan ruang yang lebih kecil daripada buffer yang dibutuhkan
- Kode sebelumnya menggunakan nilai header untuk alokasi, tetapi menggunakan metadata protokol untuk penyalinan, sehingga dapat berujung pada out-of-bounds write
- Patch memeriksa
*buf_size < rx_message.BodyLengthdireceive_http_response()padahttpboot.c, dan jika gagal menanganinya sebagaiEFI_BAD_BUFFER_SIZEsertaInvalid Content-Length - Cakupan perubahan adalah 7 baris ditambahkan dan 1 baris dihapus pada satu file
httpboot.c, dan typoContent-Lenghtjuga diperbaiki menjadiContent-Length
Alur terjadinya kerentanan
- CVE-2023-40547 adalah masalah yang terjadi saat shim mengambil file melalui HTTP atau protokol terkait
- Dalam proses mengalokasikan buffer untuk menyimpan data yang diterima, nilai ukuran pada header HTTP digunakan
- Header HTTP dapat dimanipulasi, dan dapat menetapkan ukuran yang lebih kecil daripada data yang benar-benar diterima
- Alur sebelumnya memakai nilai header untuk alokasi buffer, tetapi saat menyalin data dari buffer rx, alur tersebut menggunakan metadata protokol sebagai acuan
- Karena perbedaan ini, data yang lebih besar daripada buffer yang dialokasikan dapat disalin, dan akibatnya out-of-bounds write dapat terjadi
Isi patch
- Patch menambahkan pemeriksaan defensif ke
receive_http_response(EFI_HTTP_PROTOCOL *http, VOID **buffer, UINT64 *buf_size)dihttpboot.c - Jika
*buf_size == 0, patch memperbaiki typo pada pesan galat yang sudah ada dan berpindah kegoto errorFailed to get Content-Lenght→Failed to get Content-Length
- Pemeriksaan baru memeriksa kondisi
*buf_size < rx_message.BodyLength- Jika kondisi benar,
efi_status = EFI_BAD_BUFFER_SIZEditetapkan - Galat
Invalid Content-Lengthdicetak - Setelah itu berpindah ke
goto error
- Jika kondisi benar,
Cakupan perubahan
- File yang diubah hanya satu, yaitu
httpboot.c - Jumlah perubahan adalah 7 baris ditambahkan dan 1 baris dihapus
- Intinya adalah logika yang memeriksa apakah panjang body yang diterima,
rx_message.BodyLength, lebih besar daripada ukuran alokasi,*buf_size
Catatan terkait
- Commit ini ditandai sebagai perubahan yang menyelesaikan CVE-2023-40547
- Pesan commit menyatakan bahwa masalah ini berasal dari kesalahan memercayai header HTTP
- Pelapor kerentanan tercatat sebagai Bill Demirkapi dari Microsoft Security Response Center
1 komentar
Komentar Hacker News
shim adalah bootloader EFI yang umum dipakai oleh distro Linux yang ingin mengaktifkan Secure Boot
Dari sudut pandang distro, mereka ingin memudahkan pengguna mengaktifkan Secure Boot dengan kunci tanda tangan Microsoft yang sudah tersedia secara bawaan, alih-alih membuat pengguna mendaftarkan kunci sendiri
Namun, karena Microsoft umumnya tidak menandatangani bootloader GPL seperti GRUB, dibuatlah shim yang bisa ditandatangani dengan kunci Microsoft, dan shim memverifikasi tanda tangan target boot-nya dengan kunci terpisah yang disebut Machine Owner Key, yaitu MOK
Saat menentukan biner EFI yang akan di-boot di shim, URL HTTP bisa diberikan, dan jika server HTTP bersifat berbahaya, hal ini dapat memicu penulisan di luar batas
Namun, karena biasanya dipakai untuk mem-boot bootloader tahap kedua lokal seperti GRUB, pada kebanyakan instalasi kemungkinan risikonya tampak rendah
Secure Boot sejak awal memang dirancang agar biner yang sudah ditandatangani pun bisa dicabut lewat daftar DBX, dan jika daftar ini dimasukkan ke UEFI, biner terkait akan ditolak meski memiliki tanda tangan yang valid
Jika tanda tangan biner shim lama yang memiliki bug ini ditambahkan ke daftar tersebut, masing-masing perangkat dapat memperbarui daftarnya sendiri; daftar itu juga bisa didistribusikan melalui pembaruan kapsul seperti LVFS, dan jika Anda mengelola sendiri kunci serta variabel Secure Boot, Anda juga bisa mengunduh lalu mendaftarkan daftar tersebut dari https://uefi.org/revocationlistfile
Kalau memang begitu, ini tidak akan diberi peringkat Critical
Bug ini bisa dieksploitasi secara lokal saat malware berprivilegi menimpa partisi EFI, saat melakukan serangan man-in-the-middle di jaringan sekitar yang mengaktifkan PXE boot, dan sebagai serangan man-in-the-middle jarak jauh saat memakai HTTP boot
Penyerang jarak jauh tanpa hak akses yang berada di posisi man-in-the-middle dapat mengeksploitasinya tanpa akses langsung jika perangkat korban menggunakan HTTP boot
Penyerang jarak jauh yang mendapatkan hak akses dan eksekusi kode di perangkat korban dapat melewati Secure Boot meskipun korban tidak memakai HTTP boot, asalkan firmware mendukung HTTP
Misalnya, dengan mengubah variabel urutan boot untuk menunjuk ke server yang dikendalikan penyerang, atau dengan menimpa bootloader di partisi EFI menggunakan image shim dan GRUB2 yang tampak normal, lalu membuat
grub.cfgmelakukan chainload ke shim baru melalui HTTPIni dimungkinkan karena sintaks perangkat GRUB2 dapat menentukan perangkat yang didukung, termasuk HTTP
Selain itu, penyerang sekitar tanpa hak akses yang berada di posisi man-in-the-middle dan menghadapi perangkat korban yang memakai PXE boot dapat mengeksploitasinya dengan merangkai PXE shim → PXE GRUB2 → shim via HTTP
Sumber: https://techcommunity.microsoft.com/t5/hardware-dev-center/u...
Jika klausul anti-Tivoization GPLv3 dapat menuntut penyediaan kunci tanda tangan Secure Boot, bukankah kunci tanda tangan MOK juga harus diberikan jika diminta?
Kalau begitu, siapa pun pada dasarnya bisa memperoleh kunci untuk menandatangani kode arbitrer yang akan di-boot secara tidak langsung melalui Secure Boot, dan saya tidak yakin itu secara bermakna berbeda dari hasil jika Microsoft langsung menerbitkan kunci tanda tangan untuk proyek GPLv3 seperti GRUB
Di perangkat BIOS lama saya pernah membuat WBM melakukan chainload ke GRUB, tetapi di perangkat UEFI saya belum pernah mencobanya, jadi saya tidak tahu apakah ada hambatan tertentu
Anda mungkin bertanya, “Mengapa melakukan boot dari server yang tidak tepercaya atau telah dikompromikan?” atau, “Kalau servernya sudah dikompromikan, bukankah cukup mengirim biner berbahaya saja sehingga ini tidak berarti?” Singkatnya, biner yang pada akhirnya di-boot oleh shim harus ditandatangani dengan MOK
Karena itu, baik saat boot di jaringan yang telah dikompromikan, via HTTP, maupun dari server yang telah dikompromikan, jaminan keamanannya seharusnya tetap sama terlepas dari apakah HTTPS digunakan atau tidak
Secure Boot tidak mencegah downgrade, jadi fakta bahwa server yang dikompromikan bisa dipakai untuk serangan downgrade adalah hal terpisah dari kerentanan ini
Perlindungan terhadap serangan downgrade memang pada akhirnya harus diimplementasikan secara terpisah dengan cara yang lebih kuat
Meski begitu, saya tidak tahu mengapa shim perlu mendukung HTTP boot secara langsung
Ini bisa saja ditangani oleh biner EFI lokal kedua yang ditandatangani dengan MOK, tetapi mungkin fitur itu dianggap relatif sederhana untuk diimplementasikan
Saya tidak paham mengapa kode ini memperlakukan panjang body dengan dua acuan berbeda
Menurut RFC, dalam HTTP/1.1 Content-Length adalah informasi otoritatif tentang panjang body permintaan/respons HTTP
Data yang berada di jalur setelah panjang itu, secara definisi, adalah bagian dari pesan lain
Sebaliknya, jika Content-Length lebih besar daripada
rx_message.BodyLength, artinya seluruh pesan belum diterima, jadi harus menunggu lagi atau memunculkan error timeoutDalam kedua kasus, jika tidak ada jaminan bahwa
rx_message.BodyLengthsama dengan Content-Length, berarti nilainya salahJika ingin menanganinya dengan lebih longgar, tidak ada alasan untuk melihat header Content-Length; cukup gunakan
rx_message.BodyLengthsebagai ukuran buffer dan tafsirkan semua data di jalur sebagai pesan yang diterimaKode saat ini rumit tanpa perlu, dan bug seperti ini pun masuk dengan cara seperti itu
Dari kode di sekitarnya https://github.com/rhboot/shim/blob/0226b56513b2b8bd5fd281bc... terlihat bahwa di dalam loop, potongan data diterima dan setiap kali diperiksa apakah data baru melebihi kapasitas buffer yang ditentukan oleh Content-Length
Namun sebelumnya, pemeriksaan itu tidak dilakukan untuk pembacaan pertama di luar loop, dan itulah bug-nya
Meski begitu, saya tidak melihat ada kode yang melakukan pemeriksaan akhir bahwa ukuran yang diunduh sama dengan
*buf_size, yaitu Content-LengthJika kondisi ini gagal, itu bisa menjadi sinyal bahwa koneksi ditutup terlalu cepat
Ini jelas bug dan syukurlah sudah diperbaiki, tetapi saya juga bertanya-tanya siapa yang men-boot perangkatnya dari host yang tidak tepercaya
Jika penyerang sudah menguasai layanan HTTP sampai bisa mengirim header berbahaya, menghindari overflow ini adalah masalah yang paling kecil; sertifikat pun sudah disusupi, dan mereka juga bisa menyisipkan malware ke payload yang sesuai spesifikasi lalu mengirimkannya
Ini memang bug, tetapi saya tidak yakin ini layak disebut Critical
Mereka sungguh percaya pada strategi keamanan bahwa semua hal yang berpotensi dikompromikan tidak boleh tidak ditandatangani oleh Secure Boot
Jika ada satu saja sesuatu yang ditandatangani namun rentan, itu bisa dipakai untuk mendekripsi disk terenkripsi Secure Boot+TPM milik semua orang
Sulit memahami mengapa pendekatan seperti ini dianggap sebagai model keamanan yang valid, dan kerentanan seperti ini sudah sangat banyak
Selain itu, Windows, si gajah di dalam ruangan, sepenuhnya diabaikan
Contoh pola pikir seperti ini: https://lkml.org/lkml/2018/4/3/767
Terlepas dari kekhawatiran Linus, pada banyak distro mode integritas benar-benar aktif saat boot dengan Secure Boot
Mungkin karena kebijakan Microsoft dan situasi di mana distro dipaksa mengikuti prosedur yang dijelaskan di thread tersebut demi mendapatkan tanda tangan Microsoft UEFI
Akibatnya, saat Secure Boot diaktifkan, fitur distro biasanya dibatasi, misalnya tidak bisa memakai hibernasi
Saat sesuatu yang penting sedang terjadi, pakailah HTTP yang ada S-nya
Booting perangkat juga termasuk di sini, dan header HTTPS sejak awal selalu terenkripsi
Meski begitu, bug ini ditemukan dengan baik
Header yang salah tetap bisa dikirim dalam kedua kasus
Karena enkripsi membutuhkan waktu dan tanggal yang akurat
RTC mungkin valid, tetapi saya juga tidak tahu apakah penanganan zona waktunya benar, dan bagaimanapun waktunya bisa saja meleset
Sepertinya Anda belum memahami masalahnya dengan benar
Content-lengthbukan panjang body yang sebenarnya, melainkan panjang setelah Content-encodingApakah build shim ini menyertakan httpboot?
Setahu saya shim hanya untuk menjalankan binary EFI lain di disk, dan saya rasa saya belum pernah melihat fitur network boot milik shim benar-benar dipakai
Mungkin saya yang kurang paham, tetapi saya kira sebagian besar klien HTTP hanya membaca sampai Content-Length yang ditentukan, dan jika byte yang terbaca lebih sedikit daripada Content-Length maka dianggap error
Menurut saya spesifikasi UEFI tampaknya tidak secara spesifik menentukan perilaku saat header Content-Length dan panjang body respons tidak cocok
Jadi sangat mungkin beberapa implementasi hanya membuat permintaan
connection:closedan tidak memeriksa Content-LengthKerentanan ini dilaporkan oleh MSRC dan deskripsi CVE tidak membahas eksploitasi nyata
Mungkin nanti akan diungkap, atau mungkin ini hanya masalah teoretis
Bisa jelaskan mengapa membaca lebih sedikit dari panjang body yang sebenarnya itu berbahaya?
Saya justru mengira kebalikannya yang berbahaya
Namun ukurannya diambil dari header HTTP yang bisa dimanipulasi, dan penyerang bisa menentukan ukuran yang lebih kecil daripada data yang diterima
Dalam kasus ini, kode memakai nilai header untuk alokasi, tetapi saat menyalin dari buffer penerimaan menggunakan ukuran dari metadata protokol, sehingga terjadi penulisan di luar batas