Почему и когда следует избегать использования eval в сценариях оболочки? [duplicate]

При обратном проектировании исполняемого файла часто используются несколько инструментов.

  1. Команда "файл", которая принимает путь к файлу в качестве первого параметра, так что вы можете определить (в большинстве случаев), какой тип исполняемого файла у вас есть.
  2. Дизассемблеры, которые ТОЧНО показывают, что делает исполняемый файл, но их трудно читать тем, кто не пишет ассемблерный код для этой конкретной архитектуры или имеет опыт дизассемблирования.
  3. Декомпиляторы, такие как Boomerang, Hex-ray и Snowman, могут обеспечить лучшую читаемость, но они не восстанавливают фактические имена переменных или синтаксис исходной программы, и они не на 100% надежны, особенно в тех случаях, когда инженеры, создавшие исполняемый файл протестирован с этими пакетами и попытался дополнительно скрыть безопасность.
  4. Диаграммы или таблицы потоков данных. Я не знаю бесплатного инструмента, который бы делал это автоматически, но скрипт Python или Bash поверх текстового анализатора вывода сборки (который может быть написан на sed или Perl) может быть полезным.
  5. Карандаш и бумага, хотите верьте, хотите нет, для набросков потоков и идей.

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

5
13.04.2017, 15:36
3 ответа

Вопросы

Когда целесообразно использовать?

Когда аргументы eval правильно заключены в кавычки (два уровня) и значения переменных, расширенные eval при первом синтаксическом анализе, не станут исполняемыми командами при втором синтаксическом анализе .

Почему использование eval - плохая идея?

Это не IMO (и этот вопрос требует мнений), если вы действительно знаете, что делаете.

Во-первых: проверьте, что (eval) того, что вы написали, соответствует вашим ожиданиям, заменив eval на echo и Во-вторых: хорошенько подумайте, какие переменные устанавливаются локально и какие значения они могут иметь.

Существуют ли какие-либо правила, согласно которым его можно использовать с полной и полной безопасностью, или это всегда и неизбежно дыра в безопасности?

Есть некоторые условия, при которых его можно использовать в полной и полной безопасности, да.

Но это всегда риск (например, риск написания неверного сценария).
Так получилось, что eval увеличивает риск до некоторой степени.

Это обязательно потребует подробного описания:

Подробности:

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

В принципе, она не более или менее опасна, чем любая другая команда (вспомните rm -rf / ). Он будет выполнен с текущими правами пользователя, поэтому дважды подумайте, прежде чем использовать его с правами root. Но это также верно для команды rm , показанной выше. Для ограниченного пользователя rm завершится ошибкой в ​​большинстве каталогов, принадлежащих пользователю root. Но rm по-прежнему опасная команда.

Известная идиома.

Команда eval очень полезна (и совершенно безопасна) в известных идиомах.
Например, это выражение выводит значение последнего позиционного параметра :

$ set -- "ls -l" "*" "c;date"

$ eval echo \"\$\{$#\}\"          ### command under test

c;date

Команда не является условно безопасной (как есть), потому что единственный возможный результат из $ # - число. И единственный возможный результат $ n (где n - число) - это содержимое такой позиционной переменной.

Если вы хотите увидеть, что делает команда, замените eval на echo

$ set -- "ls -l" "*" "c; date"
$ echo echo \"\$\{$#\}\"          ### command under test
echo "${3}"

А echo $ {3} - очень распространенная (и безопасная) идиома.

Мы все еще можем изменить некоторые цитаты и получить тот же результат:

$ echo echo '"${'$#\}\"
echo "${3}"
$ eval echo '"${'$#\}\"
c;date

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

Немного другая идиома.

$ b=book
$ book="A Tale of Two Cities"
$ eval 'a=$'"$b"               ### safe for some values of $b.
$ echo "$a"
A Tale of Two Cities

И здесь мы находим первую (из двух) и основную проблему с eval:
недостаточное цитирование:

$ b='book;date'

$ eval 'a=$'"$b"               ### safe for some values of $b.
Fri Apr 22 22:03:09 UTC 2016

Дата команды была выполнена (чего мы не собирались).

Но это не выполнит дату (по крайней мере, я так подумал, спасибо @Wildcard).
Правильным решением этой команды является очистка ввода, но я отложу обсуждение этого вопроса до тех пор, пока не будет определена вторая проблема eval.

$ eval 'a="$'"$b"\"
$ echo "$a"
A Tale of Two Cities;date

Уловка несложная, просто замените eval на echo и оцените, безопасна ли напечатанная командная строка. Сравните небезопасную и безопасную команду:

$ echo 'a=$'"$b"
a=$book;date
$ echo 'a="$'"$b"\"
a="$book;date"

Эти простые двойные кавычки сохранят строку как строку и не будут преобразованы в команду для выполнения оболочкой.

Возможно, будет легче понять логику цитирования, если мы поместим «внешние кавычки» и «внутренние кавычки» (добавлены пробелы для разборчивости):

$ echo a\=\"\$    "$b"   \"

Внутренние кавычки - это те, которые находятся вокруг $ b которые всегда являются «хорошей идеей».
Все внешние обозначены обратной косой чертой (в этом примере).
Команда без пробелов (как и следовало бы использовать):

$ echo a\=\"\$"$b"\"
a="$book;date"

$ eval a\=\"\$"$b"\"
$ echo "$a"
A Tale of Two Cities;date

Но даже этот пример был очень быстро сломан пользователем (спасибо @Wildcard):

$ b='";date;:"'
$ eval a\=\"\$"$b"\"
Fri Apr 22 23:25:43 UTC 2016

Выполнение команды eval было прервано стать злым, давайте использовать echo:

$ echo a\=\"\$"$b"\"
a="$";date;:""

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

Вторая проблема.

И это даже более запутанно, чем первое. В некоторых случаях мы не хотим, чтобы результат первого раскрытия параметров eval оставался в кавычках. Во многих случаях потому, что мы хотим выполнить команду внутри переменной.

Или злоумышленник (как уже было показано выше) создает строку, которая соответствует квотам, а затем помещает команду вне кавычек. Команда будет выполнена, и (если нам повезет) будет сообщено об ошибке.

Это нарушает первый уровень защиты: цитирование
И оставляет значения переменных полностью открытыми для выполнения.

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

Если значение переменной контролируется каким-то пользователем, мы сообщаем этому пользователю:

Дайте мне любую команду, я выполню ее с моими разрешениями.

Это всегда опасная ставка, верный баг, а корень - безумный поступок.

Очистка внешних данных.

Сказав все вышесказанное, это совершенно безопасно:

#!/bin/bash

a=${1//[^0-9]}       ### Only leave a number (one or many digits).

eval echo $(( a + 1 ))

Независимо от того, что внешний пользователь поместил в позиционный параметр, он станет числом, это может быть большое число, для которого $ ((...)) завершится ошибкой, но этот ввод не вызовет выполнение какой-либо команды.

Теперь мы можем поговорить об очистке переменной b из приведенной выше команды. Команда была:

$ b='";date;:"'
$ eval a\=\"\$"$b"\"
Sat Apr 23 01:56:30 UTC 2016

Потому что b содержало несколько символов, которые использовались для изменения строки.

$ echo a\=\"\$"$b"\"
a="$";date;:""

Переход на одинарные кавычки не сработает, им может соответствовать другое значение b. Нам нужно «очистить» b , удалив (как минимум) кавычки.
Мы могли бы заменить $ b на $ {b // \ "} :

$ eval a\=\"\$"${b//\"}"\"
$ echo "$a"
$;date;:

Более надежная очистка имени переменной.

Но мы можно сделать его еще лучше, признав, что имя переменной в оболочке может иметь только 0-9a-zA_Z и символы подчеркивания. Мы можем удалить все остальные с помощью этого:

$ c="$( LC_COLLATE=C; echo "${b//[^0-9A-Za-z_]}" )"

Это очистит переменную b на допустимое имя переменной.
А затем мы можем добавить еще более строгую проверку, фактически проверив, что имя переменной внутри $ b (используя очищенный ] c ) существует:

$ if declare -p "$c" &>/dev/null; then   eval a\=\"\$"$c"\" ; fi
10
27.01.2020, 20:33

Вопрос будет привлекать мнения ...

eval является частью стандартной оболочки:

Утилита eval должна создать команду, объединяя аргументы вместе, разделяя каждый с помощью символ <пробел> . Созданная команда должна быть прочитана и выполнена оболочкой.

Вероятные причины недовольства eval :

  • сценаристы могут неправильно цитировать, вызывая непредусмотренное поведение (подумайте о возможностях с несопоставленными кавычками, исходящими из переменной в оценке).
  • результат оценки может быть неочевидным (частично из-за проблем с цитированием, но частично из-за того, что eval позволяет вам устанавливать переменные на основе имен других переменных).

С другой стороны, если данные, входящие в eval , проверяются, например, результаты проверки имен файлов и проверки отсутствия кавычек, которые усложняют ситуацию, то это полезный инструмент. Обычно предлагаемые альтернативы менее переносимы, например, специфичны для некоторой конкретной реализации оболочки.

Дальнейшее чтение (но имейте в виду, что bash предлагает специфичные для реализации / нестандартные альтернативы):

11
27.01.2020, 20:33

Eval - простое решение для плохого кода. Вот пример, который приходит на ум:

Допустим, вы хотите получить элемент массива по ссылке. Вы можете использовать eval:

$ nov=(osc pap que)
$ rom=nov
$ eval echo \${$rom[2]}
que

Лучшим способом будет:

$ nov=(osc pap que)
$ rom=nov[2]
$ echo ${!rom}
que

Еще лучше:

$ tail -1 <<+
> osc
> pap
> que
> +
que
-1
27.01.2020, 20:33

Теги

Похожие вопросы