Вплив на безпеку роботи perl -ne '…' *


27

Мабуть, працює:

perl -n -e 'some perl code' *

Або

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

(те саме з -pзамість -n)

Або

perl -e 'some code using <>' *

Часто зустрічається в одностроях, розміщених на цьому веб-сайті, має наслідки для безпеки. Яка угода? Як цього уникнути?

Відповіді:


33

В чому проблема

По-перше, як і для багатьох утиліт, у вас виникне проблема з іменами файлів, починаючи з -. Перебуваючи в:

sh -c 'inline sh script here' other args

Інші аргументи передаються до inline sh script; з perlеквівалентом,

perl -e 'inline perl script here' other args

Інші аргументи перевіряються на наявність більш варіантів PERL перший, не інлайн сценарію. Так, наприклад, якщо -eBEGIN{do something evil}в поточному каталозі є файл, який називається ,

perl -ne 'inline perl script here;' *

(з чи без -n) зробить щось зле.

Як і для інших утиліт, вирішення цього завдання - використання маркера кінця параметрів ( --):

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

Але навіть тоді це все ще небезпечно, і це стосується <>оператора, який використовує -n/ -p.

Проблема пояснюється в perldoc perlopдокументації.

Цей спеціальний оператор використовується для зчитування одного рядка (один запис; записи за замовчуванням є рядками) вводу, де цей вхід надходить з кожного переданого аргументу по черзі @ARGV.

В:

perl -pe '' a b

-pпередбачає while (<>)цикл навколо коду (тут порожній).

<>спочатку відкриється a, прочитає записи по одному рядку, поки файл не вичерпається, а потім відкриється b...

Проблема полягає в тому, що для відкриття файлу він використовує першу, небезпечну форму open:

open ARGV, "the file as provided"

З цією формою, якщо аргумент є

  • "> afile", відкривається afileв режимі письма,
  • "cmd|", він працює cmdі читає його вихід.
  • "|cmd", у вас відкритий потік для запису на вхід cmd.

Так, наприклад:

perl -pe '' 'uname|'

Не виводить вміст файла, що називається uname|(ідеально правильне ім'я файлу btw), але вихід unameкоманди.

Якщо ви працюєте:

perl -ne 'something' -- *

І хтось створив файл з назвою rm -rf "$HOME"|(знову-таки цілком правильне ім'я файлу) у поточному каталозі (наприклад, через те, що цей каталог колись могли записувати інші, або ви витягнули хитромудрий архів, або ви виконали якусь команду dodgy, або була використана інша вразливість у якомусь іншому програмному забезпеченні), то ви великі проблеми. Сфери, де важливо знати про цю проблему, - це інструменти, які автоматично обробляють файли в публічних областях /tmp(наприклад, інструменти, які можуть бути викликані такими інструментами).

Файли називаються > foo, foo|, |fooє проблемою. Але в меншій мірі < fooі fooз провідними або кінцевими символами інтервалів ASCII (включаючи пробіл, вкладку, новий рядок, cr ...), а це означає, що ці файли не будуть оброблені, або неправильний буде.

Також майте на увазі, що деякі символи в деяких багатобайтових наборах символів (наприклад, ǖу BIG5-HKSCS) закінчуються на байт 0x7c, кодування |.

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

Тож у локалі, що використовують цю гарнітуру,

 perl -pe '' ./nǖ

Спробував би запустити ./n\x88команду , як perlби НЕ намагатися інтерпретувати це ім'я файлу локалі користувача!

Як виправити / обійти

AFAIK, ви нічого не можете зробити, щоб змінити таку небезпечну поведінку за замовчуванням perlраз і назавжди для всієї системи.

По-перше, проблема виникає лише з символами на початку та в кінці імені файлу. Отже, хоча perl -ne '' *або perl -ne '' *.txtє проблемою,

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

не тому , що всі аргументи тепер починаються з ./і закінчуються .txt(так не -, <, >, |, космос ...). В цілому, це хороша ідея , щоб Приставка кульки з ./. Це також дозволяє уникнути проблем з файлами, що викликаються -або починаються з -багатьох інших утиліт (і ось, це означає, що вам більше не потрібен --маркер кінця параметрів ( ).

Використання -Tдля включення taintрежиму допомагає певною мірою. Вона скасує команду, якщо зустрінеться такий шкідливий файл (лише для випадків >і |випадків, а не <пробілів).

Це корисно при інтерактивному використанні таких команд, оскільки це попереджає про те, що відбувається щось хитре. Це може виявитися небажаним при автоматичній обробці, оскільки це означає, що хтось може зробити цю обробку невдалою, просто створивши файл.

Якщо ви хочете обробити кожен файл, незалежно від їх імені, ви можете використовувати в ARGV::readonly perlмодуль на CPAN ( до жаль , як правило , не встановлюється за умовчанням). Це дуже короткий модуль, який робить:

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
   };
};

В основному, це sanitizes @ARGV, перетворюючи, " foo|"наприклад, на "< ./ foo|\0".

Ви можете зробити те ж саме у BEGINзаяві у вашій perl -n/-pкоманді:

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

Тут ми спростимо його на основі припущення, ./яке використовується.

Побічним ефектом цього (та ARGV::readonly), хоча, є те, що $ARGVв your code hereпоказах, що слідує символ NUL.

Оновити 2015-06-03

perlv5.21.5 і вище є новий <<>>оператор, який поводиться так, <>за винятком того, що він не буде виконувати цю спеціальну обробку. Аргументи вважатимуться лише іменами файлів. Тож із цими версіями тепер можна писати:

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

(не забудьте --або не використовуйте ./*), не боячись перезаписати файли або виконати несподівані команди.

-n/ -pвсе ж використовуйте небезпечну <>форму. І будьте обережні посилання, як і раніше, дотримуйтесь, так що це не означає, що це безпечно використовувати у ненадійних каталогах.


2
ви працювали над цим цілий день, я буду робити ставку. молодець.
mikeserv

2
приємне оновлення до perl, але дивно, що розробники perl не додали параметри -P та -N, щоб використовувати його (не можуть змінити існуючі -p та -n, оскільки деякі сценарії можуть покладатися на небезпечну поведінку)
cas

9

Окрім відповіді @ Stéphane Chazelas , нам не потрібно турбуватися з цього питання, якщо ми будемо використовувати -iпараметр командного рядка:

$ perl -pe '' 'uname|'
Linux

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

Тому що при використанні -iопції perlвикористовується stat для перевірки стану файлу перед його обробкою:

$ 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.

1
Чи не можлива умова перегону між statперевіркою та ефективною обробкою perl, що відбувається відразу після?
Тотор

@Totor: Я думаю, що ні.
cuonglm

Це не про stat. Просто -iредагувати файли на місці, тому не має сенсу приймати аргументи, окрім фактичних шляхів до файлів, тому -i, спеціальна обробка не робиться.
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.