Я написал это как повторение превосходного ответа Криса Дауна в стиле учебника.
В 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 vulnerableif 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"