Веб-безопасность 200
В информационной безопасности есть казалось бы очевидные вещи. Но они все еще часто встречаются, в том числе в крупных компаниях и государственных организациях. На эту тему эксперт по информационной безопасности и преподаватель Иван Юшкевич провел мастер-класс по безопасности на конференции РИТ++ на платформе hacktory.ai.
Практическую часть МК о том, как накрутить лайки в социальных сетях, украсть криптовалюту и получить доступ к самым большим секретам пользователей, вы можете посмотреть здесь и здесь.
Теоретическую часть пришлось разбить на 3 части. Сегодня рассказ будет о том, что злоумышленник может сделать с найденными файлами и директориями. А также, как используется удаленное выполнение кода (инъекции), в том числе и в SQL.
Поиск файлов и директорий
Одна из самых распространенных проблем, связанная с информационной безопасностью — файлы хранятся в общедоступном виде. Это касается файлов с чувствительной информацией или тех, про которые забыли, например:
Файлы административной панели (/admin.php);
Бэкапы и логи (/log.txt; /back.sql; /backup.tar.gz);
Структура веб проекта;
Файлы систем контроля версий (/.git);
Отладочные файлы (test.php);
Скрытые копии редактируемых файлов (/admin.php-).
Это в принципе может быть всё что угодно. Но это то, что не должно находиться в открытом доступе и может стать угрозой, если файл кто-то обнаружит.
Угрозы могут быть самыми разными. Начиная от того, что человек может получить доступ к интересной информации о сервере, например, php-info. И заканчивая серьезными вещами. Например, если он сможет найти систему контроля версий, то при некоторых особенностях он сможет получить даже доступ к исходному коду самого веб-приложения.
Важно всегда держать в голове:
1. Не полагайтесь на то, что если вы переименовали какой-то ресурс, то подобрать название крайне сложно. Злоумышленник может найти любой ресурс, каким бы замысловатым его название ни было.
2. Никогда не храните бэкапы и файлы конфигураций в общей директории веб-ресурсов. Это очевидно.
3. Иногда дополнительное расширение или новые панели могут создавать собственные файлы и ресурсы. Их тоже необходимо закрывать от публичного доступа.
Ищем файлы и директории
Предположим, есть некоторый ресурс. Мы на него зашли и обнаружили, что есть файл index.html, папки со скриптами и стилями (/css/, /js/) и несколько api-ручек (/api/v1/user/: login, register, info/:id/).
В принципе, ничего интересного. Однако если применить один из инструментов (его мы рассмотрим чуть позже) для поиска файлов и скрытых директорий, можно обнаружить еще 5 интересных адресов.
Рассмотрим каждую из них и подумаем, какую угрозу они в принципе могут нести. Начнем снизу.
Test.php
Test.php, да и любые отладочные файлы могут представлять абсолютно разную функциональность, начиная от простой информации или вывода на страничку «Привет, мир», и заканчивая теми, что помогут злоумышленнику скомпрометировать сервер.
Исследуя веб-ресурс, можно найти поддомены, например, тестовые, и на них поискать скрипты, которые использовались для отладки, тестирования или чего-то еще. С их помощью можно получить доступ к серверу. Довольно много ресурсов были таким образом скомпрометированы.
Например, один из исследователей зарепортил уязвимость, обнаружив на сайте скрипт check.php, и получив с его помощью доступ к информации о сервере. Скорее всего, это было самое тривиальное PHP-info, но все же такие вещи встречаются в реальной жизни.
Рассмотрим следующую папку.
Директория /.git/
/.git/ cодержит изменения и артефакты системы контроля версий. Если её обнаружат, а ее не почистили, не удалили или не закрыли к ней доступ, то можно получить доступ даже к исходным кодам всего проекта. Нужно будет просто восстановить те или иные файлы по их артефактам. Звучит интересно, потому что так возможен доступ к исходному коду файла test.php.
А проанализировав test.php, мы могли бы узнать, например, что он создавал бэкапы с определенным названием в папке /backup/.
Директория /backup/
Ее название говорит само за себя. Там может оказаться что угодно, начиная от исходных данных самого приложения и заканчивая бэкапами БД. И она может нанести непоправимый ущерб, если находится в открытом доступе. В этом случае можно получить доступы к учетным записям, исходным кодам, БД — к чему угодно.
Может показаться, что эта уязвимость прямо из 90-х. Однако нет, она встречается повсеместно. Например, на одном из сайтов — к слову, на сайте Министерства безопасности США — исследователь нашел такую папку и получил доступ к бэкапам.
Директория /access/
Можно найти папку /access/ с говорящим файлом access.log, в котором, собирается какая-то информация (логи). Эти логи иногда могут содержать тоже очень интересную информацию, критичную для веб-ресурса.
Проанализировав access.log, можно найти некоторую папку, явно указывающую, что она принадлежит к административной панели. И если после анализа директории /backup/ у нас доступ к backup БД, то мы знаем, какие пользователи и их пароли нужны, чтобы получить доступ к этой административной панели. То есть из одной уязвимости можно получить доступ к административной панели, даже если у нее странное название, которое нелегко подобрать.
Один исследователь исследовал ресурс мессенджера Slack и нашел файл, или даже ручку под названием debug (тоже вполне говорящее название). С его помощью он получил информацию о том, какие пользователи сейчас находятся в системе, их API адреса, идентификаторы сессий и многое другое Вознаграждение составило $1500.
Директория /api/v1/user/
Не только файлы или папки могут являться угрозой. Иногда проблема может крыться в API-ручках. Поскольку считается правильным делать ручки (названия функций) говорящими, чтобы можно было легко понять, к чему они относятся, то злоумышленник может попробовать поперебирать те или иные составляющие части адреса ручек.
Например, заменить user на admin, чтобы получить доступ к другой ручке, изначально скрытой, и которая может использоваться в той самой административной панели. Если она не требует никакой авторизации и на ней не проверяется контроль доступа, то, совершить с её помощью какие-то противоправные действия.
То есть, хотя изначально сайт состоял всего из 7 адресов, за каждым из них может быть огромное количество скрытой информации, которая может позволить скомпрометировать сервер. Не надейтесь, что переименовав скрипт во время тестирования сайта или переноса данных, вы закроете доступ к данным. Всё это можно найти.
Инструменты
Инструментов для поиска файлов и директорий огромное количество. В другой статье на Хабре подробно рассказано об этом.
В интернете также можно найти подборки словарей с интересными файлами, которые могут быть не удалены с сервера или, скорее всего, существуют на многих серверах.
Как избежать нахождения информации
Как обезопасить наше веб-приложение от того, чтобы кто-то не нашел чувствительную информацию и не использовал ее во вред?
Не хранить чувствительные данные в открытом доступе – самое очевидное решение.
А если мы что-то и храним, например, по каким-то ручкам или в каких-то файлах, то всегда проверять, что эта информация в сети интернет не находится в публичном доступе. Что файлы и ручки прикрыты, может быть, паролями или настройки на веб-сервере не позволяют получить к ним доступ по прямым ссылкам.
Если ставим дополнительные плагины, директории и т.п., то нужно проверять, что они не создают какую-то информацию или сама настройка веб-сервера не раскрывает эту информацию. Например, если был ресурс, настроенный на работу с веб-сервером Apache, а потом эту настройку перенесли на Nginx, то иногда некоторые файлы и директории могут стать станут доступными для сторонних пользователей.
В любом случае, если мы пытаемся что-то скрыть, это всегда, скорее всего, будет найдено. Поэтому при разработке стоит учитывать, что если куда-то у пользователя доступа быть не должно, он туда и ходить не должен.
Перейдем к следующей уязвимости.
Удаленное выполнение кода (инъекции)
Внедрение команд — одна из самых распространенных и уязвленных уязвимостей. Она относится в OWASP к классу А1 под названием инъекция, и на самом деле, неважно, чего — кода или команд. Инъекция может быть абсолютно везде.
Сначала рассмотрим самое простое и тривиальное внедрение команд.
Внедрение команд
Суть уязвимости крайне проста: пользователь передает некоторые данные и они попадают в какую-то строку, которая потом передается на выполнение в оболочку ОС. То есть атака возможна, когда приложение формирует команду к ОС и подставляет в неё небезопасные данные, контролируемые пользователем — GET/POST параметры, HTTP-заголовки, Cookies, и т. д. В результате все данные из не доверенных источников попадают куда бы то ни было без валидации этих данных.
В большинстве случаев это возможно из-за недостаточной валидации входных данных. Если мы куда-то передаем пользовательские данные, никак их не обрабатываем и не проверяем, то, скорее всего, в огромном количестве случаев это приведет к проблемам.
Предотвратить внедрение команд возможно, если принимать меры безопасности на стадиях проектирования и разработки приложения.
Ищем возможность внедрения команд
Уязвимость можно обнаружить, проверяя код. Проверьте, вызываются ли какие-либо методы исполнения команд и принимается ли в качестве данных для этих команд не прошедший валидацию пользовательский ввод.
PING
Рассмотрим хрестоматийный пример. Допустим, у нас есть сайт vuln.com и скрипт /ping.php:
Этот скрипт вызывает утилиту ping, которая отправляет запросы на ip-адрес, а он попадает в скрипт в качество аргумента. Затем скрипт возвращает вывод команды на экран. То есть это некоторый сервис, который что-то пингует. В нормальном случае мы подставляем туда ip-адрес 8.8.8.8:
vuln.com/ping.php?ip=8.8.8.8 fc
В результате выполняется команда: ‘ping - с 4’ 8.8.8.8. Как вы можете заметить, в этом коде данные подставляются, как есть. Поэтому нам ничто не мешает вместо 8.8.8.8 (ведь никто не проверяет, что это ip-адрес и что он содержит некорректные управляющие символы) передать при отправке в качестве аргумента значения 123;whoami. “;” будет являться управляющим символом, который свидетельствует о разделении команд.
Тогда сформированная команда будет выглядеть следующим образом:
‘ping - с 4’ 123;whoami
Команда попробует пропинговать адрес 123, и естественно, сделать это не сможет. А так как “;” говорит, что предыдущая команда закончилась и началась следующая, то скрипт выполнит “whoami”. Собственно, в этом и заключается суть уязвимости. Она крайне простая, но в результате данные пользователей попадают в необработанном виде куда-то, где и исполняются.
Может показаться, что это тоже архаизм, и сейчас его найти невозможно. Однако нет. В качестве примера есть такой report.
Рассмотрим его подробнее.
Nextcloud
Существует ПО Nextcloud, которое позволяет создавать свое облако и шарить файлы — такое standalone решение. В 2018 году исследователь нашел в нем уязвимость как раз через внедрения команды.
Посмотрите на 11 строку. Выполняются функция “exec”, команда “unrar” и передаются значения переменных “file” и “dir”. Последние получаются, как показывает этот код, из архива “rar”, который, скорее всего, может как-то передавать пользователей. То есть если исследователь сможет сформировать такой архив “rar”, в котором будет содержаться имя “file с ;”, то при распаковке он сможет выполнить свою стороннюю команду и получить доступ к серверу.
Да, наверное, он не сможет это сделать штатными способами, но есть ПО, которое это сделает за него. А если там содержатся приватные данные или сервер используется как корпоративный dropbox?
Но не только PHP уязвим к данному классу атак. Например, если вы используете какие-то библиотеки, уязвимость может открыться в них.
Image Magick
Набор утилит (ПО) Image Magick отвечает за конвертацию изображения и их обработку. Огромное количество компаний использовала его для обработки изображений.
И также много исследователей нашли уязвимости внутри Image Magick, которые позволяла выполнять произвольные команды.
Найденные уязвимости
H1514 Remote Code Execution on kitcrm using bulk customer update of Priority Products ($15000)
RCE in profile picture upload ($2500)
ImageMagick GIF coder vulnerability leading to memory disclosure ($500)
RCE using bash command injection on /system/images (toimitilat.lahitapiola.fi) ($6800)
Remote Code Execution on www.semrush.com/my_reports on Logo upload ($10000)
Рассмотрим, в чем суть данной уязвимости.
ImageMagick в конфигурации использовал некоторые сторонние утилиты, в которые он подставлял строки из файлов или метаданных файла и дальше собирал какие-то данные. В итоге получалось, что в некоторую конфигурацию он подставлял те данные, которые приходили от пользователя, и вызывал те или иные команды, чтобы получить результат.
В итоге пользователь мог передать какую-нибудь картинку (например, аватарку) в формате SVG, внутри которой была ссылка на стороннюю картинку. При конвертации картинки, которая внутри SVG, ImageMagick пытался ее запросить с помощью команды wget, и подставлял адрес той картинки. Что приводило к тому, что можно было поставить двойную кавычку и выполнить стороннюю команду.
Компоненты
Под капотом всех приложений, которые сейчас издаются, кроется огромное количество разнообразных взаимосвязанных компонент и библиотек.
Каждая компонента состоит из множества библиотек, даже из таких, которые мы просто скачали и начали использовать. Уязвимости могут крыться как раз в этих библиотеках. Мы можем быть не виноваты, но наше ПО все равно будет уязвимо.
Сами посудите, есть некоторый сервис, который может заниматься абсолютно какой-нибудь ерундой, если смотреть в рамках всего приложения. Однако в нем какой-то компонент использует библиотеку, о существовании которой мы не знаем — и в ней кроется уязвимость. По цепочке это приводит к тому, что всё наше приложение, как бы круто оно ни было написано, становится уязвимым.
Поиск уязвимости blackbox методом
Инъекции могут быть в разных местах команды. Иногда инъекция может выполниться даже после правильного завершения команды.
Существуют разные спецсимволы, которые позволяют разделить команды:; ,&,&&, | , 11 , >. Выбор спецсимвола влияет на условия — параллельное или последовательное выполнение, в зависимости или вне от того, что возвращает первая команда, перенаправление вывода и так далее.
Например, можно внедрить команду “sleep” и посмотреть, увеличится ли время выполнения (базовое время выполнения скрипта ping.php — около 4 секунд). Если поставить символ &, который говорит, что команда должна выполняться параллельно, то сервер вернет ответ за 5 секунд. А если восьмерки обернуть кавычками, то команда завершится неправильно:
В принципе можно сформировать несколько небольших универсальных векторов, используя команды, которые есть почти в любой ОС типа sleep, и проверять вектора на эксплуатацию. Естественно, для эксплуатации той или иной совместимости надо выполнять нормальные команды, например, “sleep”. Они помогут подтвердить, что уязвимость присутствует.
Если можно увидеть ответ от сервера, то станет ясно, от какого пользователя выполнялось приложение или скрипт. Если результат выполнения команды не отображается на странице, речь идет о слепой инъекции. Об исполнении команды можно понять по косвенным признакам:
По времени. Для этого используйте функции sleep<), benchmark) и аналогичные.
Запрос на внешний сервис и разрешение DNS имени. Попробуйте команды ping, nslookup, dig, whois.
В большинстве случаев выполнение произвольных, но безвредных команд вроде sleep, id, hostname, whoami достаточно для подтверждения критичности уязвимости.
Поиск уязвимости при наличии исходных текстов (White Box)
Если у нас есть доступ к коду, почему бы не проанализировать его?
Абсолютно во всех языках программирования есть функции, которые отвечают за вызов командной оболочки или за исполнение стороннего кода. У всех этих функций, что хорошо, имеются говорящие названия: exec, system, start. Поэтому, даже не имея навыков разработки на определенном языке программирования, можно поискать функции с говорящими названиями и, возможно, это будет иметь успех.
Инструменты
При наличии исходных текстов уязвимость можно обнаружить поиском небезопасных функций, специфичных для данного языка программирования. Здесь помогут даже grep или notepad++.
Однако есть довольно много инструментов, которые позволяют автоматизировать такой поиск и исследовать исходный код на наличие тех или иных уязвимостей:
Анализаторы кода
Checkmarx;
Cppcheck для C++ и C;
Rats, который работает типа «грепалки»;
RIPS – крутой анализатор для PHP (сейчас есть в платном и бесплатном варианте);
Findbugs для языка Java;
GREP
Много их...
Однако обратите внимание, что многие из них выполняют статический анализ и ищут уязвимости, связанные именно с программированием. Например, как используется переменная: не используется вообще или вызывается 3 раза и при этом ни для чего не работает. Многие из этих инструментов заточены только под статический анализ, а не под безопасность. Скорее всего, они найдут какие-то уязвимости, но далеко не все.
Исправление и предотвращение внедрения команд
Весь пользовательский ввод или любые данные, которые приходят из не доверенных источников, мы должны обрабатывать, и эти данные должны проходить валидацию.
Символы, которые необходимо фильтровать: <>&*'!-?; []А~! . " % @ \ '
Во всех языках программирования и фреймворках есть специальные функции, которые позволяют безопасно выполнять те или иные команды, например, ProcessBuilder в Java. Для построения команды эти функции должны использоваться обязательно.
Но если в какой-то команде есть ряд параметров, из-за которых команда сама по себе может представлять угрозу, то злоумышленнику, понятное дело, достаточно её и нет смысла вызывать стороннюю команду.
Также желательно, чтобы веб-приложение и его компоненты работали по принципу наименьших привилегий. То есть если мы запускаем наше приложение от root, не стоит потом говорить, что жизнь не очень легка. Если даже злоумышленник найдет уязвимость, проэксплуатирует ее и получит доступ к серверу, нужно, чтобы он потратил еще уйму времени и несколько раз просигнализировал о себе. Чтобы мы смогли его обнаружить и закрыть ту или иную дыру.
Посмотрим теперь на другой вид инъекций.
SQL-инъекции
В общем смысле с помощью SQL-инъекции злоумышленник может контролировать запросы, которые приложение выполняет в базе данных. Инъекция возникает, когда приложение подставляет неотфильтрованные данные из запроса пользователя (например GET/POSТ параметры, HTTP заголовки) в запрос к базе данных без какой-либо обработки.
Это позволяет злоумышленнику внедрить свой стиль команды, изменить логику SQL запроса и таким образом получить доступ к чувствительным данным, хранящимся в БД, например: данным пользователей, платежным данным, сохраненным паролям, если они лежат не в зашифрованном виде.
В ряде случаев злоумышленник может не только получить данные, но изменить и удалить их, а также — более редкий кейс, но тоже бывает — выполнить код на уровне ОС. Здесь злоумышленник может развить атаку и скомпрометировать весь сервер или другие объекты инфраструктуры.
Как выглядит уязвимость?
Представим, что у нас есть таблица “Users”для двух пользователей (Боба и Алисы), а также скрипт, который получает от клиента идентификатор userid и выводит имя пользователя по этому значению:
Скрипт возвращает «Bob», потому что у Боба userid=1. И вроде бы код хорош, но это не так, так как он уязвим по SQL-инъекции. Итоговый SQL запрос имеет следующий вид:
Так как 1 подставляется как есть, то можно передать туда какой-то текст. Если коде нет ни санитизации, ни фильтрации пользовательского ввода, то значение GET-параметра id попадет в $sqlQuery как есть. Для эксплуатации злоумышленник должен изменить значение параметра id так, чтобы он содержал вредоносный SQL-код для исполнения.
Это был условно практический пример, теперь перейдем к практическому. Представим стандартную панель входа в сервис, куда нужно ввести имя пользователя и пароль. И есть скрипт аутентификации и таблица с пользователями, у которых есть логины и пароли:
После получения логин и пароль вставляются как есть в SQL запрос к БД. Если запрос будет валидным, то он исполнится, как и должен был. Но если попытаться найти Боба с паролем куча единиц, то получим ошибку, потому что такого пароля нет. И это место для SQL-инъекции.
Как можно использовать эту уязвимость? Например, отправить в качестве логина admin’ и знак комментария:
Благодаря одинарной кавычке запрос закроется раньше, чем это предполагалось. Двойной дефис в MySQL является комментарием, то есть эту часть запроса СУБД при исполнении просто отбросит, а там была проверка пароля. Запрос выполнится успешно и приложение нас пропустит.
Некоторые разновидности SQL-инъекций
UNION BASED
Способ основан на использовании оператора UNION. Предположим, в приложении присутствует две таблицы — статьи (Articles) и пользователи (Users). SQL-инъекция присутствует в запросе к таблице Articles, при этом необходимо извлечь данные из таблицы Users. В этом случае поможет применение SQL-оператора UNION, который объединяет два запроса в один. Важное правило — количество колонок в обоих запросах должно совпадать.
Как это происходит на практике. Представим, что первый запрос содержит 4 столбца. Попытка UNION с запросом из трех столбцов будет неудачна, поскольку БД вернет ошибку. Если Debug Mode включен, мы можем эту ошибку даже увидеть. Но объединение четырех колонок слева и четырех справа будет успешным.
Если колонок в таблице пользователей меньше, можно добавить их с помощью констант, написав 1, 2, 3 и т.д. Или же придётся из таблицы пользователей выбирать нужные колонки.
ERROR BASED
Этот способ основан на эксплуатации того, что ошибка в SQL-запросе попадает в содержимое HTML-страницы. То есть мы видим ошибку, которая происходит в нашей СУБД.
В MySQL существует функция extroctvolue(), которая ожидает в качестве аргумента данные в XML представлении, иначе она выбрасывает ошибку. Нетрудно догадаться, что этим можно воспользоваться. Если передать туда невалидные данные, она выполнит запрос и можно будет увидеть версию системы, на которой крутится СУБД. Или пользователя, от имени которого выполняется запрос в СУБД.
TIME BASED
Наверное, это самый интересный способ, который основан на времени исполнения запроса. Он применяется, если инъекция «слепая» и на страницу ничего не выводится. То есть можно только по косвенным признакам понять, что там существует SQL-инъекция. Для этого можно воспользоваться функциями:
sleep() — запрос выполняется долго, как и при использовании обычной консоль-утилиты sleep в Bash;
benchmark() — что-то выполняет очень много раз, например, может считать миллион раз MD5 от 1.
Также можно использовать конструкции вида:
IF(expr1,expr2,expr3) - ЕСЛИ «ехрг1» ИСТИНА, то выполни «ехрг2», ИНАЧЕ «ехргЗ»
Таким образом можно подбирать логин пользователя по букве. Например, если поставить первую букву «А» и запрос не уснет на 10 с, то значит, буква не «А», и т.д., пока запрос не уснет. Как только мы угадаем первую букву пользователя, запрос подвиснет на 10 с.
При этом такую рутину можно автоматизировать утилитой SQLMAP.
Инструменты
SQLMAP поддерживает большинство техник эксплуатации и умеет обходить WAF. У нее автоматическая выгрузка дампа БД, если она найдет уязвимые места SQL-инъекции. Обычно мы должны ей указать какой-то параметр, который по нашему мнению может быть уязвим.
С ее помощью можно выполнять команды (RCE), и она может работать с запросом из Burp. И при этом не нужно вручную составлять все атрибуты командной строки.
Исправление и предотвращение SQL-инъекций
Мы можем использовать:
Параметризированные запросы, когда запросы готовятся заранее, и мы им потом «скармливаем» наши данные;
ORM — она сделает всё за нас, и нам не нужно будет беспокоиться;
Query-билдеры, то есть составители запросов, которые нам предоставляют фреймворки. PHP-фреймворки этим богаты, такие как Symfony, Laravel и Spring. На выходе получается производительность обычных запросов.
В следующей статье рассказ будет про Cross Site Request forgery (CSRF), XSS (Cross Site Scripting или межсайтовый скриптинг) и XXE: XML External Entity Processing.
FrontendConf 2021 — это два дня интересных докладов, ярких встреч и много-много нетворкинга. Расписание. Билеты. Доклады.
Встречаемся 11-12 октября в Москве!