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


32

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

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

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


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”печать обратно на себя. Или злоумышленник может сделать намного хуже, чем просто напечатать вещи.


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, который выглядит так, как будто он соответствует ограничениям, которые он знает о том, как выглядит определение функции, и он оценивает строку, непреднамеренно выполняя также эхо (которое может быть любой командой, злонамеренной или нет) , Также смотрите это


Я все еще обнаружил, что любая определенная функция 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"). Некоторые более поздние патчи могут использовать %%вместо первого ().
Приостановлено до дальнейшего уведомления.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.