Влияние на безопасность использования неанизированных данных в арифметической оценке Shell

17

В комментарии к недавнему вопросу Стефан Шазелас упоминает, что для арифметики с двойными круглыми скобками существуют последствия для безопасности, такие как:

x=$((1-$x))

на большинстве снарядов.

Мои навыки Google кажутся ржавыми, и я ничего не могу найти. Каковы последствия для безопасности арифметики двойных скобок?

garethTheRed
источник

Ответы:

22

Проблема в случаях, когда содержимое $xне было подвергнуто санитарной обработке и содержит данные, которые потенциально могут находиться под контролем злоумышленника в случаях, когда код оболочки может в конечном итоге использоваться в контексте повышения привилегий (например, сценарий, вызываемый setuid). приложение, сценарий sudoers или используется для прямой или косвенной обработки данных вне сети (CGI, DHCP hook ...).

Если:

x='(PATH=2)'

Потом:

x=$((1-$x)))

имеет побочный эффект установки , PATHчтобы 2(относительный путь , который вполне может находиться под контролем злоумышленника). Вы можете заменить PATHна LD_LIBRARY_PATHили IFS... То же самое происходит x=$((1-x))в bash, zsh или ksh (не в dash и не в yash, которые принимают только числовые константы в переменных).

Обратите внимание, что:

x=$((1-$x))

не будет работать должным образом для отрицательных значений $xв некоторых оболочках, которые реализуют (необязательный согласно POSIX) --(декремент) оператор (как x=-1, например, это означает, что оболочка запрашивает 1--1арифметическое выражение). "$((1-x))"не имеет проблемы, поскольку xраскрывается как часть (не до) арифметической оценки.

В bash, zshи ksh(не dashили yash), если xесть:

x='a[0$(uname>&2)]'

Тогда разложение $((1-$x))или $((1-x))вызывает то , что unameкоманда должна быть выполнена (для zsh, aдолжно быть переменным массив, но можно использовать psvar, например , для этого).

Таким образом, не следует использовать неиницализированные или не продезинфицировать внешние данные в арифметических выражениях в оболочках (обратите внимание , что арифметическая оценка может быть сделана путем $((...))(он же $[...]в bashили zsh) , но и в зависимости от оболочки в let, [/ test, declare/typeset/export..., return, break, continue, exit, printf, printвстроенные функции, индексы массивов ((..))и [[...]]конструкции, чтобы назвать несколько).

Чтобы проверить, что переменная содержит буквенное десятичное целое число, вы можете использовать POSIXly:

case $var in
  ("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac

Помните, что [0-9]в некоторых локалях совпадения больше, чем 0123456789. [[:digit:]]должно быть в порядке, но я бы не стал на это ставить.

Также помните, что числа с ведущими нулями в некоторых контекстах рассматриваются как восьмеричные ( 010иногда 10, иногда 8) и имейте в виду, что приведенная выше проверка пропустит числа, которые потенциально больше максимального целого числа, поддерживаемого вашей системой (или любым другим приложением, которое вы будете использовать). используйте это целое число в; bash, например, рассматривает 18446744073709551616 как 0, поскольку это 2 64 ). Таким образом, вы можете добавить дополнительные проверки в приведенном выше утверждении case, например:

(0?* | -0?*)
  echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
  echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;

Примеры:

$ export 'x=psvar[0$(uname>&2)]'
$ ksh93 -c 'echo "$((x))"'
Linux
ksh93: psvar: parameter not set
$ ksh93 -c '[ x -lt 2 ]'
Linux
ksh93: [: psvar: parameter not set
$ bash -c 'echo "$((x))"'
Linux
0
$ bash -c '[[ $x -lt 2 ]]'
Linux
$ bash -c 'typeset -i a; export a="$x"'
Linux
$ bash -c 'typeset -a a=([x]=1)'
Linux
$ bash -c '[ -v "$x" ]'
Linux
$ mksh -c '[[ $x -lt 2 ]]'
Linux
$ zsh -c 'echo "$((x))"'
Linux
0
$ zsh -c 'printf %d $x'
Linux
0
$ zsh -c 'integer x'
Linux
$ zsh -c 'exit $x'
Linux

Больше читать на:

Стефан Шазелас
источник
x='P=3'; : $(($x + 5))будет установлен Pна 8, но x='P=3'; : $((x + 5))будет установлен Pна 3zsh, kshили bash). «То же самое происходит с $((x + 1))...» теперь не правильно; он будет установлен PATHв 2, как встарь.
Мосви
@mosvy, верно, спасибо (и за ваше раннее редактирование). Исправлено сейчас.
Стефан