Защита команды оболочки с помощью строковой переменной


9

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

cd var; echo > create_a_file_here

с переменной var , которая содержит строку (надеюсь) каталога, где я хочу создать файл "create_a_file_here". Теперь, если кто-то видит эту строку кода, можно использовать ее, назначив, например:

var = "; rm -rf /"

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

Кто-нибудь знает хороший способ убедиться, что «cd var» только меняет каталог и ничего больше?


4
В зависимости от того, как вы можете вызвать оболочку, вы также можете передать varв качестве аргумента. Например вызов shс аргументами -c, 'cd "$1"; echo > create_a_file_here', 'sh', varработ и не требует каких - либо изменений var. 'sh'Аргумент передается как $0.
ipsec

1
Какой язык программирования? Используете ли вы POSIX shили создаете свой собственный язык программирования с аналогичным синтаксисом, но который расширяется varвместо того, чтобы требовать от вас писать cd "$var"? Или это bashс shopt -s cdable_vars? О, я думаю, вы имеете в виду, что какая-то другая программа разветвляет оболочку для выполнения этих команд. Так что просто цитируйте var, но убедитесь, что он не содержит сам символ кавычки ...
Питер Кордес

@PeterCordes Если вы говорите о Bash, хорошо использовать переменную с двойными кавычками, которая содержит двойные кавычки. например, s='"'; echo "$s"печатные издания ".
wjandrea

@WJAndrea: да, но нет кавычки "козырной карты", которую нельзя победить при построении присваивания переменной из ненадежного ввода. О, решение: сделать var=untrusted stringв родительской программе, так varчто переменная окружения, которая уже установлена ​​при вызове sh. Тогда вам просто нужно цитировать его каждый раз, когда вы расширяете его, что можно сделать надежно. Ах, я вижу, что идея уже является частью ответа Стефана>. <
Питер Кордес

Ответы:


9

Если я правильно понимаю, varэто переменная в вашем языке программирования.

И на вашем языке программирования вы просите оболочку интерпретировать строку, которая является конкатенацией "cd ", содержимое этой переменной и "; echo > create_a_file_here".

Если да, то если содержимое varне контролируется жестко, это уязвимость внедрения команд.

Вы можете попробовать и правильно заключить в кавычки содержимое переменной¹ в синтаксисе оболочки, чтобы она гарантированно передавалась как один аргумент cdвстроенной функции.

Другой подход заключается в передаче содержимого этой переменной другим способом. Очевидным способом было бы передать это в переменную окружения. Например, в C:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

На этот раз код, который вы просите интерпретировать оболочкой, исправлен, нам все еще нужно правильно написать его в синтаксисе оболочки (здесь предполагается, что это POSIX-совместимая оболочка):

  • Расширение переменной оболочки должно быть заключено в кавычки, чтобы предотвратить split + glob
  • вам нужно -Pдля cdсделать простойchdir()
  • вам нужно --отметить конец опций, чтобы избежать проблем с varзапуском -(или +в некоторых оболочках)
  • Мы устанавливаем CDPATHпустую строку в случае, если она находится в среде
  • Мы запускаем echoкоманду только в случае cdуспеха.

Есть (по крайней мере) еще одна проблема: если она varесть -, она не попадает в вызываемый каталог, -а в предыдущий каталог (как хранится в $OLDPWD) и OLDPWD=- CDPATH= cd -P -- "$DIR"не гарантирует его обход. Так что вам нужно что-то вроде:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

¹ Обратите внимание , что только делает system(concat("cd \"", var, "\"; echo..."));это не путь, вы бы просто переместив проблему.

Например, var = "$(rm -rf /)"все равно будет проблемой.

Только надежный способ процитировать текст для Bourne-подобных оболочек заключается в одинарные кавычки , а также заботиться о одинарные кавычки , которые могут произойти в строке. Например, поверните char *var = "ab'cd"к char *escaped_var = "'ab'\\''cd'". То есть замените все 'на '\''и оберните все это внутри '...'.

Это по- прежнему предполагает , что эта строка в кавычках не используется в обратных кавычках, и вы по- прежнему нужно --, -P, &&, CDPATH=...


12

Простое решение: не вызывайте оболочку из вашей программы. Совсем.

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

Так, например, в Python вместо запуска os.system("somecmd " + somearg)используйте subprocess.run(["somecmd", somearg]). В C вместо system(), используйте fork()и exec()(или найдите библиотеку, которая делает это).

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

Разрешите только персонажам, чьи функции вы знаете, таким образом, существует меньший риск что-то упустить. Конечным результатом может быть то, что вы только решите разрешить [a-zA-Z0-9_], но этого может быть достаточно для выполнения работы. Возможно, вы также захотите убедиться, что ваш язык и набор инструментов не включают в себя буквы с акцентом, как äи öв этом. Они, вероятно, не считаются специальными в какой-либо оболочке, но, опять же, лучше быть уверенным, пройдут они или нет.


1
И убедитесь, что все, что вы используете для сопоставления [a-zA-Z0-9], не включает в себя такие вещи, как àкодировка которых также может быть неверно истолкована некоторыми оболочками (например bash) в некоторых локалях.
Стефан Шазелас

@ StéphaneChazelas (из интереса :) они (и только под теми затронутыми местами?)
Уилф

10

В языке программирования должны быть более эффективные способы, чем выполнение команд оболочки. Например, замена cd varэквивалентом языка программирования chdir (var);должна гарантировать, что любой обман со значением varonly приводит к ошибке «Directory not found», а не к непреднамеренным и, возможно, вредоносным действиям.

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

В C я мог бы сделать что-то вроде:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

Конечно, ваш язык программирования может сделать что-то подобное?


Спасибо за ваш ответ, но, к сожалению, я должен использовать обходной путь с командами bash. Я проверил процитирование переменной - как и предполагал Муру - и это похоже на работу!
ES___
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.