Implikasi keamanan dari menjalankan perl -ne '...' *

27

Rupanya, berlari:

perl -n -e 'some perl code' *

Atau

find . ... -exec perl -n -e '...' {} +

(sama dengan -pbukannya -n)

Atau

perl -e 'some code using <>' *

sering ditemukan dalam satu baris yang diposting di situs ini, memiliki implikasi keamanan. Apa masalahnya? Bagaimana cara menghindarinya?

Stéphane Chazelas
sumber

Jawaban:

33

Apa masalahnya

Pertama, seperti banyak utilitas, Anda akan memiliki masalah dengan nama file yang dimulai dengan -. Sementara di:

sh -c 'inline sh script here' other args

Argumen lain diteruskan ke inline sh script; dengan yang perlsetara,

perl -e 'inline perl script here' other args

Argumen lain dipindai untuk opsi lebih lanjut untuk melakukan perl pertama, bukan ke skrip inline. Jadi, misalnya, jika ada file yang dipanggil -eBEGIN{do something evil}di direktori saat ini,

perl -ne 'inline perl script here;' *

(dengan atau tanpa -n) akan melakukan sesuatu yang jahat.

Seperti untuk utilitas lain, pekerjaan untuk itu adalah dengan menggunakan penanda opsi akhir ( --):

perl -ne 'inline perl script here;' -- *

Tetapi meskipun demikian, itu masih berbahaya dan itu tergantung pada <>operator yang digunakan oleh -n/ -p.

Masalahnya dijelaskan dalam perldoc perlopdokumentasi.

Itu operator khusus digunakan untuk membaca satu baris (satu catatan, catatan menjadi garis secara default) dari input, di mana input tersebut berasal dari masing-masing argumen pada gilirannya diteruskan @ARGV.

Di:

perl -pe '' a b

-pmenyiratkan while (<>)loop di sekitar kode (kosong di sini).

<>pertama akan membuka a, membaca catatan satu baris pada satu waktu sampai file habis dan kemudian membuka b...

Masalahnya adalah, untuk membuka file, ia menggunakan bentuk pertama yang tidak aman dari open:

open ARGV, "the file as provided"

Dengan bentuk itu, jika argumennya adalah

  • "> afile", itu terbuka afiledalam mode penulisan,
  • "cmd|", ini berjalan cmddan membaca outputnya.
  • "|cmd", Anda memiliki aliran terbuka untuk menulis ke input cmd.

Jadi misalnya:

perl -pe '' 'uname|'

Tidak menampilkan konten dari file yang disebut uname|(nama file yang benar-benar valid btw), tetapi output dari unameperintah.

Jika Anda menjalankan:

perl -ne 'something' -- *

Dan seseorang telah membuat file yang disebut rm -rf "$HOME"|(lagi nama file yang benar-benar valid) di direktori saat ini (misalnya karena direktori itu pernah ditulisi oleh orang lain, atau Anda telah mengekstrak arsip yang cerdik, atau Anda telah menjalankan beberapa perintah yang cerdik, atau Kerentanan lain dalam beberapa perangkat lunak lain dieksploitasi), maka Anda dalam masalah besar. Area di mana penting untuk mengetahui masalah itu adalah alat yang memproses file secara otomatis di tempat umum area seperti /tmp(atau alat yang dapat disebut dengan alat tersebut).

File yang disebut > foo, foo|, |fooadalah masalah. Tetapi pada tingkat lebih rendah < foodanfoo dengan karakter spasi ASCII memimpin atau mengikuti (termasuk spasi, tab, baris baru, cr ...) serta itu berarti file-file itu tidak akan diproses atau yang salah akan.

Juga berhati-hatilah bahwa beberapa karakter dalam beberapa set karakter multi-byte (seperti ǖdalam BIG5-HKSCS) berakhir pada byte 0x7c, yang merupakan pengkodean dari |.

$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000  88  7c
        210   |
0000002

Jadi di lokal menggunakan charset itu,

 perl -pe '' ./nǖ

Akan mencoba menjalankan ./n\x88perintah karena tidakperl akan mencoba menafsirkan nama file di lokal pengguna!

Bagaimana cara memperbaikinya?

AFAIK, tidak ada yang dapat Anda lakukan untuk mengubah perilaku default yang tidak aman itu perluntuk semua sistem.

Pertama, masalah terjadi hanya dengan karakter di awal dan akhir nama file. Jadi, sementara perl -ne '' *atau perl -ne '' *.txtmasalah,

perl -ne 'some code' ./*.txt

tidak karena semua argumen sekarang mulai dengan ./dan akhir di .txt(jadi tidak -, <, >, |, ruang ...). Secara umum, ini adalah ide yang baik untuk awalan dengan gumpalan./ . Itu juga menghindari masalah dengan file yang dipanggil -atau mulai dengan -banyak utilitas lain (dan di sini, itu berarti Anda tidak memerlukan --penanda opsi-akhir ( ) lagi).

Menggunakan -Tuntuk mengaktifkan taintmode membantu sampai batas tertentu. Ini akan membatalkan perintah jika file berbahaya tersebut ditemukan (hanya untuk >dan |kasus, bukan <atau spasi putih sekalipun).

Itu berguna ketika menggunakan perintah seperti itu secara interaktif yang memperingatkan Anda bahwa ada sesuatu yang cerdik terjadi. Itu mungkin tidak diinginkan ketika melakukan beberapa pemrosesan otomatis, karena itu berarti seseorang dapat membuat pemrosesan itu gagal hanya dengan membuat file.

Jika Anda ingin memproses setiap file, terlepas dari nama mereka, Anda dapat menggunakan yang ARGV::readonly perlmodul CPAN (sayangnya biasanya tidak terinstal secara default). Itu adalah modul yang sangat singkat:

sub import{
   # Tom Christiansen in Message-ID: <24692.1217339882@chthon>
   # reccomends essentially the following:
   for (@ARGV){
       s/^(\s+)/.\/$1/;   # leading whitespace preserved
       s/^/< /;       # force open for input
       $_.=qq/\0/;    # trailing whitespace preserved & pipes forbidden
   };
};

Pada dasarnya, itu membersihkan @ ARGV dengan mengubah " foo|"misalnya menjadi "< ./ foo|\0".

Anda dapat melakukan hal yang sama dalam BEGINpernyataan di perl -n/-pperintah Anda :

perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*

Di sini kita menyederhanakannya dengan asumsi itu ./ sedang digunakan.

Efek samping dari itu (dan ARGV::readonly) adalah bahwa $ARGVdalamyour code here menunjukkan bahwa karakter NUL tertinggal.

Perbarui 2015-06-03

perlv5.21.5 dan di atasnya memiliki <<>>operator baru yang berperilaku seperti <>kecuali bahwa itu tidak akan melakukan pemrosesan khusus. Argumen hanya akan dianggap sebagai nama file. Jadi dengan versi-versi itu, Anda sekarang dapat menulis:

perl -e 'while(<<>>){ ...;}' -- *

(jangan lupa --atau gunakan ./*meskipun) tanpa takut akan menimpa file atau menjalankan perintah yang tidak terduga.

-nSaya -pmasih menggunakan <>bentuk berbahaya sekalipun. Dan berhati-hatilah symlink masih diikuti, sehingga tidak berarti aman untuk digunakan di direktori yang tidak terpercaya.

Stéphane Chazelas
sumber
2
Anda telah bekerja pada itu sepanjang hari, saya akan bertaruh. sudah selesai dilakukan dengan baik.
mikeserv
2
pembaruan yang bagus untuk perl, tetapi aneh bahwa dev perl tidak menambahkan opsi -P dan -N untuk memanfaatkannya (tidak dapat mengubah -p dan -n yang ada karena beberapa skrip mungkin bergantung pada perilaku tidak aman)
cas
9

Selain jawaban @ Stéphane Chazelas , kami tidak perlu khawatir tentang masalah ini jika menggunakan -iopsi baris perintah:

$ perl -pe '' 'uname|'
Linux

$ perl -i -pe '' 'uname|'
Can't open uname|: No such file or directory.

Karena ketika menggunakan -iopsi, perlgunakan stat untuk memeriksa status file sebelum memprosesnya:

$ strace -fe trace=stat perl -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
Process 6106 attached
Linux
Process 6105 suspended
Process 6105 resumed
Process 6106 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

$ strace -fe trace=stat perl -i -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("uname|", 0x785f40)                = -1 ENOENT (No such file or directory)
Can't open uname|: No such file or directory.
cuonglm
sumber
1
Apakah tidak ada kemungkinan kondisi balapan antara statcek dan pemrosesan perl yang efektif terjadi setelahnya?
Totor
@ Motor: Saya kira tidak.
cuonglm
Ini bukan tentang stat. Hanya saja -iuntuk mengedit file di tempat, jadi tidak masuk akal untuk menerima argumen selain jalur file yang sebenarnya, jadi dengan itu -i, pemrosesan khusus tidak dilakukan.
Stéphane Chazelas