Подтвердить что ты не робот

Является ли поведение уязвимости Shellshock в Bash документированным или вообще преднамеренным?

Недавняя уязвимость CVE-2014-6271 в том, как Bash интерпретирует переменные среды . Эксплоит полагается на Bash, анализируя некоторые объявления переменных среды как определения функций, но затем продолжая выполнение кода, следующего за определением:

$ x='() { echo i do nothing; }; echo vulnerable' bash -c ':'
vulnerable

Но я не понимаю. Я ничего не смог найти в руководстве Bash о интерпретации переменных среды как функций вообще (кроме наследования функций, которые различны). Действительно, правильное определение имени функции просто рассматривается как значение:

$ x='y() { :; }' bash -c 'echo $x'
y() { :; }

Но коррумпированный ничего не печатает:

$ x='() { :; }' bash -c 'echo $x'

$ # Nothing but newline

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

Update

В сообщении Barmar, я предположил, что имя функции было именем параметра:

$ n='() { echo wat; }' bash -c 'n'
wat

Кого я мог бы поклясться, что раньше я пытался, но я думаю, что я не очень старался. Теперь это повторяемо. Здесь немного больше тестов:

$ env n='() { echo wat; }; echo vuln' bash -c 'n'
vuln
wat
$ env n='() { echo wat; }; echo $1' bash -c 'n 2' 3 -- 4

wat

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

В любом случае, основной ответ на мой вопрос: да, вот как Bash реализует унаследованные функции.

4b9b3361

Ответ 1

Это похоже на ошибку реализации.

По-видимому, способ экспорта экспортируемых функций в bash заключается в том, что они используют специально отформатированные переменные среды. Если вы экспортируете функцию:

f() { ... }

он определяет переменную среды, например:

f='() { ... }'

Что, вероятно, происходит, когда новая оболочка видит переменную среды, значение которой начинается с (), она добавляет имя переменной и выполняет полученную строку. Ошибка заключается в том, что это включает в себя выполнение чего-либо после определения функции.

Обнаруженное исправление, по-видимому, должно анализировать результат, чтобы увидеть, действительно ли оно является корректным определением функции. Если нет, он выводит предупреждение о попытке определения недопустимой функции.

Эта статья подтверждает мое объяснение причины ошибки. В нем также подробно описывается, как исправление устраняет это: не только они более тщательно анализируют значения, но и переменные, которые используются для передачи экспортируемых функций, следуют специальному соглашению об именах. Это соглашение об именах отличается от того, которое используется для переменных среды, созданных для сценариев CGI, поэтому HTTP-клиент никогда не должен иметь возможность войти в эту дверь.

Ответ 2

Следующее:

x='() { echo I do nothing; }; echo vulnerable' bash -c 'typeset -f'

печатает

vulnerable
x () 
{ 
    echo I do nothing
}
declare -fx x

кажется, чем Bash, после анализа x=..., обнаружил его как функцию, экспортировал его, увидел declare -fx x и разрешил выполнение команды после объявления.

echo vulnerable

x='() { x; }; echo vulnerable' bash -c 'typeset -f'

печатает:

vulnerable
x () 
{ 
    echo I do nothing
}

и запустите x

x='() { x; }; echo Vulnerable' bash -c 'x'

печатает

Vulnerable
Segmentation fault: 11

segfaults - бесконечные рекурсивные вызовы

Он не переопределяет уже определенную функцию

$ x() { echo Something; }
$ declare -fx x
$ x='() { x; }; echo Vulnerable' bash -c 'typeset -f'

печатает:

x () 
{ 
    echo Something
}
declare -fx x

например. x остается прежней (правильно) определенной функцией.

Для Bash 4.3.25(1)-release уязвимость закрыта, поэтому

x='() { echo I do nothing; }; echo Vulnerable' bash -c ':'

печатает

bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'

но - что странно (по крайней мере для меня)

x='() { x; };' bash -c 'typeset -f'

STILL PRINTS

x () 
{ 
    x
}
declare -fx x

и

x='() { x; };' bash -c 'x'

так что STILL принимает странное определение функции...

Ответ 3

Мне кажется, стоит посмотреть на код Bash. Патч дает некоторое представление о проблеме. В частности,

*** ../bash-4.3-patched/variables.c 2014-05-15 08:26:50.000000000 -0400
--- variables.c 2014-09-14 14:23:35.000000000 -0400
***************
*** 359,369 ****
      strcpy (temp_string + char_index + 1, string);

!     if (posixly_correct == 0 || legal_identifier (name))
!       parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
!
!     /* Ancient backwards compatibility.  Old versions of bash exported
!        functions like name()=() {...} */
!     if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
!       name[char_index - 2] = '\0';

      if (temp_var = find_function (name))
--- 364,372 ----
      strcpy (temp_string + char_index + 1, string);

!     /* Don't import function names that are invalid identifiers from the
!        environment, though we still allow them to be defined as shell
!        variables. */
!     if (legal_identifier (name))
!       parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);

      if (temp_var = find_function (name))

Когда Bash экспортирует функцию, она отображается как переменная среды, например:

$ foo() { echo 'hello world'; }
$ export -f foo
$ cat /proc/self/environ | tr '\0' '\n' | grep -A1 foo
foo=() {  echo 'hello world'
}

Когда новый Bash процесс находит функцию, определенную таким образом в своей среде, она вычисляет код в переменной с помощью parse_and_execute(). Для обычного, не вредоносного кода его выполнение просто определяет функцию в Bash и перемещается. Однако, поскольку он передан в общую функцию выполнения, Bash будет правильно анализировать и выполнять дополнительный код, определенный в этой переменной после определения функции.

Вы можете видеть, что в новом коде добавлен флаг с именем SEVAL_ONECMD, который сообщает Bash оценивать только первую команду (то есть определение функции) и SEVAL_FUNCDEF, чтобы разрешать только определения functio0n.

Ответ 4

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

  • Есть, возможно, 4 возможных варианта
  • Необязательный дефис как синоним для -i (для обратной совместимости я предполагаю)
  • Нулевая или более NAME = пары VALUE. Это переменные назначения, которые могут включать определения функций.
  • Обратите внимание, что не требуется точка с запятой (;) между или после назначений.
  • Последний аргумент может быть одной командой, за которой следуют ее аргументы. Он будет работать с любыми разрешениями, предоставленными используемому логину. Безопасность контролируется путем ограничения разрешений для пользователя входа и установки разрешений на пользовательские исполняемые файлы, так что пользователи, отличные от исполняемого владельца, могут читать и выполнять программу, а не изменять ее.
[ [email protected]:~ ] env --help
Usage: env [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]
Set each NAME to VALUE in the environment and run COMMAND.

  -i, --ignore-environment   start with an empty environment
  -u, --unset=NAME           remove variable from the environment
      --help     display this help and exit
      --version  output version information and exit

A mere - implies -i.  If no COMMAND, print the resulting environment.

Report env bugs to [email protected]
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
Report env translation bugs to <http://translationproject.org/team/>