Анализ JavaScript для пентестеров
18 мая 2023 · 23 мин · Константин
Содержание
Если вы занимаетесь пентестированием веб-приложений, то вы наверняка сталкиваетесь с JavaScript. В настоящее время почти каждое веб-приложение использует этот язык. Такие фреймворки, как Angular, React и Vue.js, размещают множество функций и бизнес-логику веб-приложений во внешнем интерфейсе. Таким образом, для тщательного пентестирования веб-приложений вам необходимо проанализировать их клиентский JavaScript.
В этой статье я расскажу вам, как это сделать. Мы рассмотрим основы статического и динамического анализа, познакомимся с понятиями обфускация и деобфускация, объясним, как обойти механизмы кодовой защиты, а также приведем практические примеры и предложим подходящие инструменты для решения конкретных задач.
Обратите внимание, что эта статья довольно длинная. Можно и даже рекомендуется пропустить темы, с которыми вы уже знакомы.
Статический анализ
Статический анализ — это анализ программного обеспечения без его выполнения. Цели статического анализа могут быть самыми разнообразными. URL-адреса в коде могут увеличить пространство для атаки и выявить неработающие средства управления доступом. Код также может содержать конфиденциальную информацию, например пароли, учетные цифровые идентификационные данные или API-ключи. Кроме того, использование опасных функций или устаревшего программного обеспечения может привести к возникновению уязвимостей в приложении.
Соберите код JavaScript
Для выполнения статического анализа сначала необходимо собрать код JavaScript. Самый простой известный мне способ — использовать Burp Suite следующим образом:
Отфильтруйте историю HTTP прокси-сервера, чтобы отображались только файлы с расширением js:
История прокси-сервера Burp Suite
Отметьте полученный список файлов JavaScript и скопируйте URL-адреса:
Настройки фильтра истории прокси-сервера
Сохраните URL-адреса в текстовый файл:
Контекстное меню для копирования выбранных URL-адресов
Для скачивания используйте Use wget -i urls.txt :
Пакетная загрузка с помощью wget
Кроме того, можно использовать инструменты разработчика вашего браузера для загрузки файлов по одному:
Инструменты разработчика Chromium
Определение конечных точек
Чтобы обнаружить конечные точки и параметры в файлах JavaScript, можно использовать LinkFinder. Результаты можно либо сохранить в HTML-файл, либо вывести на стандартное устройство вывода:
HTML-файл, либо вывести на стандартное устройство вывода:
$ python linkfinder.py -i 'js/*' -o result.html
$ python linkfinder.py -i 'js/*' -o cli
Комбинация с другими инструментами командной строки также может быть полезной:
$ python linkfinder.py -i 'js/*' -o cli | sort -u | grep rest
/rest/admin
/rest/captcha
/rest/chatbot
/rest/continue-code
/rest/continue-code/apply/
/rest/continue-code-findIt
/rest/continue-code-findIt/apply/
/rest/continue-code-fixIt
/rest/continue-code-fixIt/apply/
/rest/country-mapping
/rest/deluxe-membership
/rest/image-captcha/
/rest/memories
/rest/order-history
/rest/products
/rest/repeat-notification
/rest/saveLoginIp
/rest/track-order
/rest/user
/rest/user/authentication-details/
/rest/user/change-password?current=
/rest/user/login
/rest/user/reset-password
/rest/user/security-question?email=
/rest/user/whoami
/rest/wallet/balance
Обнаружение учетных цифровых идентификационных данных
Чтобы обнаружить учетные цифровые идентификационные данные в коде, можно использовать TruffleHog. Раньше TruffleHog искал учетные цифровые идентификационные данные в репозиториях git. В настоящее время он обладает встроенной поддержкой файловых систем и многого другого. Просто проверьте, что вы используете подкоманду filesystem.
$ ./trufflehog filesystem ~/Downloads/js --no-verification --include-detectors="all"
TruffleHog. Unearth your secrets.
Found unverified result
Detector Type: AWS
Decode Type: PLAIN
Raw result: AKIAIOSFODNN7EXAMPLE
File: ~/Downloads/js/main.js
Кроме того, для обнаружения конечных точек и учетных цифровых идентификационных данных пользователи Burp Suite Professional также могут использовать JS Miner. По завершении пассивного анализа файлов JavaScript на панели мониторинга Burp Suite появится список проблем.
Панель мониторинга Burp Suite со списком проблем
По моему опыту, JS Miner выдает наилучшие результаты по обнаружению учетных цифровых идентификационных данных и конечных точек в JavaScript.
Обнаруженные учетные цифровые идентификационные данные JS Miner
Если вы ищете что-то конкретное, вы, конечно, также можете использовать базовые инструменты командной строки, такие как grep. Ключевые слова, которые вы, возможно, захотите использовать:
password
admin
login
token
user
auth
key
Поиск опасных функций
Инструмент анализа с открытым исходным кодом для обнаружения уязвимостей в коде Semgrep. Вы можете настроить свои собственные правила поиска или использовать правила, созданные сообществом. Они способны обнаруживать учетные цифровые идентификационные данные, а также использовать потенциально уязвимые методы:
Semgrep обнаруживает JWT и функцию innerHTML
Интересными функциями и свойствами в JavaScript являются, например:
Element.innerHTML
eval()
window.postMessage()
window.addEventListener()
window.localStorage
window.sessionStorage
document.cookie
Поиск по устаревшим библиотекам
Устаревшие библиотеки JavaScript часто содержат уязвимости. Типичный пример jQuery. Часто информацию о версии можно найти в пути или имени файла библиотеки или в виде строки версии в самом файле.
Строки версии в пути и файле
Чтобы проверить, публикуются ли уязвимости, можно использовать онлайн-сервисы, такие как snyk.io.
Результаты от snyk.io для jQuery 2.2.4
Burp Suite Professional имеет встроенное средство проверки зависимостей, которое автоматизирует эту процедуру. Кроме того, можно использовать расширение Retire.js.
Устаревшая версия jQuery, обнаруженная Retire.js
Внимание! Перед созданием отчетности убедитесь, что веб-приложение действительно уязвимо! Уязвимости в библиотеках часто затрагивают только определенные функции. Если веб-приложение не использует эти функции, оно не уязвимо, несмотря на наличие библиотеки с уязвимостями. Найдите в JavaScript веб-приложения уязвимые функции, используя описанные выше методы.
Динамический анализ
Динамический анализ — это анализ программного обеспечения во время его выполнения. Как правило, вам требуется выполнить анализ не всего программного обеспечения, а только определенной его части или функции. Это позволяет воспроизвести его функциональные средства, и с точки зрения выполнения сложных вычислений такой анализ лучше, чем статический анализ.
Основные инструменты
Основными инструментами для динамического анализа JavaScript являются инструменты разработчика вашего браузера. Я предпочитаю использовать инструменты разработчика Chromium, а не Firefox из-за их более высокой производительности.
Вы можете открыть их либо в основном меню, либо нажав F12.
Открытие инструментов разработчика в Chromium
Откроется панель инструментов на текущей активной вкладке. Выберите вкладку [Sources] (Источники).
Она состоит из 4 частей.
Исходные файлы текущей страницы
Содержимое выбранного файла
Инструменты для отладки
Интерактивная консоль (если она не отображается, открыть нажатием клавиши ESC)
Вкладка [Sources] (Источники) инструмента разработчика
Пример приложения
Я запрограммировал небольшой пример приложения, чтобы продемонстрировать основные приемы. Имеется простая служба проверки связи, где вы можете ввести хост и получить вывод команды проверки связи. Такая служба должна быть уязвима для инъекции команд. Итак, давайте посмотрим поближе.
Пример службы проверки связи
Фильтрация на стороне клиента
Сначала я анализирую запрос, который отправляется при нажатии кнопки [submit] (отправить). На сервер отправляется JWT, который среди других значений содержит хост, соединение с которым нужно проверить. JWT подписаны. Это лишает нас возможности манипулировать значением хоста в Burp Suite или эффективно выполнять активное сканирование без аннулирования подписи.
Запрос отправляется в Burp Suite
Таким образом, я пытаюсь отправить полезные данные инъекции базовой команды 127.0.0.1;id из самого веб-приложения. На изображении ниже показано, как должна работать инъекция.
Уточнение инъекции команд и вставка полезных данных в поле ввода.
Но специальный символ ; фильтруется на стороне клиента. Значение хоста внутри JWT содержит значение 127.0.0.1id.
Отфильтрованный запрос в Burp Suite
Поиск точки входа
Чтобы проанализировать, что происходит при нажатии кнопки [submit] (отправить), я кликаю по ней правой кнопкой мыши и выбираю [Inspect] (Проверить).
Проверка HTML-элемента
Откроется вкладка [Elements] (Элементы) в инструментах разработчика. HTML-код кнопок ничего не показывает, так как нет свойства onclick. Но при выборе вкладки [Event Listeners] (Прослушиватели событий) появляется информация, что прослушиватель событий был зарегистрирован на вызов функции отправки в строке 3 файла secure.js.
Вкладка [Event Listeners] (Прослушиватели событий) в инструментах разработчика
Я снова переключаюсь на вкладку [Sources] (Источники), щелкнув ссылку secure.js:3. Для анализа функции я создаю точку останова в строке 4, щелкнув номер строки слева. Точка останова отображается на панели инструментов справа.
Точки останова на вкладке [Sources] (Источники)
Отладчик
Я снова нажимаю [Submit] (Отправить). Выполнение JavaScript останавливается на строке 4, и запускается отладчик. Я прохожу через определенные операторы JavaScript, нажимая F9 или щелкая соответствующий символ в верхней части панели инструментов.
Инструменты отладчика для пошагового выполнения программы
После выполнения каждого оператора, содержащего переменную, его содержимое отображается рядом и выделяется желтым цветом. Регулярное выражение по всей видимости удаляет все специальные символы, кроме ._-.
Значения переменных отображаются внутри отладчика
Прежде чем очищенное от персональных данных и конфиденциальной информации значение хоста будет отправлено в качестве полезных данных в JWT, я использую раздел Scope на панели инструментов, чтобы изменить значение переменной обратно на исходное входное значение.
Изменение значения на 127.0.0.1;id
При нажатии кнопки воспроизведения возобновляется выполнение кода в обычном режиме и появляется сообщение, что инъекция команды прошла успешно.
Результаты команды id
Общие советы
Еще три замечания
Чтобы обнаружить точку входа, иногда полезно установить точку останова на Any XHR/fetch на панели инструментов. Таким образом, выполнение приостанавливается перед отправкой XHR, и отображаются все вычисляемые переменные предыдущих операторов.
Добавьте точку останова XHR/выборки
Конечно, в JavaScript имеется также и ключ подписи. Его можно извлечь и использовать в Burp Suite для расчета правильной подписи. Я расскажу об этом в следующей статье.
DOM Invader — это расширение встроенного браузера Burp Suite, которое помогает тестировать DOM XSS. Как обычно, довольно хороши для этого ресурсы PortSwigger. Если вы хотите, чтобы я также рассказал об этом в блоге, скажите об этом.
Обфускация и деобфускация
Прежде чем мы перейдем к методам запутывания (обфускации), давайте рассмотрим понятия минификации и преобразования в удобочитаемый формат.
Минификация
Минификация — это процесс уменьшения размера файла JavaScript при сохранении его функционала. Это достигается путем удаления ненужных символов, таких как пробелы или новые строки. Иногда минификация также включает сокращение имен переменных и рефакторинг исходного кода. Многие фреймворки JavaScript минимизируют код для публикации по умолчанию.
Цель минификации — повысить эффективность передачи JavaScript. Ниже приведен (несколько искусственный) пример минимизированного кода, созданного с помощью UglifyJS, но он проясняет суть.
Пример минификации
Размер файла заметно уменьшился примерно на 40 % за счет выполнения следующих преобразований:
Преобразование в удобочитаемый формат
Преобразование в удобочитаемый формат — это процесс преобразования минимизированного кода назад в удобочитаемый для человека формат. Имеется несколько способов преобразования минимизированного кода в удобочитаемый формат.
$ pip install jsbeautifier
# Usage
$ js-beautify main.min.js > main.js
Карты исходного кода
Одним из способов полного восстановления минимизированного кода являются так называемые карты исходного кода. Файлы карты исходного кода просто содержат объект JSON, как показано ниже. Важнейшей частью является строка mappings (преобразования данных), которая содержит сопоставление между исходным и минифицированным кодом.
Пример карт исходного хода
Сопоставления закодированы в VLQ Base64, и я не буду подробно обсуждать это здесь.1
Для визуализации сопоставления можно использовать визуализаторы карт исходного кода. Например, 23->2:5 означает, что слово в столбце 23 e= сопоставляется со словом в строке 2, столбце 5 input=.
Пример визуализации карты исходного кода
Иногда карты исходного кода поставляются вместе с минимизированным кодом. Это делается путем добавления комментария к минифицированному JavaScript, который содержит местоположение карты исходного кода. Карта исходного кода будет загружаться только при открытии инструментов разработчика.
Ссылка на карту исходного кода из минифицированного JavaScript
Минификация и анализ
При выполнении анализа JavaScript минификация не представляет большой проблемы. Да, без карт исходного кода код менее понятен из-за необратимых преобразований. Однако учетные цифровые идентификационные данные, ссылки и вызовы уязвимых функций все еще могут быть обнаружены с помощью статического анализа, как обсуждалось выше.
При выполнении динамического анализа отсутствие описательных идентификаторов компенсируется отладчиком в инструментах разработчика Chromium. Рядом с операторами JavaScript отображаются значения переменных после их выполнения.
Обфускация
Обфускация JavaScript предназначена для предотвращения его анализа, статического или динамического, с сохранением его функционала. Для этого допускается даже потеря производительности кода. Часто обфускаторы помещают слова и символы из кода в массив. Во время выполнения восстановление исходного кода осуществляется за счет повторяющихся ссылок на этот массив. На изображении показан пример так называемой упаковки.
Пример упакованного массива в обфусцированном коде
Как видите, произошло следующее преобразование:
document.getElementById("input").value
document[d(0x0)](d(0x1))[d(0x2)];
d — это функция, которая ссылается на массив, определенный в функции a. 0x0 — первая строка, 0x1 — вторая и т. д.
Некоторые другие преобразования, которые часто используются:
Имеется еще больше обфускаторов в различных вариантах. Один из наиболее совершенных — JSFuck. Он использует только эти шесть символов: ![]+()
Пример обфускации с помощью JSFuck
Деобфускация
Деобфускация — это процесс преобразования обфусцированного кода назад в удобочитаемый для человека формат. Как вы можете понять из приведенных выше примеров, это возможно сделать нетривиальным образом. Преобразование обфусцированного кода в удобочитаемый формат — это всего лишь один шаг, который все равно оставляет код почти нечитаемым.
Кроме того, деобфускаторы часто полагаются на следующие методы:
Обфускация и анализ
К сожалению, результаты, полученные после деобфускации, обычно не имеют ничего общего с исходным кодом JavaScript. Тем не менее, они часто помогают упростить последовательность выполнения кода, что является преимуществом для последующего динамического анализа. На рисунке ниже показано преобразование простого кода в обфусцированный и в окончательно деобфусцированный код.
Преобразование простого кода в обфусцированный и деобфусцированный
Практическое занятие: анализ обфусцированного кода
Рассмотрев темы, посвященные статическому анализу, динамическому анализу и обфускации JavaScript, я выполню динамический анализ обфусцированного JavaScript на примере приложения. На каждом этапе я буду объяснять свой подход, процедуру и выводы.
Прежде чем перейти к рассмотрению инструментов динамического анализа, я бы хотел сказать пару слов о соответствующем образе мышления. Динамический анализ может быть трудоемким процессом. Иногда полезно вспомнить, что любая функция на стороне клиента, которую вы ищете, определенно присутствует в клиентском коде. Например, если веб-приложение подписывает данные перед их отправкой на сервер, ключ подписи ДОЛЖЕН находиться в клиентском коде. Такое отношение помогает вам быть упорнее.
Пример приложения
Приложение, которое я собираюсь проанализировать — это служба проверки связи, описанная выше, но с обфусцированным JavaScript. Пользователь отправляет хост и получает реакцию на команду Ping от сервера. Перехват запроса в Burp Suite показывает, что приложение отправляет JWT, содержащий хост (IP-адрес) целевого сервера.
Запрос отправляется в Burp Suite
Так как JWT подписаны, невозможно манипулировать значением хоста без аннулирования подписи. Это не позволяет мне работать с запросом в Burp Suite, что необходимо для пентеста. Но поскольку JWT рассчитывается с помощью JavaScript, ключ должен находиться где-то в клиентском коде. Цель заключается в том, чтобы извлечь секретный ключ для проведения надлежащего анализа конечной точки.
Поиск точки входа
Во-первых, я хочу узнать, что происходит при нажатии кнопки [Submit] (Отправить). Для этого я открываю вкладку [Elements] (Элементы) инструментов разработчика и проверяю прослушиватели событий. Это выявляет прослушиватель событий щелчка.
Прослушиватель событий в инструментах разработчика Chromium
При нажатии на ссылку открывается вкладка [Sources] (Источники) и подсвечивается положение функции прослушивателя. Посмотрите, как инструменты разработчика Chromium преобразовали обфусцированный код, который на самом деле написан внутри одной строки.
Преобразованный минимизированный JavaScript с выделенной функцией отправки
Я ставлю точку останова на первый оператор в функции отправки и нажимаю кнопку [Submit] (Отправить). Точка останова запускает и приостанавливает выполнение. Это означает, что я нашел правильное положение и теперь могу осуществлять пошаговое выполнение.
Выполнение приостановлено на точке останова
Определение значение в зоне интереса
В какой-то момент готовый к отправке JWT должен храниться внутри переменной JavaScript. Я использую кнопку [Step over next function call] (Перейти к вызову следующей функции) в инструментах разработчика, чтобы найти это положение. Выделенные ниже строки содержат заголовок JWT, полезные данные и, наконец, закодированный JWT, начиная со строки параметров ey[...]
Определение места расчета JWT
Подписание должно происходить где-то в строке, в которой рассчитывается JWT. Я ставлю точку останова в этой строке и удаляю старую, чтобы пропустить ненужные операторы во время анализа. После этого я снова нажимаю кнопку [Submit] (Отправить).
Новая точка останова при расчете JWT
Функция распаковки
Теперь начинается сложная часть. Чтобы продолжить выполнение, я использую кнопку [Step] (Шаг). Это приводит к функции распаковки обфусцированного кода, где символы и строки JavaScript загружаются из массива.
Функция 0x246a() содержит упакованный массив
Теперь переменная ссылается на весь массив. Это можно увидеть в разделе «Scope» инструментов разработчика справа.
Упакованный массив, отображаемый в разделе «Scope»
Этот шаг показывает, что будет возвращена 20-я запись массива.
Функция распаковки делает свою работу
Действительно, возвращается значение JWS с индексом 20.
Возвращаемая переменная 0x51f223 содержит значение JWS
Восстановление вызова подписания
При повторном использовании того же метода я проверяю, что следующие распакованные значения из массива будут:
Последний шаг распаковки перед расчетом JWT
Это позволяет мне восстановить вызов, который используется для расчета JWT: строка, которая вычисляет JWT, имеет следующую структуру KJUR['jws'][a](c,d,e,f). Переменные a, b, c и f — это распакованные значения, указанные выше. Переменные d и e — это заголовок и полезные данные JWT. Собрав все вместе и заменив массив- на точечное представление, мы получим следующий вызов функции:
KJUR.jws.JWS.sign("HS256", {header}, {payload}, "6465[...]3866")
Происхождение обфусцированных переменных и вызовов функций
Переход к следующему шагу подтверждает это. В разделе «scope» отображаются те переменные, которые были переданы в функцию.
Область действия функции KJUR.jws.JWS.sign()
На данный момент самый простой способ — выяснить, взята ли эта функция из библиотеки JavaScript и что именно делает JWS.sign. Быстрый поиск в Google показывает, что библиотека действительно используется. Она называется jsrsasign пользователем GitHub kjur.
В документации показано, что метод генерирует подпись JWS с указанным ключом. Ключ в положении 4 вызова функции в данном случае является шестнадцатеричным значением. Это означает, что длинное целое число, показанное выше — это ключ.
Документация jsrsasign
Расшифровка ключа
Я декодирую шестнадцатеричный ключ и кодирую результат в base64, так как это то, что мне нужно для JWT Editor расширения Burp Suite.
Использование xxd и base64 для декодирования и кодирования
Подписание управляемых JWT
В расширении JWT Editor я создаю новый симметричный ключ, поскольку HS256 — это метод симметричного подписания. Я вставляю ключ из предыдущего шага в поле "k".
Создание нового симметричного ключа в JWT Editor
Я отправляю запрос из истории прокси-сервера Burp Suite в Repeater и переключаюсь на вкладку JSON Web Token. Здесь я заменяю хост 127.0.0.1 на infosec.exchange и нажимаю кнопку [Sign] (Подписать).
Использование ключа для подписи обрабатываемого запроса
Я отправляю запрос с обновленной подписью на сервер и получаю результаты проверки связи для infosec.exchange. Это означает, что расчет подписи выполнен успешно, и теперь я могу использовать значение по мере необходимости.
18 мая 2023 · 23 мин · Константин
Содержание
Если вы занимаетесь пентестированием веб-приложений, то вы наверняка сталкиваетесь с JavaScript. В настоящее время почти каждое веб-приложение использует этот язык. Такие фреймворки, как Angular, React и Vue.js, размещают множество функций и бизнес-логику веб-приложений во внешнем интерфейсе. Таким образом, для тщательного пентестирования веб-приложений вам необходимо проанализировать их клиентский JavaScript.
В этой статье я расскажу вам, как это сделать. Мы рассмотрим основы статического и динамического анализа, познакомимся с понятиями обфускация и деобфускация, объясним, как обойти механизмы кодовой защиты, а также приведем практические примеры и предложим подходящие инструменты для решения конкретных задач.
Обратите внимание, что эта статья довольно длинная. Можно и даже рекомендуется пропустить темы, с которыми вы уже знакомы.
Статический анализ
Статический анализ — это анализ программного обеспечения без его выполнения. Цели статического анализа могут быть самыми разнообразными. URL-адреса в коде могут увеличить пространство для атаки и выявить неработающие средства управления доступом. Код также может содержать конфиденциальную информацию, например пароли, учетные цифровые идентификационные данные или API-ключи. Кроме того, использование опасных функций или устаревшего программного обеспечения может привести к возникновению уязвимостей в приложении.
Соберите код JavaScript
Для выполнения статического анализа сначала необходимо собрать код JavaScript. Самый простой известный мне способ — использовать Burp Suite следующим образом:
Отфильтруйте историю HTTP прокси-сервера, чтобы отображались только файлы с расширением js:
История прокси-сервера Burp Suite
Отметьте полученный список файлов JavaScript и скопируйте URL-адреса:
Настройки фильтра истории прокси-сервера
Сохраните URL-адреса в текстовый файл:
Контекстное меню для копирования выбранных URL-адресов
Для скачивания используйте Use wget -i urls.txt :
Пакетная загрузка с помощью wget
Кроме того, можно использовать инструменты разработчика вашего браузера для загрузки файлов по одному:
Инструменты разработчика Chromium
Определение конечных точек
Чтобы обнаружить конечные точки и параметры в файлах JavaScript, можно использовать LinkFinder. Результаты можно либо сохранить в HTML-файл, либо вывести на стандартное устройство вывода:
HTML-файл, либо вывести на стандартное устройство вывода:
$ python linkfinder.py -i 'js/*' -o result.html
$ python linkfinder.py -i 'js/*' -o cli
Комбинация с другими инструментами командной строки также может быть полезной:
$ python linkfinder.py -i 'js/*' -o cli | sort -u | grep rest
/rest/admin
/rest/captcha
/rest/chatbot
/rest/continue-code
/rest/continue-code/apply/
/rest/continue-code-findIt
/rest/continue-code-findIt/apply/
/rest/continue-code-fixIt
/rest/continue-code-fixIt/apply/
/rest/country-mapping
/rest/deluxe-membership
/rest/image-captcha/
/rest/memories
/rest/order-history
/rest/products
/rest/repeat-notification
/rest/saveLoginIp
/rest/track-order
/rest/user
/rest/user/authentication-details/
/rest/user/change-password?current=
/rest/user/login
/rest/user/reset-password
/rest/user/security-question?email=
/rest/user/whoami
/rest/wallet/balance
Обнаружение учетных цифровых идентификационных данных
Чтобы обнаружить учетные цифровые идентификационные данные в коде, можно использовать TruffleHog. Раньше TruffleHog искал учетные цифровые идентификационные данные в репозиториях git. В настоящее время он обладает встроенной поддержкой файловых систем и многого другого. Просто проверьте, что вы используете подкоманду filesystem.
$ ./trufflehog filesystem ~/Downloads/js --no-verification --include-detectors="all"
TruffleHog. Unearth your secrets.
Found unverified result

Detector Type: AWS
Decode Type: PLAIN
Raw result: AKIAIOSFODNN7EXAMPLE
File: ~/Downloads/js/main.js
Кроме того, для обнаружения конечных точек и учетных цифровых идентификационных данных пользователи Burp Suite Professional также могут использовать JS Miner. По завершении пассивного анализа файлов JavaScript на панели мониторинга Burp Suite появится список проблем.
Панель мониторинга Burp Suite со списком проблем
По моему опыту, JS Miner выдает наилучшие результаты по обнаружению учетных цифровых идентификационных данных и конечных точек в JavaScript.
Обнаруженные учетные цифровые идентификационные данные JS Miner
Если вы ищете что-то конкретное, вы, конечно, также можете использовать базовые инструменты командной строки, такие как grep. Ключевые слова, которые вы, возможно, захотите использовать:
password
admin
login
token
user
auth
key
Поиск опасных функций
Инструмент анализа с открытым исходным кодом для обнаружения уязвимостей в коде Semgrep. Вы можете настроить свои собственные правила поиска или использовать правила, созданные сообществом. Они способны обнаруживать учетные цифровые идентификационные данные, а также использовать потенциально уязвимые методы:
Semgrep обнаруживает JWT и функцию innerHTML
Интересными функциями и свойствами в JavaScript являются, например:
Element.innerHTML
eval()
window.postMessage()
window.addEventListener()
window.localStorage
window.sessionStorage
document.cookie
Поиск по устаревшим библиотекам
Устаревшие библиотеки JavaScript часто содержат уязвимости. Типичный пример jQuery. Часто информацию о версии можно найти в пути или имени файла библиотеки или в виде строки версии в самом файле.
Строки версии в пути и файле
Чтобы проверить, публикуются ли уязвимости, можно использовать онлайн-сервисы, такие как snyk.io.
Результаты от snyk.io для jQuery 2.2.4
Burp Suite Professional имеет встроенное средство проверки зависимостей, которое автоматизирует эту процедуру. Кроме того, можно использовать расширение Retire.js.
Устаревшая версия jQuery, обнаруженная Retire.js
Внимание! Перед созданием отчетности убедитесь, что веб-приложение действительно уязвимо! Уязвимости в библиотеках часто затрагивают только определенные функции. Если веб-приложение не использует эти функции, оно не уязвимо, несмотря на наличие библиотеки с уязвимостями. Найдите в JavaScript веб-приложения уязвимые функции, используя описанные выше методы.
Динамический анализ
Динамический анализ — это анализ программного обеспечения во время его выполнения. Как правило, вам требуется выполнить анализ не всего программного обеспечения, а только определенной его части или функции. Это позволяет воспроизвести его функциональные средства, и с точки зрения выполнения сложных вычислений такой анализ лучше, чем статический анализ.
Основные инструменты
Основными инструментами для динамического анализа JavaScript являются инструменты разработчика вашего браузера. Я предпочитаю использовать инструменты разработчика Chromium, а не Firefox из-за их более высокой производительности.
Вы можете открыть их либо в основном меню, либо нажав F12.
Открытие инструментов разработчика в Chromium
Откроется панель инструментов на текущей активной вкладке. Выберите вкладку [Sources] (Источники).
Она состоит из 4 частей.
Исходные файлы текущей страницы
Содержимое выбранного файла
Инструменты для отладки
Интерактивная консоль (если она не отображается, открыть нажатием клавиши ESC)
Вкладка [Sources] (Источники) инструмента разработчика
Пример приложения
Я запрограммировал небольшой пример приложения, чтобы продемонстрировать основные приемы. Имеется простая служба проверки связи, где вы можете ввести хост и получить вывод команды проверки связи. Такая служба должна быть уязвима для инъекции команд. Итак, давайте посмотрим поближе.
Пример службы проверки связи
Фильтрация на стороне клиента
Сначала я анализирую запрос, который отправляется при нажатии кнопки [submit] (отправить). На сервер отправляется JWT, который среди других значений содержит хост, соединение с которым нужно проверить. JWT подписаны. Это лишает нас возможности манипулировать значением хоста в Burp Suite или эффективно выполнять активное сканирование без аннулирования подписи.
Запрос отправляется в Burp Suite
Таким образом, я пытаюсь отправить полезные данные инъекции базовой команды 127.0.0.1;id из самого веб-приложения. На изображении ниже показано, как должна работать инъекция.
Уточнение инъекции команд и вставка полезных данных в поле ввода.
Но специальный символ ; фильтруется на стороне клиента. Значение хоста внутри JWT содержит значение 127.0.0.1id.
Отфильтрованный запрос в Burp Suite
Поиск точки входа
Чтобы проанализировать, что происходит при нажатии кнопки [submit] (отправить), я кликаю по ней правой кнопкой мыши и выбираю [Inspect] (Проверить).
Проверка HTML-элемента
Откроется вкладка [Elements] (Элементы) в инструментах разработчика. HTML-код кнопок ничего не показывает, так как нет свойства onclick. Но при выборе вкладки [Event Listeners] (Прослушиватели событий) появляется информация, что прослушиватель событий был зарегистрирован на вызов функции отправки в строке 3 файла secure.js.
Вкладка [Event Listeners] (Прослушиватели событий) в инструментах разработчика
Я снова переключаюсь на вкладку [Sources] (Источники), щелкнув ссылку secure.js:3. Для анализа функции я создаю точку останова в строке 4, щелкнув номер строки слева. Точка останова отображается на панели инструментов справа.
Точки останова на вкладке [Sources] (Источники)
Отладчик
Я снова нажимаю [Submit] (Отправить). Выполнение JavaScript останавливается на строке 4, и запускается отладчик. Я прохожу через определенные операторы JavaScript, нажимая F9 или щелкая соответствующий символ в верхней части панели инструментов.
Инструменты отладчика для пошагового выполнения программы
После выполнения каждого оператора, содержащего переменную, его содержимое отображается рядом и выделяется желтым цветом. Регулярное выражение по всей видимости удаляет все специальные символы, кроме ._-.
Значения переменных отображаются внутри отладчика
Прежде чем очищенное от персональных данных и конфиденциальной информации значение хоста будет отправлено в качестве полезных данных в JWT, я использую раздел Scope на панели инструментов, чтобы изменить значение переменной обратно на исходное входное значение.
Изменение значения на 127.0.0.1;id
При нажатии кнопки воспроизведения возобновляется выполнение кода в обычном режиме и появляется сообщение, что инъекция команды прошла успешно.
Результаты команды id
Общие советы
Еще три замечания
Чтобы обнаружить точку входа, иногда полезно установить точку останова на Any XHR/fetch на панели инструментов. Таким образом, выполнение приостанавливается перед отправкой XHR, и отображаются все вычисляемые переменные предыдущих операторов.
Добавьте точку останова XHR/выборки
Конечно, в JavaScript имеется также и ключ подписи. Его можно извлечь и использовать в Burp Suite для расчета правильной подписи. Я расскажу об этом в следующей статье.
DOM Invader — это расширение встроенного браузера Burp Suite, которое помогает тестировать DOM XSS. Как обычно, довольно хороши для этого ресурсы PortSwigger. Если вы хотите, чтобы я также рассказал об этом в блоге, скажите об этом.
Обфускация и деобфускация
Прежде чем мы перейдем к методам запутывания (обфускации), давайте рассмотрим понятия минификации и преобразования в удобочитаемый формат.
Минификация
Минификация — это процесс уменьшения размера файла JavaScript при сохранении его функционала. Это достигается путем удаления ненужных символов, таких как пробелы или новые строки. Иногда минификация также включает сокращение имен переменных и рефакторинг исходного кода. Многие фреймворки JavaScript минимизируют код для публикации по умолчанию.
Цель минификации — повысить эффективность передачи JavaScript. Ниже приведен (несколько искусственный) пример минимизированного кода, созданного с помощью UglifyJS, но он проясняет суть.
Пример минификации
Размер файла заметно уменьшился примерно на 40 % за счет выполнения следующих преобразований:
- были удалены пробелы, новые строки, комментарии и ненужные фигурные скобки;
- локальная переменная input была переименована в e;
- оператор if-else был заменен так называемым тернарным оператором (x?a:b).
Преобразование в удобочитаемый формат
Преобразование в удобочитаемый формат — это процесс преобразования минимизированного кода назад в удобочитаемый для человека формат. Имеется несколько способов преобразования минимизированного кода в удобочитаемый формат.
- Отладчик Chromium выполняет структурную распечатку программы JavaScript по умолчанию. Он добавляет отступы и разрывы строк в коде.
- Для преобразования локальных файлов JavaScript в удобочитаемый формат, можно использовать такой инструмент, как js-beautify:
$ pip install jsbeautifier
# Usage
$ js-beautify main.min.js > main.js
- Также имеется онлайн сервис:
Карты исходного кода
Одним из способов полного восстановления минимизированного кода являются так называемые карты исходного кода. Файлы карты исходного кода просто содержат объект JSON, как показано ниже. Важнейшей частью является строка mappings (преобразования данных), которая содержит сопоставление между исходным и минифицированным кодом.
Пример карт исходного хода
Сопоставления закодированы в VLQ Base64, и я не буду подробно обсуждать это здесь.1
Для визуализации сопоставления можно использовать визуализаторы карт исходного кода. Например, 23->2:5 означает, что слово в столбце 23 e= сопоставляется со словом в строке 2, столбце 5 input=.
Пример визуализации карты исходного кода
Иногда карты исходного кода поставляются вместе с минимизированным кодом. Это делается путем добавления комментария к минифицированному JavaScript, который содержит местоположение карты исходного кода. Карта исходного кода будет загружаться только при открытии инструментов разработчика.
Ссылка на карту исходного кода из минифицированного JavaScript
Минификация и анализ
При выполнении анализа JavaScript минификация не представляет большой проблемы. Да, без карт исходного кода код менее понятен из-за необратимых преобразований. Однако учетные цифровые идентификационные данные, ссылки и вызовы уязвимых функций все еще могут быть обнаружены с помощью статического анализа, как обсуждалось выше.
При выполнении динамического анализа отсутствие описательных идентификаторов компенсируется отладчиком в инструментах разработчика Chromium. Рядом с операторами JavaScript отображаются значения переменных после их выполнения.
Обфускация
Обфускация JavaScript предназначена для предотвращения его анализа, статического или динамического, с сохранением его функционала. Для этого допускается даже потеря производительности кода. Часто обфускаторы помещают слова и символы из кода в массив. Во время выполнения восстановление исходного кода осуществляется за счет повторяющихся ссылок на этот массив. На изображении показан пример так называемой упаковки.
Пример упакованного массива в обфусцированном коде
Как видите, произошло следующее преобразование:
document.getElementById("input").value
document[d(0x0)](d(0x1))[d(0x2)];d — это функция, которая ссылается на массив, определенный в функции a. 0x0 — первая строка, 0x1 — вторая и т. д.
Некоторые другие преобразования, которые часто используются:
- переименование идентификаторов в шестнадцатеричные строки;
- код с функциями самозащиты, который взламывается при преобразовании в удобочитаемый формат;
- внедрение мертвого кода, увеличивающего размер файла.
Имеется еще больше обфускаторов в различных вариантах. Один из наиболее совершенных — JSFuck. Он использует только эти шесть символов: ![]+()
Пример обфускации с помощью JSFuck
Деобфускация
Деобфускация — это процесс преобразования обфусцированного кода назад в удобочитаемый для человека формат. Как вы можете понять из приведенных выше примеров, это возможно сделать нетривиальным образом. Преобразование обфусцированного кода в удобочитаемый формат — это всего лишь один шаг, который все равно оставляет код почти нечитаемым.
Кроме того, деобфускаторы часто полагаются на следующие методы:
- распаковка массивов;
- замена прокси-функций;
- удаление мертвых ветвей кода;
- переименование идентификаторов.
Обфускация и анализ
К сожалению, результаты, полученные после деобфускации, обычно не имеют ничего общего с исходным кодом JavaScript. Тем не менее, они часто помогают упростить последовательность выполнения кода, что является преимуществом для последующего динамического анализа. На рисунке ниже показано преобразование простого кода в обфусцированный и в окончательно деобфусцированный код.
Преобразование простого кода в обфусцированный и деобфусцированный
Практическое занятие: анализ обфусцированного кода
Рассмотрев темы, посвященные статическому анализу, динамическому анализу и обфускации JavaScript, я выполню динамический анализ обфусцированного JavaScript на примере приложения. На каждом этапе я буду объяснять свой подход, процедуру и выводы.
Прежде чем перейти к рассмотрению инструментов динамического анализа, я бы хотел сказать пару слов о соответствующем образе мышления. Динамический анализ может быть трудоемким процессом. Иногда полезно вспомнить, что любая функция на стороне клиента, которую вы ищете, определенно присутствует в клиентском коде. Например, если веб-приложение подписывает данные перед их отправкой на сервер, ключ подписи ДОЛЖЕН находиться в клиентском коде. Такое отношение помогает вам быть упорнее.
Пример приложения
Приложение, которое я собираюсь проанализировать — это служба проверки связи, описанная выше, но с обфусцированным JavaScript. Пользователь отправляет хост и получает реакцию на команду Ping от сервера. Перехват запроса в Burp Suite показывает, что приложение отправляет JWT, содержащий хост (IP-адрес) целевого сервера.
Запрос отправляется в Burp Suite
Так как JWT подписаны, невозможно манипулировать значением хоста без аннулирования подписи. Это не позволяет мне работать с запросом в Burp Suite, что необходимо для пентеста. Но поскольку JWT рассчитывается с помощью JavaScript, ключ должен находиться где-то в клиентском коде. Цель заключается в том, чтобы извлечь секретный ключ для проведения надлежащего анализа конечной точки.
Поиск точки входа
Во-первых, я хочу узнать, что происходит при нажатии кнопки [Submit] (Отправить). Для этого я открываю вкладку [Elements] (Элементы) инструментов разработчика и проверяю прослушиватели событий. Это выявляет прослушиватель событий щелчка.
Прослушиватель событий в инструментах разработчика Chromium
При нажатии на ссылку открывается вкладка [Sources] (Источники) и подсвечивается положение функции прослушивателя. Посмотрите, как инструменты разработчика Chromium преобразовали обфусцированный код, который на самом деле написан внутри одной строки.
Преобразованный минимизированный JavaScript с выделенной функцией отправки
Я ставлю точку останова на первый оператор в функции отправки и нажимаю кнопку [Submit] (Отправить). Точка останова запускает и приостанавливает выполнение. Это означает, что я нашел правильное положение и теперь могу осуществлять пошаговое выполнение.
Выполнение приостановлено на точке останова
Определение значение в зоне интереса
В какой-то момент готовый к отправке JWT должен храниться внутри переменной JavaScript. Я использую кнопку [Step over next function call] (Перейти к вызову следующей функции) в инструментах разработчика, чтобы найти это положение. Выделенные ниже строки содержат заголовок JWT, полезные данные и, наконец, закодированный JWT, начиная со строки параметров ey[...]
Определение места расчета JWT
Подписание должно происходить где-то в строке, в которой рассчитывается JWT. Я ставлю точку останова в этой строке и удаляю старую, чтобы пропустить ненужные операторы во время анализа. После этого я снова нажимаю кнопку [Submit] (Отправить).
Новая точка останова при расчете JWT
Функция распаковки
Теперь начинается сложная часть. Чтобы продолжить выполнение, я использую кнопку [Step] (Шаг). Это приводит к функции распаковки обфусцированного кода, где символы и строки JavaScript загружаются из массива.
Функция 0x246a() содержит упакованный массив
Теперь переменная ссылается на весь массив. Это можно увидеть в разделе «Scope» инструментов разработчика справа.
Упакованный массив, отображаемый в разделе «Scope»
Этот шаг показывает, что будет возвращена 20-я запись массива.
Функция распаковки делает свою работу
Действительно, возвращается значение JWS с индексом 20.
Возвращаемая переменная 0x51f223 содержит значение JWS
Восстановление вызова подписания
При повторном использовании того же метода я проверяю, что следующие распакованные значения из массива будут:
- sign
- HS256
Последний шаг распаковки перед расчетом JWT
Это позволяет мне восстановить вызов, который используется для расчета JWT: строка, которая вычисляет JWT, имеет следующую структуру KJUR['jws'][a](c,d,e,f). Переменные a, b, c и f — это распакованные значения, указанные выше. Переменные d и e — это заголовок и полезные данные JWT. Собрав все вместе и заменив массив- на точечное представление, мы получим следующий вызов функции:
KJUR.jws.JWS.sign("HS256", {header}, {payload}, "6465[...]3866")
Происхождение обфусцированных переменных и вызовов функций
Переход к следующему шагу подтверждает это. В разделе «scope» отображаются те переменные, которые были переданы в функцию.
Область действия функции KJUR.jws.JWS.sign()
На данный момент самый простой способ — выяснить, взята ли эта функция из библиотеки JavaScript и что именно делает JWS.sign. Быстрый поиск в Google показывает, что библиотека действительно используется. Она называется jsrsasign пользователем GitHub kjur.
В документации показано, что метод генерирует подпись JWS с указанным ключом. Ключ в положении 4 вызова функции в данном случае является шестнадцатеричным значением. Это означает, что длинное целое число, показанное выше — это ключ.
Документация jsrsasign
Расшифровка ключа
Я декодирую шестнадцатеричный ключ и кодирую результат в base64, так как это то, что мне нужно для JWT Editor расширения Burp Suite.
Использование xxd и base64 для декодирования и кодирования
Подписание управляемых JWT
В расширении JWT Editor я создаю новый симметричный ключ, поскольку HS256 — это метод симметричного подписания. Я вставляю ключ из предыдущего шага в поле "k".
Создание нового симметричного ключа в JWT Editor
Я отправляю запрос из истории прокси-сервера Burp Suite в Repeater и переключаюсь на вкладку JSON Web Token. Здесь я заменяю хост 127.0.0.1 на infosec.exchange и нажимаю кнопку [Sign] (Подписать).
Использование ключа для подписи обрабатываемого запроса
Я отправляю запрос с обновленной подписью на сервер и получаю результаты проверки связи для infosec.exchange. Это означает, что расчет подписи выполнен успешно, и теперь я могу использовать значение по мере необходимости.