Я написал это как повторение превосходного ответа Криса Дауна в стиле учебника.
В bash вы можете использовать такие переменные оболочки
$ t="hi there"
$ echo $t
hi there
$
По умолчанию эти переменные не наследуются дочерними процессами.
$ bash
$ echo $t
$ exit
Но если вы пометите их для экспорта, bash установит флаг, который означает, что они войдут в среду подпроцессов (хотя envp
параметр не очень заметен, main
в вашей программе на С есть три параметра: main(int argc, char *argv[], char *envp[])
где последний массив указателей является массивом переменных оболочки с их определениями).
Итак, давайте экспортировать t
следующим образом:
$ echo $t
hi there
$ export t
$ bash
$ echo $t
hi there
$ exit
Принимая во внимание, что выше t
было не определено в подоболочке, теперь оно появляется после того, как мы его экспортировали (используйте, export -n t
если вы хотите прекратить его экспорт).
Но функции в bash - это другое животное. Вы объявляете их так:
$ fn() { echo "test"; }
И теперь вы можете просто вызвать функцию, вызвав ее, как если бы это была другая команда оболочки:
$ fn
test
$
Еще раз, если вы создаете подоболочку, наша функция не экспортируется:
$ bash
$ fn
fn: command not found
$ exit
Мы можем экспортировать функцию с export -f
:
$ export -f fn
$ bash
$ fn
test
$ exit
Вот сложная часть: экспортированная функция наподобие fn
преобразуется в переменную окружения так же, как был описан наш экспорт переменной оболочки t
. Этого не происходит, когда fn
была локальная переменная, но после экспорта мы можем видеть ее как переменную оболочки. Однако вы также можете иметь обычную (то есть, не функциональную) переменную оболочки с тем же именем. Bash различает на основе содержимого переменной:
$ echo $fn
$ # See, nothing was there
$ export fn=regular
$ echo $fn
regular
$
Теперь мы можем использовать env
для отображения всех переменных оболочки, помеченных для экспорта, и отображаются как обычные, так fn
и функции fn
:
$ env
.
.
.
fn=regular
fn=() { echo "test"
}
$
Под-оболочка будет принимать оба определения: одно как обычная переменная, а другое как функция:
$ bash
$ echo $fn
regular
$ fn
test
$ exit
Вы можете определить, fn
как мы делали выше, или напрямую как обычное присвоение переменной:
$ fn='() { echo "direct" ; }'
Обратите внимание, что это необычная вещь! Обычно мы определяем функцию, fn
как мы делали выше, с помощью fn() {...}
синтаксиса. Но поскольку bash экспортирует его через среду, мы можем «сократить путь» прямо к обычному определению, приведенному выше. Обратите внимание, что (вопреки вашей интуиции, возможно) это не приводит к появлению новой функции, fn
доступной в текущей оболочке. Но если вы породите ** sub ** shell, то это произойдет.
Давайте отменим экспорт функции fn
и оставим новый обычный fn
(как показано выше) без изменений.
$ export -nf fn
Теперь функция fn
больше не экспортируется, но есть обычная переменная fn
, и она () { echo "direct" ; }
в ней содержится .
Теперь, когда подоболочка видит обычную переменную, которая начинается с ()
нее, остальная часть интерпретируется как определение функции. Но это только тогда, когда начинается новая оболочка. Как мы видели выше, простое определение обычной переменной оболочки, начинающейся с ()
, не приводит к тому, что она ведет себя как функция. Вы должны начать подоболочку.
А теперь ошибка "Shellshock":
Как мы только что увидели, когда новая оболочка принимает определение обычной переменной, начиная с ()
нее, она интерпретируется как функция. Однако, если после закрывающей скобки задано больше данных, определяющих функцию, она также выполняет все, что там есть.
Это требования, еще раз:
- Новый Bash появляется
- Переменная окружения принята
- Эта переменная окружения начинается с "()" и затем содержит тело функции внутри фигурных скобок, а затем имеет команды после
В этом случае уязвимый bash выполнит последние команды.
Пример:
$ export ex='() { echo "function ex" ; }; echo "this is bad"; '
$ bash
this is bad
$ ex
function ex
$
Обычная экспортируемая переменная ex
была передана в подоболочку, которая была интерпретирована как функция, ex
но завершающие команды были выполнены ( this is bad
) как порожденная подоболочка.
Объясняя гладкий однострочный тест
Популярный однострочный документ для тестирования уязвимости Shellshock - это тот, который упоминается в вопросе @ jippie:
env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Вот разбивка: во-первых, :
в Bash это просто сокращение для true
. true
и :
оба оценивают (как вы уже догадались) в bash:
$ if true; then echo yes; fi
yes
$ if :; then echo yes; fi
yes
$
Во-вторых, env
команда (также встроенная в bash) печатает переменные среды (как мы видели выше), но также может использоваться для запуска одной команды с экспортированной переменной (или переменными), заданной для этой команды, и bash -c
запускает одну команду из ее командная строка:
$ bash -c 'echo hi'
hi
$ bash -c 'echo $t'
$ env t=exported bash -c 'echo $t'
exported
$
Объединяя все эти вещи вместе, мы можем запустить bash как команду, дать ему какую-то глупую вещь (например bash -c echo this is a test
) и экспортировать переменную, которая начинается с ()
так, чтобы подоболочка интерпретировала ее как функцию. Если присутствует шеллшок, он также немедленно выполнит любые завершающие команды в подоболочке. Поскольку функция, которую мы передаем, не имеет отношения к нам (но должна анализировать!), Мы используем самую короткую допустимую возможную функцию:
$ f() { :;}
$ f
$
Функция f
здесь просто выполняет :
команду, которая возвращает true и завершает работу. Теперь добавьте к этому некоторую «злую» команду и экспортируйте обычную переменную в подоболочку, и вы выиграете. Вот еще одна строка:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
Так x
экспортируется как обычная переменная с простой допустимой функцией с echo vulnerable
прикрепленным до конца. Это передается в bash, и bash интерпретирует x
как функцию (о которой мы не заботимся), а затем, возможно, выполняет echo vulnerable
if shellshock.
Мы могли бы немного укоротить одну строку, удалив this is a test
сообщение:
$ env x='() { :;}; echo vulnerable' bash -c :
Это не беспокоит, this is a test
но :
снова запускает команду без вывода сообщений. (Если вы отключите его, -c :
то будете сидеть в подоболочке и выходить вручную.) Возможно, наиболее удобной версией будет эта:
$ env x='() { :;}; echo vulnerable' bash -c "echo If you see the word vulnerable above, you are vulnerable to shellshock"