Perlindungan perintah shell dengan variabel string

9

Dalam bahasa pemrograman, saya menjalankan perintah shell sederhana

cd var; echo > create_a_file_here

dengan var menjadi variabel yang berisi string (semoga) direktori ke tempat di mana saya ingin membuat file "create_a_file_here". Sekarang jika seseorang melihat baris kode ini, dimungkinkan untuk mengeksploitasinya dengan menetapkan misalnya:

var = "; rm -rf /"

Segalanya bisa menjadi sangat jelek. Salah satu cara untuk menghindari kasus di atas adalah mungkin mencari string dalam var untuk beberapa karakter khusus seperti ';' sebelum menjalankan perintah shell, tetapi saya ragu ini mencakup semua kemungkinan exploit.

Adakah yang tahu cara yang baik untuk memastikan bahwa "cd var" hanya mengubah direktori dan tidak ada yang lain?

ES___
sumber
4
Bergantung pada bagaimana Anda dapat memanggil shell, Anda juga dapat memberikan varargumen. Misalnya memanggil shdengan argumen -c, 'cd "$1"; echo > create_a_file_here', 'sh', varkarya dan tidak perlu perubahan apapun var. The 'sh'argumen dilewatkan sebagai $0.
ipsec
1
Bahasa pemrograman apa? Apakah Anda menggunakan POSIX sh, atau membuat bahasa pemrograman Anda sendiri dengan sintaksis yang sama tetapi yang berkembang varbukannya mengharuskan Anda untuk menulis cd "$var"? Atau bashdengan ini shopt -s cdable_vars? Oh, saya pikir maksud Anda beberapa program lain menggunakan shell untuk menjalankan perintah ini. Jadi var, kutip saja , tapi pastikan itu tidak termasuk karakter kutipan sendiri ...
Peter Cordes
@PeterCordes Jika Anda berbicara tentang Bash, variabel kuotasi ganda yang berisi kuotasi ganda baik-baik saja. misalnya s='"'; echo "$s"cetak ".
wjandrea
@WJAndrea: ya, tapi tidak ada kutipan "kartu truf" yang tidak dapat dikalahkan saat membangun tugas variabel dari input yang tidak dipercaya. Oh, solusi: lakukan var=untrusted stringdi program induk, demikian varjuga variabel lingkungan yang sudah ditetapkan saat memohon sh. Maka Anda hanya perlu mengutipnya setiap kali Anda mengembangkannya, yang mungkin dilakukan dengan andal. Ah, saya melihat bahwa ide itu sudah menjadi bagian dari jawaban Stéphane>. <
Peter Cordes

Jawaban:

9

Jika saya mengerti benar, varadalah variabel dalam bahasa pemrograman Anda.

Dan dalam bahasa pemrograman Anda, Anda meminta shell untuk menafsirkan string yang merupakan gabungan dari "cd ", isi dari variabel itu dan "; echo > create_a_file_here".

Jika demikian, ya, jika konten vartidak dikontrol dengan ketat, ini adalah kerentanan injeksi perintah.

Anda dapat mencoba dan mengutip dengan benar konten variabel¹ dalam sintaksis shell sehingga dijamin akan diteruskan sebagai argumen tunggal ke cdbuiltin.

Pendekatan lain adalah dengan melewatkan konten variabel itu dengan cara lain. Cara yang jelas adalah dengan melewatkannya dalam variabel lingkungan. Misalnya, dalam C:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

Kali ini, kode yang Anda minta untuk ditafsirkan oleh shell sudah diperbaiki, kami masih perlu menuliskannya dengan benar di sintaksis shell (di sini diasumsikan sebagai shell yang sesuai dengan POSIX):

  • Perluasan variabel shell harus dikutip untuk mencegah split + glob
  • Anda perlu -Puntuk cdmelakukan sederhanachdir()
  • Anda perlu --menandai akhir opsi untuk menghindari masalah dengan varmemulai dengan -(atau +dalam beberapa shell)
  • Kami menetapkan CDPATHke string kosong jika itu di lingkungan
  • Kami hanya menjalankan echoperintah jika cdberhasil.

Ada (setidaknya) satu masalah yang tersisa: jika varadalah -, tidak melakukan chdir ke direktori yang disebut -tapi dengan sebelumnya direktori (yang disimpan dalam $OLDPWD) dan OLDPWD=- CDPATH= cd -P -- "$DIR"tidak dijamin untuk bekerja di sekitarnya. Jadi Anda akan membutuhkan sesuatu seperti:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

¹ Perhatikan bahwa hanya melakukan system(concat("cd \"", var, "\"; echo..."));adalah tidak cara untuk pergi, Anda akan hanya bergerak masalah.

Misalnya, var = "$(rm -rf /)"masih akan menjadi masalah.

Satu- satunya cara yang dapat diandalkan untuk mengutip teks untuk cangkang mirip Bourne adalah dengan menggunakan tanda kutip tunggal dan juga mengurus tanda kutip tunggal yang mungkin terjadi dalam string. Misalnya, mengubah char *var = "ab'cd"ke char *escaped_var = "'ab'\\''cd'". Artinya, ganti semua 'untuk '\''dan bungkus semuanya di dalam '...'.

Yang masih mengasumsikan bahwa string yang dikutip tidak digunakan dalam tanda kutip mundur, dan Anda masih memerlukan --, -P, &&, CDPATH=...

Stéphane Chazelas
sumber
12

Solusi sederhana: Jangan panggil shell dari program Anda. Sama sekali.

Contoh Anda di sini sepele, mengubah direktori dan membuat file harus mudah dalam bahasa pemrograman apa pun. Tetapi bahkan jika Anda perlu menjalankan perintah eksternal, biasanya tidak perlu melakukannya melalui shell.

Jadi, misalnya dalam Python, alih-alih berjalan os.system("somecmd " + somearg), gunakan subprocess.run(["somecmd", somearg]). Di C, alih-alih system(), gunakan fork()dan exec()(atau temukan perpustakaan yang melakukannya).

Jika Anda perlu menggunakan shell, kutip argumen baris perintah, atau sampaikan melalui lingkungan, seperti dalam jawaban Stéphane . Selain itu, jika Anda mengkhawatirkan karakter khusus, solusi yang tepat adalah tidak mencoba memfilter (daftar hitam) karakter yang berpotensi berbahaya, tetapi hanya membuat karakter yang dikenal aman (daftar putih).

Hanya izinkan karakter yang fungsinya Anda ketahui, dengan cara itu ada sedikit risiko kehilangan sesuatu. Hasil akhirnya mungkin Anda hanya memutuskan untuk mengizinkan [a-zA-Z0-9_], tetapi itu mungkin cukup untuk menyelesaikan pekerjaan. Anda mungkin juga ingin memeriksa bahwa lokal dan perangkat Anda tidak menyertakan huruf beraksen seperti ädan ödi dalamnya. Mereka mungkin tidak dianggap spesial oleh shell mana pun, tapi sekali lagi, lebih baik memastikan apakah mereka lulus atau tidak.

ilkkachu
sumber
1
Dan pastikan apa pun yang Anda gunakan untuk mencocokkan [a-zA-Z0-9]tidak termasuk hal-hal seperti àyang pengkodeannya juga bisa disalahartikan oleh beberapa shell (seperti bash) di beberapa lokal.
Stéphane Chazelas
@ StéphaneChazelas (tidak tertarik :) apakah mereka (dan hanya di bawah lokasi yang terpengaruh?)
Wilf
10

Dalam bahasa pemrograman, harus ada cara yang lebih baik untuk melakukan sesuatu daripada mengeksekusi perintah shell. Misalnya, mengganti cd vardengan yang setara dengan bahasa pemrograman Anda chdir (var);harus memastikan bahwa setiap penipuan dengan nilai varhanya menghasilkan kesalahan "Direktori tidak ditemukan" daripada tindakan yang tidak diinginkan dan mungkin berbahaya.

Anda juga dapat menggunakan jalur absolut alih-alih mengubah direktori. Cukup gabungkan nama direktori, slash, dan nama file yang ingin Anda gunakan.

Di C, saya mungkin melakukan sesuatu seperti:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

Tentunya bahasa pemrograman Anda dapat melakukan hal serupa?

telcoM
sumber
Terima kasih atas jawaban Anda, tetapi sayangnya saya harus menggunakan solusi dengan perintah bash. Saya menguji mengutip variabel - seperti yang disarankan muru - dan tampaknya berhasil!
ES___