Menggunakan setuid sedikit dengan benar

45

Saya memiliki proses yang membutuhkan hak akses root ketika dijalankan oleh pengguna normal. Rupanya saya bisa menggunakan "setuid bit" untuk mencapai ini. Apa cara yang tepat untuk melakukan ini pada sistem POSIX?

Juga, bagaimana saya bisa melakukan ini dengan skrip yang menggunakan juru bahasa (bash, perl, python, php, dll)?

goldilocks
sumber

Jawaban:

53

The bit setuid dapat diatur pada file executable sehingga ketika menjalankan, program ini akan memiliki hak-hak istimewa dari pemilik file bukan pengguna yang sebenarnya, jika mereka berbeda. Ini adalah perbedaan antara uid efektif (id pengguna) dan uid nyata .

Beberapa utilitas umum, seperti passwd, dimiliki oleh root dan dikonfigurasi dengan cara ini karena kebutuhan ( passwdperlu mengakses /etc/shadowyang hanya dapat dibaca oleh root).

Strategi terbaik ketika melakukan ini adalah melakukan apa pun yang perlu Anda lakukan sebagai superuser segera kemudian menurunkan hak istimewa sehingga bug atau penyalahgunaan lebih kecil kemungkinannya terjadi saat menjalankan root. Untuk melakukan ini, Anda mengatur cairan efektif proses menjadi cairan asli. Dalam POSIX C:

#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void report (uid_t real) {
    printf (
        "Real UID: %d Effective UID: %d\n",
        real,
        geteuid()
    );
}

int main (void) {
    uid_t real = getuid();
    report(real);
    seteuid(real);
    report(real);
    return 0;
}

Fungsi yang relevan, yang seharusnya memiliki padanan dalam sebagian besar bahasa tingkat yang lebih tinggi jika digunakan secara umum pada sistem POSIX:

  • getuid(): Dapatkan nyata uid.
  • geteuid(): Dapatkan uid yang efektif .
  • seteuid(): Tetapkan uid yang efektif .

Anda tidak dapat melakukan apa pun dengan yang terakhir tidak sesuai dengan uid asli kecuali sejauh setuid ditetapkan pada executable . Jadi untuk mencoba ini, kompilasi gcc test.c -o testuid. Anda kemudian perlu, dengan hak istimewa:

chown root testuid
chmod u+s testuid

Yang terakhir mengatur bit setuid. Jika sekarang Anda menjalankan ./testuidsebagai pengguna normal, Anda akan melihat proses secara default berjalan dengan efektif 0, root.

Bagaimana dengan skrip?

Ini bervariasi dari platform ke platform , tetapi di Linux, hal-hal yang memerlukan penerjemah, termasuk bytecode, tidak dapat menggunakan bit setuid kecuali diatur pada penerjemah (yang akan sangat sangat bodoh). Berikut skrip perl sederhana yang meniru kode C di atas:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n"; 

Sesuai dengan akar * nixynya, perl telah membangun variabel khusus untuk uid ( $>) dan uid nyata ( $<). Tetapi jika Anda mencoba yang sama chowndan chmoddigunakan dengan executable yang dikompilasi (dari C, contoh sebelumnya), itu tidak akan membuat perbedaan. Script tidak bisa mendapatkan hak istimewa.

Jawabannya adalah dengan menggunakan biner setuid untuk menjalankan skrip:

#include <stdio.h>
#include <unistd.h> 

int main (int argc, char *argv[]) {
    if (argc < 2) {
        puts("Path to perl script required.");
        return 1;
    }
    const char *perl = "perl";
    argv[0] = (char*)perl;
    return execv("/usr/bin/perl", argv);
}

Kompilasi ini gcc --std=c99 whatever.c -o perlsuid, lalu chown root perlsuid && chmod u+s perlsuid. Anda sekarang dapat mengeksekusi skrip perl apa saja dengan uid efektif 0, terlepas dari siapa pemiliknya.

Strategi serupa akan bekerja dengan php, python, dll. Tapi ...

# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face

TOLONG TOLONG JANGAN tinggalkan barang seperti ini . Kemungkinan besar, Anda sebenarnya ingin mengkompilasi dalam nama skrip sebagai jalur absolut , yaitu, ganti semua kode main()dengan:

    const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/perl", (char * const*)args);

Mereka memastikan /opt/suid_scriptsdan semua yang ada di dalamnya adalah read-only untuk pengguna non-root. Kalau tidak, seseorang bisa bertukar apa pun untuk whatever.pl.

Selain itu, berhati-hatilah karena banyak bahasa skrip memungkinkan variabel lingkungan mengubah cara mereka mengeksekusi skrip . Misalnya, variabel lingkungan dapat menyebabkan pustaka yang disediakan oleh pemanggil dimuat, yang memungkinkan pemanggil untuk mengeksekusi kode arbitrer sebagai root. Jadi, kecuali Anda tahu bahwa baik juru bahasa dan skrip itu sendiri kuat terhadap semua variabel lingkungan yang mungkin, JANGAN LAKUKAN INI .

Jadi, apa yang harus saya lakukan?

Cara yang lebih aman untuk memungkinkan pengguna non-root menjalankan skrip sebagai root adalah dengan menambahkan aturan sudo dan meminta pengguna menjalankannya sudo /path/to/script. Sudo menghapus sebagian besar variabel lingkungan, dan juga memungkinkan administrator memilih siapa yang dapat menjalankan perintah dan dengan argumen apa. Lihat Bagaimana menjalankan program tertentu sebagai root tanpa prompt kata sandi? sebagai contoh.

goldilocks
sumber
1
banyak cangkang akan berperilaku berbeda jika disebut sebagai -privilegedcangkang dan, jika dipanggil dari skrip yang bertanggung jawab, juga dapat dengan aman dipanggil dengan suid rootcara itu. Saya takut bahwa gagasan tentang skrip yang bertanggung jawab adalah semacam pipedream di dunia nyata. Pokoknya, mungkin perlu disebutkan betapa pentingnya untuk menghindari menulis segala macam jika mungkin sementara izin ditingkatkan ...
mikeserv
@ mikeserv Saya tidak akrab dengan konsep " -privilegedshell" dan "script yang bertanggung jawab". Apakah Anda ingin melakukan jawaban lain tentang cangkang? Saya tidak bisa menjanjikan Anda tanda centang;) Tidak ada banyak alasan bagus untuk melakukan ini - sudoadalah pilihan yang jauh lebih baik jika memungkinkan - saya menulisnya sebagai jawaban umum yang berasal dari pertanyaan awal tentang otentikasi kata sandi sistem di php .
goldilocks
1
Aku tidak benar-benar ingin melakukan itu - skrip yang bertanggung jawab adalah benar-benar keras - di luar saya, mungkin. Tetapi saya harus mengatakan saya tidak setuju dengan analisis Anda tentang sudo- saya anggap sudosebagai psikotik karena berpura-pura tidak seperti itu root. Anda bahkan dapat menempatkan gumpalan shell di sudoers. sumenurut saya lebih baik untuk keperluan scripting, meskipun sudobagus untuk aplikasi live. Tapi tidak adalah sebagai eksplisit sebagai skrip dengan #!/bin/ksh -pbangline atau apa pun suid kshdi $PATHadalah.
mikeserv
Meskipun jawaban ini memang menjawab pertanyaan itu, skrip setuid adalah resep untuk membuka diri Anda terhadap kerentanan eskalasi hak istimewa lokal. Ada alasan mengapa skrip tidak dapat dibuat setuid dengan mudah. Jawaban yang benar di sini adalah sudo.
mdadm
Saya mengambil kebebasan untuk mengedit jawaban Anda untuk setidaknya menyebutkan kerentanan besar dengan variabel lingkungan. Saya tidak suka ini sebagai jawaban kanonik karena masuk ke banyak penyimpangan pada metode yang tidak aman (pembungkus setuid untuk skrip). Akan lebih baik untuk merekomendasikan sudo dan meninggalkan diskusi panjang untuk utas lain (saya membahasnya di sana , omong-omong).
Gilles 'SANGAT berhenti menjadi jahat'
11

Ya Anda bisa, tetapi itu mungkin ide yang sangat buruk . Biasanya, Anda tidak akan menetapkan SUID menggigit langsung di eksekusi Anda, tetapi gunakan sudo (8) atau su (1) untuk melaksanakannya (dan batas yang bisa melaksanakannya)

Namun perlu dicatat, ada banyak masalah keamanan yang memungkinkan pengguna biasa menjalankan program (dan terutama skrip!) Sebagai root. Sebagian besar dari mereka harus melakukan itu kecuali program itu ditulis secara khusus dan sangat hati-hati untuk mengatasinya, pengguna jahat dapat menggunakannya untuk mendapatkan shell root dengan mudah.

Jadi, bahkan jika Anda melakukannya, itu akan menjadi ide yang sangat baik untuk pertama-tama membersihkan SEMUA input ke program yang ingin Anda jalankan sebagai root, termasuk lingkungannya, parameter baris perintah, STDIN dan semua file yang diproses, data yang berasal dari koneksi jaringan itu terbuka, dll. Ini sangat sulit untuk dilakukan, dan bahkan lebih sulit untuk melakukannya dengan benar.

Misalnya, Anda dapat mengizinkan pengguna yang hanya mengedit beberapa file dengan editor. Tetapi editor memungkinkan eksekusi perintah dari bantuan eksternal atau penangguhan, sehingga memberikan pengguna root shell untuk melakukan apa yang mereka inginkan. Atau Anda mungkin menjalankan skrip shell yang terlihat sangat aman bagi Anda, tetapi pengguna dapat menjalankannya dengan $ IFS yang dimodifikasi atau variabel lingkungan lainnya yang benar-benar mengubah perilaku itu. Atau mungkin skrip PHP yang seharusnya mengeksekusi "ls", tetapi pengguna menjalankannya dengan $ PATH yang dimodifikasi dan dengan demikian membuatnya menjalankan sebuah shell yang ia beri nama "ls". Atau puluhan masalah keamanan lainnya.

Jika program benar-benar harus melakukan sebagian pekerjaannya sebagai root, mungkin akan lebih baik untuk memodifikasinya sehingga berjalan seperti pengguna normal, tetapi jalankan saja (setelah membersihkan sebanyak mungkin) bagian yang benar-benar membutuhkan root melalui program pembantu suid.

Perhatikan bahwa meskipun Anda benar-benar mempercayai semua pengguna lokal Anda (seperti, Anda memberi mereka kata sandi root), itu adalah ide yang buruk, karena pengawasan keamanan yang tidak disengaja oleh APAPUN dari mereka (seperti, memiliki skrip PHP online, atau kata sandi yang lemah, atau terserah) tiba-tiba memungkinkan penyerang jarak jauh untuk sepenuhnya kompromi seluruh mesin.

Matija Nalis
sumber
1
diperbantukan. lihat contoh doa aman dalam seri programmer POSIX untuk man sh- dan bahkan yang gagal melakukannya command -p env -i .... Lagipula ini cukup sulit untuk dilakukan. Namun, jika dilakukan dengan benar, untuk tujuan eksplisit, bisa lebih baik bagi suidpenerjemah yang cakap daripada menggunakan suatau sudo- karena perilaku salah satu dari mereka akan selalu berada di luar kendali naskah (meskipun sulebih kecil kemungkinannya untuk melempar bola lengkungan karena cenderung menjadi sedikit kurang gila) . Membundel seluruh paket adalah satu-satunya taruhan yang pasti - lebih baik pastikan itu saja.
mikeserv