Чи можна безпечно використовувати `find -exec sh -c`?


30

Я намагаюся використовувати , findщоб echo 0в деякі файли, але , мабуть , це працює тільки з sh -c:

find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' \;

Але використання sh -cз допомогою find -execмене відчуває себе дуже непросто, тому що я підозрюю проблеми з цитуванням. Я трохи посварився з цим і, мабуть, мої підозри були виправдані:

  • Моя тестова установка:

    martin@dogmeat ~ % cd findtest 
    martin@dogmeat ~/findtest % echo one > file\ with\ spaces
    martin@dogmeat ~/findtest % echo two > file\ with\ \'single\ quotes\'
    martin@dogmeat ~/findtest % echo three > file\ with\ \"double\ quotes\"
    martin@dogmeat ~/findtest % ll
    insgesamt 12K
    -rw-rw-r-- 1 martin martin 6 Sep 17 12:01 file with "double quotes"
    -rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with 'single quotes'
    -rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with spaces
  • Використання find -execбез, sh -cздається, працює без проблем - тут не потрібно цитувати:

    martin@dogmeat ~ % find findtest -type f -exec cat {} \;
    one
    two
    three
  • Але коли я використовую, sh -c {}схоже, потрібне якесь цитування:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat {}' \;
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: spaces: No such file or directory
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: single quotes: No such file or directory
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: double quotes: No such file or directory
  • Подвійні лапки працюють до тих пір, поки жодне ім'я файлу не містить подвійних лапок:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat "{}"' \;
    one
    two
    cat: findtest/file with double: No such file or directory
    cat: quotes: No such file or directory
  • Одинові лапки працюють до тих пір, поки жодне ім'я файлу не містить одиничних лапок:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c "cat '{}'" \;
    one
    cat: findtest/file with single: No such file or directory
    cat: quotes: No such file or directory
    three

Я не знайшов рішення, яке працює у всіх випадках. Чи є щось, що я не помічаю, або використовую sh -cпо find -execсвоїй суті небезпечно?

Відповіді:


41

Ніколи не вкладайте {}код оболонки! Це створює вразливість введення команд. Зверніть увагу , що для cat "{}", це не тільки про "персонажах, \, `, $також є проблемою (розглянути, наприклад , файл з ім'ям ./$(reboot)/accept_ra).

(до речі, деякі findреалізації не дозволяють вам це робити, а POSIX залишає поведінку не визначеною, коли {}вона не є власною в аргументі find)

Тут ви хочете передати назви файлів як окремі аргументи sh(не в аргументі коду ) та shвбудований скрипт ( аргумент коду ) для посилання на них за допомогою позиційних параметрів:

find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} \;

Або щоб уникнути запуску одного shфайлу:

find . -name accept_ra -exec sh -c 'for file do
  echo 0 > "$file"; done' sh {} +

Те саме стосується xargs -I{}або zshs zargs -I{}. Не пишіть:

<list.txt xargs -I {} sh -c 'cmd> {}'

Що було б вразливістю командної ін'єкції таким же чином, як і findвище, але:

<list.txt xargs sh -c 'for file do cmd > "$file"; done' sh

Що також має перевагу уникнути запуску одного shфайлу та помилки, коли list.txtвін не містить жодного файлу.

З zshzargs, ви, ймовірно, хочете використовувати функцію, а не викликати sh -c:

do-it() cmd > $1
zargs ./*.txt -- do-it

Зауважте, що у всіх прикладах, наведених вище, другий shвище входить в сценарій вбудованого сценарію $0. Ви повинні використовувати що - то відповідне там (як shі find-sh), а не речі , як _, -, --або порожній рядок, в якості значення в $0використовується для повідомлень про помилки оболонкових:

$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} \;
inline-sh: ./accept_ra: Permission denied

GNU parallelпрацює інакше. З його допомогою ви не хочете використовувати так, sh -cяк parallelвже запускає оболонку, і намагається замінити {}аргументом, наведеним у правильному синтаксисі оболонки .

<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'

то буде другий , shздається, якийсь - то заповнювач, він теж працює , якщо замінений _, наприклад , - дуже корисно , якщо ви хочете викликати Баш нутрощів: find /tmp -name 'fil*' -exec bash -c 'printf "%q\n" "$1"' _ {} \;. Але чи хтось знає, де це зафіксовано?
Флоріан Фіда

1
@FlorianFida Першим аргументом оболонки стає $0(зазвичай це назва оболонки. Вам потрібно пропустити її за цим сценарієм, щоб вона не з'їла жодного з ваших звичайних позиційних аргументів. Документація для -cцього згадує.
Етан Рейснер,


1
@phk, цього argv[0]тут немає, це лише $0сценарій. Сторінка Свена тут неточна, а rоболонка не змусить входити до обмеженого режиму, наскільки це можливо, і zshне змінюватиме режим на основі $0. (exec -a rksh ksh -c 'cd /')буде працювати з обмеженням ksh, але ні ksh -c 'cd /' rksh).
Стефан Шазелас

1
Стефане, якщо ви не заперечуєте, чи є ваша відповідь, яка пояснює "чому б не вставити {} в код оболонки"? Я хотів, хоча всі ваші відповіді знайшов, але не зміг знайти жодної, хоча я можу поклястись, що побачив деякі речі, які ви написали на цю тему, але не можу пригадати, чи це була відповідь, коментар у чаті чи на іншому веб-сайті, наприклад compunix ... Якщо / коли у вас є час, НЕ могли б ви розширення на «ніколи не вбудовувати {}» частина або ви думаєте , що ми повинні мати спеціальний Q , наприклад , « з точки зору безпеки використання findз -exec sh -cі вбудовування {}в коді оболонки» ?
don_crissti
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.