«Запустите любую команду, которая передаст ненадежные данные командам, которые интерпретируют аргументы как команды»

18

Из руководства по findutils:

Например, такие конструкции, как эти две команды

# risky
find -exec sh -c "something {}" \;
find -execdir sh -c "something {}" \;

очень опасны Причина этого заключается в том, что символ {{} расширяется до имени файла, которое может содержать точку с запятой или другие символы, специальные для оболочки. Если, например, кто-то создает файл, /tmp/foo; rm -rf $HOMEдве вышеуказанные команды могут удалить чей-то домашний каталог.

Поэтому по этой причине не запускайте команды, которые передадут недоверенные данные (например, имена файлов) командам, которые интерпретируют аргументы как команды для дальнейшей интерпретации (например, 'sh').

В случае с оболочкой есть умное решение этой проблемы:

# safer
find -exec sh -c 'something "$@"' sh {} \;
find -execdir sh -c 'something "$@"' sh {} \;

Этот подход не гарантирует избежание любой проблемы, но он намного безопаснее, чем подстановка данных по выбору злоумышленника в текст команды оболочки.

  1. Является ли причиной проблемы find -exec sh -c "something {}" \;то, что замена для {}не заключена в кавычки и поэтому не рассматривается как одна строка?
  2. В решении find -exec sh -c 'something "$@"' sh {} \;,

    • first {}заменяется, но поскольку {}он не "$@"заключен в кавычки, не имеет ли он той же проблемы, что и исходная команда? Например, "$@"будет расширена "/tmp/foo;", "rm", "-rf"и "$HOME"?

    • почему {}не избежал или не цитируется?

  3. Не могли бы вы привести другие примеры (все еще с sh -cили без него, если это применимо; с или без, findкоторые могут быть необязательны), где применяются те же виды проблем и решений, и которые являются минимальными примерами, чтобы мы могли сосредоточиться на проблеме и решении с как можно меньше отвлекаться? См. Способы предоставления аргументов для команды, выполняемой `bash -c`

Благодарю.

Тим
источник
Связано: unix.stackexchange.com/questions/156008
Кусалананда

Ответы:

29

Это на самом деле не связано с цитированием, а скорее с обработкой аргументов.

Рассмотрим рискованный пример:

find -exec sh -c "something {}" \;
  • Это обрабатывается интерпретатором, и разбит на шесть слов: find, -exec, sh, -c, something {}(без кавычек больше), ;. Там нет ничего, чтобы расширяться. Оболочка работает findс этими шестью словами в качестве аргументов.

  • Когда findчто - то находит в процессе, скажем foo; rm -rf $HOME, он заменяет {}с foo; rm -rf $HOME, и работает shс аргументами sh, -cи something foo; rm -rf $HOME.

  • shтеперь видит -c, и в результате анализирует something foo; rm -rf $HOME( первый аргумент без опций ) и выполняет результат.

Теперь рассмотрим более безопасный вариант:

find -exec sh -c 'something "$@"' sh {} \;
  • Оболочка работает findс аргументами find, -exec, sh, -c, something "$@", sh, {}, ;.

  • Теперь , когда findнаходки foo; rm -rf $HOME, он заменяет {}снова, и работает shс аргументами sh, -c, something "$@", sh, foo; rm -rf $HOME.

  • shвидит -cи анализирует something "$@"как команду для запуска shи foo; rm -rf $HOMEкак позиционные параметры ( начиная с$0 ), расширяется "$@"до foo; rm -rf $HOME единого значения и запускается somethingс одним аргументом foo; rm -rf $HOME.

Вы можете увидеть это с помощью printf. Создайте новый каталог, введите его и запустите

touch "hello; echo pwned"

Запуск первого варианта выполняется следующим образом

find -exec sh -c "printf \"Argument: %s\n\" {}" \;

производит

Argument: .
Argument: ./hello
pwned

тогда как второй вариант запускается как

find -exec sh -c 'printf "Argument: %s\n" "$@"' sh {} \;

производит

Argument: .
Argument: ./hello; echo pwned
Стивен Китт
источник
В более безопасном варианте, почему {}не экранированы или не указаны?
Тим
1
Это вообще не должно быть процитировано; его нужно будет заключать в кавычки только в том случае, если он имеет какое-то другое значение, и я не думаю, что это имеет место с какой-либо из основных оболочек, используемых в настоящее время.
Стивен Китт
От gnu.org/software/findutils/manual/html_mono/… : «Обе эти конструкции ( ;и {}) должны быть экранированы (с помощью« \ ») или заключены в кавычки, чтобы защитить их от расширения оболочкой». Вы имеете в виду, что это неправильно для Баш?
Тим
3
Я имею в виду, что это неправильно для оболочек в целом. Смотрите POSIX . Это конкретное утверждение в findutilsруководстве датируется как минимум 1996 годом ... Конечно, цитировать {}, как '{}'или "{}", не вредно, но в этом нет необходимости.
Стивен Китт
@Tim Вы столкнулись с общей ситуацией, когда ваша интуиция еще не учитывает тот факт, что здесь происходит два совершенно разных типа обработки аргументов: когда / как оболочка анализирует аргументы из строки текста (что где цитирование имеет значение) отличается от передачи необработанных аргументов операционной системы (где кавычки являются просто обычными символами). В команде shell find some/path -exec sh -c 'something "$@"' {} \;на самом деле есть три уровня обработки / передачи аргументов, два варианта оболочки и один базовый вариант операционной системы.
mtraceur
3

Часть 1:

find просто использует замену текста.

Да, если вы сделали это без кавычек, вот так:

find . -type f -exec sh -c "echo {}" \;

и злоумышленник смог создать файл с именем ; echo owned, то он будет выполнять

sh -c "echo ; echo owned"

что привело бы к оболочке работает echoтогда echo owned.

Но если вы добавили кавычки, злоумышленник может просто завершить ваши кавычки, а затем поместить вредоносную команду после нее, создав файл с именем '; echo owned:

find . -type f -exec sh -c "echo '{}'" \;

что приведет в управлении оболочки echo '', echo owned.

(Если вы заменили двойные кавычки на одинарные кавычки, злоумышленник также может использовать кавычки другого типа.)


Часть 2:

В find -exec sh -c 'something "$@"' sh {} \;, {}оболочка изначально не интерпретируется оболочкой, она выполняется напрямую execve, поэтому добавление кавычек оболочки не поможет.

find -exec sh -c 'something "$@"' sh "{}" \;

не имеет никакого эффекта, так как оболочка удаляет двойные кавычки перед запуском find.

find -exec sh -c 'something "$@"' sh "'{}'" \;

добавляет кавычки, которые оболочка не обрабатывает специально, поэтому в большинстве случаев это просто означает, что команда не будет делать то, что вы хотите.

Имея это расширяться /tmp/foo;, rm, -rf, $HOMEне должно быть проблемой, потому что те аргументы something, и , somethingвероятно , не относится к его аргументы как команды для выполнения.


Часть 3:

Я предполагаю, что аналогичные соображения применимы ко всему, что принимает ненадежный ввод и выполняет его как (часть) команды, например xargsи parallel.

Mikel
источник
xargsопасно только если -Iили -Jиспользуется. В обычной работе это только добавление аргументов в конец списка, как это -exec ... {} +делается.
Чарльз Даффи
1
parallelотличие от этого, по умолчанию оболочка запускается без явного запроса пользователя, что увеличивает уязвимую поверхность; этот вопрос был предметом многочисленных дискуссий; для объяснения см. unix.stackexchange.com/questions/349483/… и включенная ссылка на lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html ).
Чарльз Даффи
@CharlesDuffy Вы имеете в виду « уменьшение уязвимой поверхности». Атака, проиллюстрированная OP, действительно не работает по умолчанию с GNU Parallel.
Оле Танге,
1
Да, я знаю, что вам это нужно для контроля четности по SSH - если вы не создавали удаленный parallelпроцесс "приемника" на другом конце любого сокета, способный выполнять прямую работу без оболочки execv. Именно это я и сделал бы, если бы на вашем месте внедрял инструмент. То, что неинтерактивная программа ведет себя по-разному в зависимости от текущей стоимости, SHELLедва ли удивительно.
Чарльз Даффи
1
Да, я считаю, что каналы и перенаправления должны быть невозможны, если пользователь явно не запускает оболочку, и в этот момент он получает только явное поведение оболочки, которую он явно запустил. Если можно ожидать только того execv, что предоставляет, это проще и менее удивительно, и правило, которое не меняется в разных местах / средах выполнения.
Чарльз Даффи
3

1. Является ли причиной проблемы find -exec sh -c "something {}" \;то, что замена для {}не заключена в кавычки и поэтому не рассматривается как одна строка?

В некотором смысле, но цитирование здесь не может помочь . Имя файла, которое заменяется вместо, {}может содержать любые символы, включая кавычки . Независимо от того, какая форма цитирования использовалась, имя файла может содержать одно и то же, и «прерывать» цитирование.

2. ... но так {}как без кавычек, "$@"также не имеет той же проблемы, что и оригинальная команда? Например, "$@"будет расширен до "/tmp/foo;", "rm", "-rf", and "$HOME"?

Номер "$@"распространяется на позиционные параметры, как отдельные слова, и не разделяет их дальше. Здесь {}он является аргументом самого по findсебе и findпередает текущее имя файла также в качестве отдельного аргумента sh. Он непосредственно доступен как переменная в сценарии оболочки, он не обрабатывается как сама команда оболочки.

... почему {}не избежал или не цитируется?

Так не должно быть в большинстве оболочек. Если вы запускаете fish, это должно быть: fish -c 'echo {}'печатает пустую строку. Но это не имеет значения, если вы цитируете это, оболочка просто удалит кавычки.

3. Не могли бы вы привести другие примеры ...

Каждый раз, когда вы раскрываете имя файла (или другую неконтролируемую строку) как есть внутри строки, которая воспринимается как некоторый код (*) , существует вероятность выполнения произвольной команды.

Например, это расширяет $fнепосредственно код Perl и вызовет проблемы, если имя файла содержит двойные кавычки. Кавычка в имени файла заканчивает кавычку в коде Perl, а остальная часть имени файла может содержать любой код Perl:

touch '"; print "HELLO";"'
for f in ./*; do
    perl -le "print \"size: \" . -s \"$f\""
done

(Имя файла должно быть немного странным, так как Perl анализирует весь код заранее, прежде чем запускать какой-либо из них. Поэтому мы должны избегать ошибки разбора.)

Пока это безопасно проходит через аргумент:

for f in ./*; do
    perl -le 'print "size: " . -s $ARGV[0]' "$f"
done

(Не имеет смысла запускать другую оболочку непосредственно из оболочки, но если вы это сделаете, это похоже на find -exec sh ...случай)

(* некоторый код включает в себя SQL, поэтому обязательно XKCD: https://xkcd.com/327/ плюс объяснение: https://www.explainxkcd.com/wiki/index.php/Little_Bobby_Tables )

ilkkachu
источник
1

Ваша проблема как раз и является причиной, по которой GNU Parallel вводит кавычки:

touch "hello; echo pwned"
find . -print0 | parallel -0 printf \"Argument: %s\\n\" {}

Это не будет работать echo pwned.

Он запустит оболочку, так что если вы расширите свою команду, вы не получите неожиданный сюрприз:

# This _could_ be run without spawining a shell
parallel "grep -E 'a|bc' {}" ::: foo
# This cannot
parallel "grep -E 'a|bc' {} | wc" ::: foo

Дополнительные сведения о проблеме spawning-a-shell см. По адресу : https://www.gnu.org/software/parallel/parallel_design.html#Always-running-commands-in-a-shell.

Оле Танге
источник
1
... это, как говорится, find . -print0 | xargs -0 printf "Argument: %s\n"так же безопасно (или, скорее, более того, так как с -print0одной обрабатывает имена файлов с новой строки правильно); Цитирование параллельного интерфейса - это обходной путь для проблемы, которой вообще не существует, когда нет оболочки.
Чарльз Даффи
Но как только вы добавите | wcкоманду, вам нужно прыгать через обручи, чтобы сделать ее безопасной. GNU Parallel по умолчанию безопасен.
Оле Танге
ITYM: он пытается охватить каждую причину языка оболочки сложным процессом тщательного цитирования всего. Это не так безопасно, как правильно хранить данные в переменных, не смешанных с кодом. Теперь, если бы он автоматически конвертировал это foo {} | wcв sh -c 'foo "$1" | wcsh {} `, это могло бы быть иначе.
ilkkachu