Linux mengabaikan bit setuid¹ pada semua executable yang ditafsirkan (yaitu executable yang dimulai dengan sebuah #!
baris). The comp.unix.questions FAQ menjelaskan masalah keamanan dengan script shell setuid. Masalah-masalah ini ada dua jenis: terkait shebang dan terkait shell; Saya masuk ke detail lebih lanjut di bawah ini.
Jika Anda tidak peduli dengan keamanan dan ingin mengizinkan skrip setuid, di Linux, Anda harus menambal kernel. Pada kernel 3.x, saya pikir Anda perlu menambahkan panggilan ke install_exec_creds
dalam load_script
fungsi, sebelum panggilan ke open_exec
, tapi saya belum menguji.
Setuid shebang
Ada kondisi ras yang melekat pada cara shebang ( #!
) biasanya diterapkan:
- Kernel membuka executable, dan menemukan bahwa itu dimulai dengan
#!
.
- Kernel menutup executable dan membuka interpreter sebagai gantinya.
- Kernel menyisipkan path ke skrip ke daftar argumen (as
argv[1]
), dan mengeksekusi interpreter.
Jika skrip setuid diizinkan dengan implementasi ini, penyerang dapat memanggil skrip arbitrer dengan membuat tautan simbolis ke skrip setuid yang ada, menjalankannya, dan mengatur untuk mengubah tautan setelah kernel melakukan langkah 1 dan sebelum penerjemah berkeliling untuk membuka argumen pertamanya. Untuk alasan ini, sebagian besar unices mengabaikan bit setuid ketika mereka mendeteksi shebang.
Salah satu cara untuk mengamankan implementasi ini adalah bagi kernel untuk mengunci file skrip sampai interpreter membukanya (perhatikan bahwa ini harus mencegah tidak hanya memutus tautan atau menimpa file, tetapi juga mengganti nama direktori di path). Tetapi sistem unix cenderung menghindar dari kunci wajib, dan tautan simbolik akan membuat fitur kunci yang benar terutama sulit dan invasif. Saya tidak berpikir ada yang melakukannya dengan cara ini.
Beberapa sistem unix (terutama OpenBSD, NetBSD dan Mac OS X, yang semuanya memerlukan pengaturan kernel untuk diaktifkan) mengimplementasikan setuid shebang aman menggunakan fitur tambahan: path merujuk ke file yang sudah dibuka pada deskriptor file N (jadi pembukaannya adalah kira-kira setara dengan ). Banyak sistem unix (termasuk Linux) memiliki tetapi tidak memiliki skrip setuid./dev/fd/N
/dev/fd/N
dup(N)
/dev/fd
- Kernel membuka executable, dan menemukan bahwa itu dimulai dengan
#!
. Katakanlah file descriptor untuk executable adalah 3.
- Kernel membuka interpreter.
- Kernel memasukkan
/dev/fd/3
daftar argumen (as argv[1]
), dan mengeksekusi penerjemah.
Halaman shebang Sven Mascheck memiliki banyak informasi tentang shebang di seluruh unit, termasuk dukungan setuid .
Penerjemah setuid
Mari kita asumsikan Anda telah berhasil membuat program Anda berjalan sebagai root, baik karena OS Anda mendukung setuid shebang atau karena Anda telah menggunakan pembungkus biner asli (seperti sudo
). Sudahkah Anda membuka lubang keamanan? Mungkin . Masalahnya di sini bukan tentang program yang ditafsirkan vs dikompilasi. Masalahnya adalah apakah sistem runtime Anda berperilaku aman jika dijalankan dengan hak istimewa.
Setiap biner yang dapat dieksekusi secara dinamis terhubung dengan cara diinterpretasikan oleh loader dinamis (misalnya /lib/ld.so
), yang memuat pustaka dinamis yang diperlukan oleh program. Pada banyak unices, Anda dapat mengonfigurasi jalur pencarian untuk pustaka dinamis melalui lingkungan ( LD_LIBRARY_PATH
adalah nama umum untuk variabel lingkungan), dan bahkan memuat pustaka tambahan ke semua binari yang dijalankan ( LD_PRELOAD
). Penyerang program dapat mengeksekusi kode arbitrer dalam konteks program tersebut dengan menempatkan keahlian khusus ( libc.so
di $LD_LIBRARY_PATH
antara taktik lain). Semua sistem waras mengabaikan LD_*
variabel dalam executable setuid.
Dalam shell seperti sh, csh dan turunannya, variabel lingkungan secara otomatis menjadi parameter shell. Melalui parameter seperti PATH
, IFS
, dan banyak lagi, Invoker script memiliki banyak kesempatan untuk mengeksekusi kode arbitrary dalam konteks skrip shell. Beberapa shell mengatur variabel-variabel ini ke default waras jika mereka mendeteksi bahwa script telah dipanggil dengan hak istimewa, tapi saya tidak tahu bahwa ada implementasi tertentu yang akan saya percayai.
Sebagian besar lingkungan runtime (apakah asli, bytecode atau ditafsirkan) memiliki fitur serupa. Beberapa mengambil tindakan pencegahan khusus dalam executable setuid, meskipun yang menjalankan kode asli sering tidak melakukan hal yang lebih baik daripada menghubungkan dinamis (yang memang mengambil tindakan pencegahan).
Perl adalah pengecualian penting. Ini secara eksplisit mendukung skrip setuid dengan cara yang aman. Bahkan, skrip Anda dapat menjalankan setuid bahkan jika OS Anda mengabaikan bit setuid pada skrip. Ini karena perl mengirim dengan setuid root helper yang melakukan pemeriksaan yang diperlukan dan menginstal ulang juru bahasa pada skrip yang diinginkan dengan hak istimewa yang diinginkan. Ini dijelaskan dalam manual perlsec . Dulu skrip perl setuid diperlukan #!/usr/bin/suidperl -wT
alih-alih #!/usr/bin/perl -wT
, tetapi pada sebagian besar sistem modern, #!/usr/bin/perl -wT
sudah cukup.
Perhatikan bahwa menggunakan pembungkus biner asli tidak melakukan apa pun untuk mencegah masalah ini . Bahkan, ini dapat membuat situasi menjadi lebih buruk , karena dapat mencegah lingkungan runtime Anda mendeteksi bahwa ia dipanggil dengan hak istimewa dan memintas konfigurasi runtime-nya.
Pembungkus biner asli dapat membuat skrip shell aman jika pembungkus membersihkan lingkungan . Script harus berhati-hati untuk tidak membuat terlalu banyak asumsi (misalnya tentang direktori saat ini) tetapi ini berjalan. Anda dapat menggunakan sudo untuk ini asalkan sudah diatur untuk membersihkan lingkungan. Variabel daftar hitam rentan kesalahan, jadi selalu daftar putih. Dengan sudo, pastikan env_reset
opsi dihidupkan, setenv
dimatikan, dan itu env_file
dan env_keep
hanya berisi variabel tidak berbahaya.
TL, DR:
- Setuid shebang tidak aman tetapi biasanya diabaikan.
- Jika Anda menjalankan program dengan hak istimewa (baik melalui sudo atau setuid), tulis kode asli atau perl, atau mulai program dengan pembungkus yang membersihkan lingkungan (seperti sudo dengan
env_reset
opsi).
¹ Diskusi ini berlaku sama jika Anda mengganti "setgid" dengan "setuid"; keduanya diabaikan oleh kernel Linux pada skrip