[Перевод] Курс MIT «Безопасность компьютерных систем». Лекция 9: «Безопасность Web-приложений», часть 1

Массачусетский Технологический институт. Курс лекций #6.858. «Безопасность компьютерных систем». Николай Зельдович, Джеймс Микенс. 2014 год


Computer Systems Security — это курс о разработке и внедрении защищенных компьютерных систем. Лекции охватывают модели угроз, атаки, которые ставят под угрозу безопасность, и методы обеспечения безопасности на основе последних научных работ. Темы включают в себя безопасность операционной системы (ОС), возможности, управление потоками информации, языковую безопасность, сетевые протоколы, аппаратную защиту и безопасность в веб-приложениях.

Лекция 1: «Вступление: модели угроз» Часть 1 / Часть 2 / Часть 3
Лекция 2: «Контроль хакерских атак» Часть 1 / Часть 2 / Часть 3
Лекция 3: «Переполнение буфера: эксплойты и защита» Часть 1 / Часть 2 / Часть 3
Лекция 4: «Разделение привилегий» Часть 1 / Часть 2 / Часть 3
Лекция 5: «Откуда берутся ошибки систем безопасности» Часть 1 / Часть 2
Лекция 6: «Возможности» Часть 1 / Часть 2 / Часть 3
Лекция 7: «Песочница Native Client» Часть 1 / Часть 2 / Часть 3
Лекция 8: «Модель сетевой безопасности» Часть 1 / Часть 2 / Часть 3
Лекция 9: «Безопасность Web-приложений» Часть 1 / Часть 2 / Часть 3

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

Основная идея заключается в том, что я сначала бы хотел показать вам пример ошибки Shellshock, о которой, вы, возможно, уже слышали. Это была довольно популярная тема в литературе по компьютерной безопасности.

kkh0hzlvc4krqxabzmnwigajxo4.jpeg

Люди дают ошибке Heartbleed максимальную оценку по шкале опасности — 10 из 10. Они считают, что это самая опасная ошибка, от которой должна защищать система безопасности. Я подумал, что будет отличной идеей показать вам живую историю этого вопроса, которую вы сможете пересказать своим родителям, чтобы они поняли, что обучение в Массачусетском технологическом институте стоит своих денег.

Итак, какова основная идея ошибки Shellshock? Это действительно отличный пример того, почему так трудно создать безопасные веб-приложения, которые охватывают несколько технологий, несколько языков, несколько ОС, так далее и так далее. Поэтому основная идея заключается в том, что Shellshock использует то, что злоумышленник может создать специальный http-запрос к серверу и управлять заголовками в этом запросе. На доске я записал очень простой пример.
Предположим, злоумышленник хочет отправить запрос GET некоторому интерфейсу CGI на тему поиска кошек, потому что это именно то, что люди всегда ищут в интернете (шутка). Поэтому здесь будет знак вопроса, и какой-то стандартный заголовок хоста с URL-адресом, например, example.com:

GET /querry.cgi? search = cats
Host: example.com
Custom — header: Custom — value

Теперь обратите внимание, что злоумышленник может также указать пользовательские заголовки. Например, я хочу найти какой-то специфичный для приложения заголовок под названием Custom-header указать там некоторое значение Custom — value, потому что веб-приложение может определить некоторые функциональные возможности, которые нельзя выразить с помощью простых предопределенных заголовков HTTP. Так что пока все это кажется довольно безобидным.
Но в конечном итоге происходит то, что многие из этих веб-серверов для обработки CGI скриптов будут фактически принимать эти пользовательские значения заголовка Custom — value и использовать их для установки переменных среды Bash. То есть они будут использовать этот заголовок Custom-header для создания пользовательского заголовка имени переменной Bash, они возьмут это значение Custom — value, которое предоставил злоумышленник, и используют его как значение переменной Bash. Как только эта переменная будет установлена, сервер CGI сделает некоторую обработку контекста этой среды.

И это плохо, потому что веб-серверы не должны принимать произвольные значения из этих случайных «грязных» вещей. Таким образом, в конкретном примере ошибки Shellshock происходит то, что если вы придадите переменной Bash некое злонамеренное значение, может начаться форменное безумие.

pte_26a2d3abmngljp3z9rwoiwa.jpeg

В принципе, это злонамеренное определение функции выбирается в языке сценариев Bash, и вас не должна беспокоить специфика этого процесса. Но дело в том, что если бы параметр Bash был задан правильно, эта часть /bin/id не была бы выполнена. Так что вы только что определили некую глупую функцию, которая ничего не делает и прекращает процесс выполнения запроса.
Однако эта последовательность символов путает парсер Bash, он как бы спотыкается об эту ерунду, расположенную после слеша. А потом он говорит: «о, я мог бы продолжить анализировать и выполнять здесь некоторые команды, не так ли?». И в данном случае он просто выполняет команду bin/id, которая отображает некоторую информацию о пользователе. Но суть уязвимости в том, что вместо bin/id здесь можно поместить абсолютно любой код!

Я приведу очень простой пример, который вы увидите на экране. Это очень простой сервер Python, самый простой, какой можно представить. Я использую здесь метод GET. Этот метод означает перебор всех HTTP заголовков в запросе.

w43cfwwaqjmmjxm78zug-oecus4.jpeg

1mmrjfhbdbu6-kpevbe24bsmhpg.jpeg

Здесь в заголовке у нас имеется значение для переменной K и значение для запроса V. В этом случае GET просто распечатывает заголовки, которые находит.

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

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

rkjtzg52m6abnpspbgoyltcpzmc.jpeg

Затем я могу написать свой особый клиент Shellshock — он расположен на следующей вкладке.

jvjhhchuelxbtmdnkrdos_apl_0.jpeg

На самом деле это довольно просто — я просто определяю одну из этих вредоносных строк attack.str, поэтому у неё вначале расположены такие «кривые» величины. А далее я просто знаю, что на стороне сервере всё теперь будет выполняться по моей воле.

В данном случае я использовал нечто безобидное — echo «Я владею твоей машиной». Но здесь может быть что угодно. Можно запустить ещё одну оболочку Bash с параметром «echo ATTACKER CMD», то есть реальную команду атакующего, что может быть очень опасно.

Итак, я устанавливаю заголовки и запрос пользователя, а затем просто использую Python для создания HTTP соединения и просто посылаю его серверу. Так что в итоге происходит? Я выполняю здесь свой клиент Shellshock.

liy5pfnb9rji1s51vi7vvwwn4bs.jpeg

Вы видите, что здесь появилась ошибка 404, потому что не имеет значения, какой файл я запросил, я просто вставляю сюда какой-то индекс, несуществующий HTML. Но если посмотреть сюда, на вторую вкладку, где у нас показан веб-сервер жертвы, согласившийся на соединение через порт 8282, то мы увидим, что он принял мои сообщения «Я владею твоей машиной» и ATTACKER CMD.

6tkfyptepbhu83ggs2shp_20dys.jpeg

Потому что как только сервер жертвы получил этот заголовок, он тут же установил значения переменной Bash, и вследствие этого запустилась команда ATTACKER CMD. Это понятно?

Аудитория: так это происходит, если программа запускается с таким заголовком?

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

Аудитория: таким образом, если вернуться к коду веб-сервера, то покажется, что существует намного худшая уязвимость, чем Shellshock. Потому что можно совершить системный вызов и выполнить команду, просто установив пользовательский заголовок на что-то другое, и мне не пришлось бы использовать ошибку Shellshock в данном примере.

Профессор: да, правильно, в этом конкретном веб-сервере, который я написал только для примера, есть вещь, которой ни за что нельзя доверять. Но если бы здесь у нас был не Python, а Apache, то можно было бы непосредственно установить значение среды для какой-либо конкретной службы с помощью параметра set nth. Но существуют такие серверы, как этот, которые создают отдельный процесс и делают что-то, очень похожее на приведённый пример.

jss4zz4vwyw6dqz-4s0kzwlr-uy.jpeg

Еще один пример, который я хотел вам привести, это пример межсайтового скриптинга. Ошибка Shellshock была своего рода примером, насколько важна дезинфекция контента. Мы обсудили то, что вы не должны просто принимать входные данные от случайных людей и использовать их непосредственно в командах любого типа.

Межсайтовый скриптинг является ещё одним примером, показывающим, почему что-то может пойти не так. В этом примере у меня есть еще один простенький CGI сервер, написанный на Python.

lpooaw-togbyuns6zaxta0lp6ei.jpeg

Это дескриптор, который выполняется при поступлении запроса от клиента. Я напечатал здесь некоторые заголовки для ответа, и мой ответ будет текстовым HTML. Как выясняется, браузеры имеют некоторые механизмы безопасности, чтобы попытаться предотвратить атаку, которую я собираюсь вам показать. Поэтому я сделал так, чтобы отключить некоторые из механизмов этой защиты, поместив эту строку заголовка в начале.

Затем скрипт CGI получает доступ ко всем полям и запросам CGI, начиная со строки form=cgi.FieldStorage (). Представьте себе, что всё, что расположено в строке после этого вопросительного знака, представляет собой заголовок и параметры нашего примера:

GET /querry.cgi? search = cats
Host: example.com
Custom — header: Custom — value

Далее скрипт cgi делает совсем простую вещь — он сразу печатает значение чего-то, что пришло от атакующего. Тут та же основная идея, и это плохая идея, потому что эта функция print печатает полученное значение непосредственно в сам HTML.

Здесь может произойти следующее. Предположим, у меня есть куча запросов, которые я хочу запустить. В этом первом запросе я просто устанавливаю значение сообщения равным Hello, то есть перехожу по адресу первой строчки.

q2xlngmnjiyfdvdytmh2gfdoymq.jpeg

Поэтому, если я перейду на свою страницу, то увижу на ней слово hello, потому что сервер принимает непосредственно то, что я ему передаю, и печатает «привет». Так что никаких сюрпризов.

1t0nakmsjq1emmcqj03bs9sosq0.jpeg

Я понимаю, что действительно могу передать туда произвольный HTML код, и если я установлю формат заголовка h1, то есть перешлю на сервер вторую строчку, оканчивающуюся на hello, оно изменится и на странице — видите, стиль слова изменился на стиль заголовка h1. Так что это работает, я впечатываю величины прямо в страницу.

gmqymv3fbxpjzkxa5wwdvijx7vu.jpeg

Отлично, теперь мы в деле, и это круто. Теперь давайте просто добавим код JavaScript, то есть запустим в браузере третью заготовленную мной строку, куда вставлен некий сценарий , который запускается после параметра alert («XSS»).

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

hdmoqr8xk5ehtmumjddzqwrquns.jpeg

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

Но ведь можно воспользоваться тем фактом, что HTML, CSS и JavaScript чрезвычайно сложные языки, и они написаны сложным для понимания способом. Я воспользуюсь этим и использую последнюю, четвёртую строку своей записи, разместив её в адресной строке браузера. Это атакующая строка, содержащая неправильный URL. Она включает в себя URL картинки и тег сценария »> и на самом деле не может подвергнуться разбору. Поэтому при получении такой строки браузер просто запутается и выдаст на экран информацию: «Страница по адресу 127.0.0.1:8282 говорит: XSS». Таким образом, встроенное обнаружение межсайтовых сценариев на самом деле не работает.

zhsadfxqyr1nqiwiy8atytitmou.jpeg

Если мы щелкнем кнопку «OK», у нас появится просто пустая страница. Но если посмотреть на её содержимое, мы увидим непонятные кавычки и скобки, которые взялись неизвестно откуда.

zaqiyp6lxm9damgqbidbi7ess3u.jpeg

Однако с точки зрения злоумышленника испорченная страница не имеет значения, потому что мы видели предупреждение, а это означает, что код был запущен. И он мог быть использован для того, чтобы украсть кукиз или сделать что-то подобное.

Аудитория: , а что такое межсайтовый аспект?

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

Итак, я показал вам две быстрые демонстрации «грязного» мира, в котором мы обитаем. Так почему же межсайтовый скриптинг настолько распространен? Почему эти проблемы так важны? Причина в том, что веб-сайты становятся все более и более динамичными, и они хотят размещать множественный пользовательский контент или включать контент из других доменов. Подумайте, например, о разделе комментариев к новостной статье, эти комментарии исходят от ненадежных людей — от пользователей. Так или иначе, эти сайты должны выяснить, каковы правила комбинации таких вещей.

Веб-сайты могут содержать пользовательские документы, например документы Google или Office 365. Все эти документы исходят от ненадежных людей, но каким — то образом они должны уживаться друг с другом и с большой инфраструктурой Google или Microsoft.

Какие же типы межсайтовых сценариев защиты мы можем использовать? Один тип такой защиты представляет собой фильтры межсайтового скриптинга в самом браузере. Эти фильтры будут пытаться обнаружить возможные атаки с использованием межсайтовых сценариев. И мы увидели один из таких фильтров в действии — это был третий пример сценария, который мы рассмотрели.
Предположим, у вас есть URL-адрес foo.com/? q=