Объяснение команды для проверки снарядов

32

Вот команда, которую я использовал, чтобы проверить мою оболочку bash на наличие ошибки Shellshock:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

Может кто-нибудь объяснить, пожалуйста, команду в деталях?

heemayl
источник
4
См. Также: unix.stackexchange.com/q/157329/70524 - может помочь ответ Фикси.
Муру

Ответы:

45

Этот ответ является производным от оригинальной статьи о журнале Fedora Мэтью Миллера, лицензированной по лицензии Creative Commons Attribution-Share Alike 4.0 .

Позволь мне объяснить:

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

Это выведет «OOPS» на уязвимую систему, но выйдет молча, если bash был исправлен.

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

Это напечатает «OOPS» в уязвимой системе, но напечатает, “this is a test”если bash был исправлен.

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

$ Ubuntu()  { echo "Ubuntu is awesome."; }

и тогда у вас есть новая команда. Имейте в виду, что echoздесь на самом деле еще не работает; он просто сохраняется как то, что произойдет, когда мы запустим нашу новую команду. Это будет важно через минуту!

$ Ubuntu
 Ubuntu is awesome.

Полезно! Но, скажем, по какой-то причине нам нужно выполнить новый экземпляр bash в качестве подпроцесса, и мы хотим запустить мою потрясающую новую команду под этим. Оператор bash -c somecommandделает именно это: запускает данную команду в новой оболочке:

$ bash -c Ubuntu
  bash: Ubuntu: command not found

Ooh. Печальный. Ребенок не унаследовал определение функции. Но это присуще среде - совокупности пар ключ-значение, которые были экспортированы из оболочки. (Это совершенно другая концепция; если вы не знакомы с этим, поверьте мне.) И, оказывается, bash также может экспортировать функции. Так:

$ export -f Ubuntu
$ bash -c Ubuntu
  Ubuntu is awesome.

Что все хорошо, за исключением того, что механизм, с помощью которого это достигается, является своего рода хитрым . По сути, поскольку нет волшебства Linux / Unix для выполнения функций в переменных среды, функция экспорта фактически просто создает обычную переменную среды, содержащую определение функции. Затем, когда вторая оболочка читает «входящую» среду и встречает переменную с содержимым, похожим на функцию, она оценивает ее.

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

Этого никогда не произойдет, если функция, хранящаяся в переменной окружения, будет создана законным путем export -f. Но зачем быть законным? Злоумышленник может просто создать любую старую переменную среды, и если она будет выглядеть как функция, новые оболочки bash подумают, что это так!

Итак, в нашем первом примере:

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

Команда envзапускает команду с заданным набором переменных. В этом случае мы устанавливаем xчто-то похожее на функцию. Функция - это всего лишь одна :, на самом деле это простая команда, которая определена как ничего не делающая. Но затем, после semi-colonкоторого указывается конец определения функции, есть echoкоманда. Это не должно быть там, но ничто не мешает нам сделать это.

Затем команда для запуска в этой новой среде представляет собой новую оболочку bash, опять же с командой « echo this is a test» или «ничего не делать :», после которой она завершается совершенно безвредно.

Но - ой! Когда эта новая оболочка запускается и читает среду, она попадает в xпеременную и, поскольку она выглядит как функция, она оценивает ее. Определение функции безвредно загружается, а затем запускается и наша вредоносная полезная нагрузка. Таким образом, если вы запустите вышеперечисленное на уязвимой системе, вы получите “OOPS”печать обратно на себя. Или злоумышленник может сделать намного хуже, чем просто напечатать вещи.

αғsнιη
источник
1
Muchas gracias за отличное объяснение того, почему это работает.
Даг Р.
2
Обратите внимание, что envэто не обязательно. Вы можете получить тот же результат (прошел / не прошел в зависимости от того, был ли обновлен Bash) с помощью команды без него: x='() { :;}; echo OOPS' bash -c "echo this is a test". Это связано с тем, что предшествующая команде с присвоением переменной передает эту переменную и ее значение в командную ( bash -c "..."в данном случае) среду.
Приостановлено до дальнейшего уведомления.
1
... но это может быть необходимо в некоторых из последних патчей. Вещи в движении.
Приостановлено до дальнейшего уведомления.
4
@DennisWilliamson Необходимость или нет env, определяется оболочкой, из которой запускается тест, а не тестируемой оболочкой. (Они могут быть одинаковыми. Даже тогда мы тестируем, как bash обрабатывает свою собственную среду.) Оболочки в стиле Борна принимают NAME=value commandсинтаксис; Оболочки в стиле C (например csh, tcsh) не делают. Таким образом, тест является немного более переносимым env(за счет порождения путаницы в том, как он работает).
Элия ​​Каган,
2

В не исправленной версииbash он сохраняет экспортированные определения функций в качестве переменных среды.

Сохранить функцию xкак,

$ x() { bar; }
$ export -f x

И проверьте его определение как,

$ env | grep -A1 x
x=() {  bar
}

Таким образом, можно использовать это, определяя свои собственные переменные среды и интерпретируя их как определения функций. Например env x='() { :;}', будет рассматриваться как

x() { :;
}

Что делает команда для проверки снарядов,

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

От man env,

  1. env - запустить программу в измененной среде.

  2. :ничего не делать, кроме выходов со статусом выхода 0. увидеть больше

  3. Когда новый экземпляр неотправленного bash запускается как bash -c "echo this is a test", созданная переменная окружения обрабатывается как функция и загружается. Соответственно получается выход

    уязвимый
    это проверка

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

souravc
источник
Я все еще обнаружил, что любая определенная функция bash, если она экспортируется, оценивается в дочерней оболочке в исправленной версии bash. Посмотрите это: chayan @ chayan: ~ / testr $ test () {echo "что-нибудь"; }; экспорт -f тест; bash -c test Ouput: что-нибудь. Итак, ваш ответ несколько не правильно направлен. Я думаю, что kasiyA объяснил ошибку как расширение переменной за пределами ее определения.
Heemayl
@heemayl это поведение естественно. Но если вы попробуете, env test='() { echo "anything"; }' bash -c "echo otherthing"вы увидите на выходе otherthing. Это исправлено в патче. не стесняйтесь, если мне все еще не ясно.
souravc
Пожалуйста, дайте мне еще раз понять. В вашем последнем комментарии мы в основном определяем функцию, а затем сообщаем bash выполнить echo. В этом примере мы не вызывали функцию в bash. Разве это не будет иметь одинаковый результат как в исправленном, так и в не исправленном bash? У меня есть представление, что ошибка в основном заключалась в том, что bash выполнял команды, помещенные после определения функции, тогда как функция никогда не вызывалась нигде позже, например, если мы сделаем это env test = '() {echo "any"; }; echo "foo" 'bash -c "echo otherthing". Пожалуйста, уточните меня в этом контексте.
Heemayl
@heemayl Я отредактировал свой ответ, надеюсь, теперь он понятен. Вы правы в примере, в моем последнем комментарии мы не вызывали функцию. Но разница в том, что в unpatched bashфункции можно вызывать функцию, как она определена, а в исправлении bashсамо определение отсутствует.
souravc
@heemayl: Нет, это неправильно. Запатентованный Bash все равно передаст определение функции в дочернюю среду. Отличие, которое вносит исправление, состоит в том, что код, который следует за определением функции ( echo vulnerable), не выполняется. Обратите внимание, что в последних исправлениях переданная функция должна иметь определенный префикс ( env 'BASH_FUNC_x()'='() { :;}; echo vulnerable' bash -c "echo this is a test"). Некоторые более поздние патчи могут использовать %%вместо первого ().
Приостановлено до дальнейшего уведомления.