• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Анализ JavaScript для пентестеров

imanotape

HDD-drive
Пользователь
Регистрация
28.10.2022
Сообщения
31
Реакции
39
Гарант сделки
3
Анализ JavaScript для пентестеров

18 мая 2023 · 23 мин · Константин

Picture1.png


Содержание

Если вы занимаетесь пентестированием веб-приложений, то вы наверняка сталкиваетесь с JavaScript. В настоящее время почти каждое веб-приложение использует этот язык. Такие фреймворки, как Angular, React и Vue.js, размещают множество функций и бизнес-логику веб-приложений во внешнем интерфейсе. Таким образом, для тщательного пентестирования веб-приложений вам необходимо проанализировать их клиентский JavaScript.

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

Обратите внимание, что эта статья довольно длинная. Можно и даже рекомендуется пропустить темы, с которыми вы уже знакомы.

Статический анализ

Статический анализ — это анализ программного обеспечения без его выполнения. Цели статического анализа могут быть самыми разнообразными. URL-адреса в коде могут увеличить пространство для атаки и выявить неработающие средства управления доступом. Код также может содержать конфиденциальную информацию, например пароли, учетные цифровые идентификационные данные или API-ключи. Кроме того, использование опасных функций или устаревшего программного обеспечения может привести к возникновению уязвимостей в приложении.

Соберите код JavaScript

Для выполнения статического анализа сначала необходимо собрать код JavaScript. Самый простой известный мне способ — использовать Burp Suite следующим образом:

Отфильтруйте историю HTTP прокси-сервера, чтобы отображались только файлы с расширением js:
Picture2.png

История прокси-сервера Burp Suite

Отметьте полученный список файлов JavaScript и скопируйте URL-адреса:
Picture3.png

Настройки фильтра истории прокси-сервера

Сохраните URL-адреса в текстовый файл:
Picture4.png

Контекстное меню для копирования выбранных URL-адресов

Для скачивания используйте Use wget -i urls.txt :
Picture5.png

Пакетная загрузка с помощью wget

Кроме того, можно использовать инструменты разработчика вашего браузера для загрузки файлов по одному:
Picture6.png

Инструменты разработчика 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 появится список проблем.
Picture7.png

Панель мониторинга Burp Suite со списком проблем

По моему опыту, JS Miner выдает наилучшие результаты по обнаружению учетных цифровых идентификационных данных и конечных точек в JavaScript.
Picture8.png

Обнаруженные учетные цифровые идентификационные данные JS Miner

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

password
admin
login
token
user
auth
key

Поиск опасных функций

Инструмент анализа с открытым исходным кодом для обнаружения уязвимостей в коде Semgrep. Вы можете настроить свои собственные правила поиска или использовать правила, созданные сообществом. Они способны обнаруживать учетные цифровые идентификационные данные, а также использовать потенциально уязвимые методы:
Picture9.png

Semgrep обнаруживает JWT и функцию innerHTML

Интересными функциями и свойствами в JavaScript являются, например:

Element.innerHTML
eval()
window.postMessage()
window.addEventListener()
window.localStorage
window.sessionStorage
document.cookie


Поиск по устаревшим библиотекам

Устаревшие библиотеки JavaScript часто содержат уязвимости. Типичный пример jQuery. Часто информацию о версии можно найти в пути или имени файла библиотеки или в виде строки версии в самом файле.
Picture10.png

Строки версии в пути и файле

Чтобы проверить, публикуются ли уязвимости, можно использовать онлайн-сервисы, такие как snyk.io.
Picture11.png

Результаты от snyk.io для jQuery 2.2.4

Burp Suite Professional имеет встроенное средство проверки зависимостей, которое автоматизирует эту процедуру. Кроме того, можно использовать расширение Retire.js.
Picture12.png

Устаревшая версия jQuery, обнаруженная Retire.js

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

Динамический анализ

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

Основные инструменты

Основными инструментами для динамического анализа JavaScript являются инструменты разработчика вашего браузера. Я предпочитаю использовать инструменты разработчика Chromium, а не Firefox из-за их более высокой производительности.

Вы можете открыть их либо в основном меню, либо нажав F12.
Picture13.png

Открытие инструментов разработчика в Chromium

Откроется панель инструментов на текущей активной вкладке. Выберите вкладку [Sources] (Источники).

Она состоит из 4 частей.

Исходные файлы текущей страницы

Содержимое выбранного файла

Инструменты для отладки

Интерактивная консоль (если она не отображается, открыть нажатием клавиши ESC)
Picture14.png

Вкладка [Sources] (Источники) инструмента разработчика

Пример приложения

Я запрограммировал небольшой пример приложения, чтобы продемонстрировать основные приемы. Имеется простая служба проверки связи, где вы можете ввести хост и получить вывод команды проверки связи. Такая служба должна быть уязвима для инъекции команд. Итак, давайте посмотрим поближе.
Picture15.png

Пример службы проверки связи

Фильтрация на стороне клиента

Сначала я анализирую запрос, который отправляется при нажатии кнопки [submit] (отправить). На сервер отправляется JWT, который среди других значений содержит хост, соединение с которым нужно проверить. JWT подписаны. Это лишает нас возможности манипулировать значением хоста в Burp Suite или эффективно выполнять активное сканирование без аннулирования подписи.
Picture16.png

Запрос отправляется в Burp Suite

Таким образом, я пытаюсь отправить полезные данные инъекции базовой команды 127.0.0.1;id из самого веб-приложения. На изображении ниже показано, как должна работать инъекция.
Picture17.png

Уточнение инъекции команд и вставка полезных данных в поле ввода.

Но специальный символ ; фильтруется на стороне клиента. Значение хоста внутри JWT содержит значение 127.0.0.1id.
Picture18.png

Отфильтрованный запрос в Burp Suite

Поиск точки входа

Чтобы проанализировать, что происходит при нажатии кнопки [submit] (отправить), я кликаю по ней правой кнопкой мыши и выбираю [Inspect] (Проверить).
Picture19.png

Проверка HTML-элемента

Откроется вкладка [Elements] (Элементы) в инструментах разработчика. HTML-код кнопок ничего не показывает, так как нет свойства onclick. Но при выборе вкладки [Event Listeners] (Прослушиватели событий) появляется информация, что прослушиватель событий был зарегистрирован на вызов функции отправки в строке 3 файла secure.js.
Picture20.png

Вкладка [Event Listeners] (Прослушиватели событий) в инструментах разработчика

Я снова переключаюсь на вкладку [Sources] (Источники), щелкнув ссылку secure.js:3. Для анализа функции я создаю точку останова в строке 4, щелкнув номер строки слева. Точка останова отображается на панели инструментов справа.
Picture21.png

Точки останова на вкладке [Sources] (Источники)

Отладчик

Я снова нажимаю [Submit] (Отправить). Выполнение JavaScript останавливается на строке 4, и запускается отладчик. Я прохожу через определенные операторы JavaScript, нажимая F9 или щелкая соответствующий символ в верхней части панели инструментов.
Picture22.png

Инструменты отладчика для пошагового выполнения программы

После выполнения каждого оператора, содержащего переменную, его содержимое отображается рядом и выделяется желтым цветом. Регулярное выражение по всей видимости удаляет все специальные символы, кроме ._-.
Picture23.png

Значения переменных отображаются внутри отладчика

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

Picture24.png

Изменение значения на 127.0.0.1;id

При нажатии кнопки воспроизведения возобновляется выполнение кода в обычном режиме и появляется сообщение, что инъекция команды прошла успешно.
Picture25.png

Результаты команды id

Общие советы

Еще три замечания

Чтобы обнаружить точку входа, иногда полезно установить точку останова на Any XHR/fetch на панели инструментов. Таким образом, выполнение приостанавливается перед отправкой XHR, и отображаются все вычисляемые переменные предыдущих операторов.
Picture26.png

Добавьте точку останова XHR/выборки

Конечно, в JavaScript имеется также и ключ подписи. Его можно извлечь и использовать в Burp Suite для расчета правильной подписи. Я расскажу об этом в следующей статье.

DOM Invader — это расширение встроенного браузера Burp Suite, которое помогает тестировать DOM XSS. Как обычно, довольно хороши для этого ресурсы PortSwigger. Если вы хотите, чтобы я также рассказал об этом в блоге, скажите об этом.

Обфускация и деобфускация

Прежде чем мы перейдем к методам запутывания (обфускации), давайте рассмотрим понятия минификации и преобразования в удобочитаемый формат.

Минификация

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

Цель минификации — повысить эффективность передачи JavaScript. Ниже приведен (несколько искусственный) пример минимизированного кода, созданного с помощью UglifyJS, но он проясняет суть.
Picture27.png

Пример минификации

Размер файла заметно уменьшился примерно на 40 % за счет выполнения следующих преобразований:

  • были удалены пробелы, новые строки, комментарии и ненужные фигурные скобки;
  • локальная переменная input была переименована в e;
  • оператор if-else был заменен так называемым тернарным оператором (x?a:b).
Кстати, минифицированные исходники часто можно узнать по расширению .min.js.

Преобразование в удобочитаемый формат

Преобразование в удобочитаемый формат — это процесс преобразования минимизированного кода назад в удобочитаемый для человека формат. Имеется несколько способов преобразования минимизированного кода в удобочитаемый формат.

  • Отладчик Chromium выполняет структурную распечатку программы JavaScript по умолчанию. Он добавляет отступы и разрывы строк в коде.
  • Для преобразования локальных файлов JavaScript в удобочитаемый формат, можно использовать такой инструмент, как js-beautify:
# Install
$ pip install jsbeautifier
# Usage
$ js-beautify main.min.js > main.js


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

Карты исходного кода

Одним из способов полного восстановления минимизированного кода являются так называемые карты исходного кода. Файлы карты исходного кода просто содержат объект JSON, как показано ниже. Важнейшей частью является строка mappings (преобразования данных), которая содержит сопоставление между исходным и минифицированным кодом.
Picture28.png

Пример карт исходного хода

Сопоставления закодированы в VLQ Base64, и я не буду подробно обсуждать это здесь.1

Для визуализации сопоставления можно использовать визуализаторы карт исходного кода. Например, 23->2:5 означает, что слово в столбце 23 e= сопоставляется со словом в строке 2, столбце 5 input=.
Picture29.png

Пример визуализации карты исходного кода

Иногда карты исходного кода поставляются вместе с минимизированным кодом. Это делается путем добавления комментария к минифицированному JavaScript, который содержит местоположение карты исходного кода. Карта исходного кода будет загружаться только при открытии инструментов разработчика.
Picture30.png

Ссылка на карту исходного кода из минифицированного JavaScript

Минификация и анализ

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

При выполнении динамического анализа отсутствие описательных идентификаторов компенсируется отладчиком в инструментах разработчика Chromium. Рядом с операторами JavaScript отображаются значения переменных после их выполнения.

Обфускация

Обфускация JavaScript предназначена для предотвращения его анализа, статического или динамического, с сохранением его функционала. Для этого допускается даже потеря производительности кода. Часто обфускаторы помещают слова и символы из кода в массив. Во время выполнения восстановление исходного кода осуществляется за счет повторяющихся ссылок на этот массив. На изображении показан пример так называемой упаковки.
Picture31.png

Пример упакованного массива в обфусцированном коде

Как видите, произошло следующее преобразование:

document.getElementById("input").value ➡️ document[d(0x0)](d(0x1))[d(0x2)];

d — это функция, которая ссылается на массив, определенный в функции a. 0x0 — первая строка, 0x1 — вторая и т. д.

Некоторые другие преобразования, которые часто используются:

  • переименование идентификаторов в шестнадцатеричные строки;
  • код с функциями самозащиты, который взламывается при преобразовании в удобочитаемый формат;
  • внедрение мертвого кода, увеличивающего размер файла.
Некоторые инструменты даже предлагают защиту от отладки, что делает «практически невозможным использование функции отладки инструментов разработчика»2. В Интернете имеется несколько обфускаторов. Самый популярный — javascript-obfuscator. Его можно запускать из командной строки или онлайн.

Имеется еще больше обфускаторов в различных вариантах. Один из наиболее совершенных — JSFuck. Он использует только эти шесть символов: ![]+()
Picture32.png

Пример обфускации с помощью JSFuck



Деобфускация

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

Кроме того, деобфускаторы часто полагаются на следующие методы:

  • распаковка массивов;
  • замена прокси-функций;
  • удаление мертвых ветвей кода;
  • переименование идентификаторов.
В Интернете также можно найти большое число самых разных деобфускаторов. Пример: javascript-deobfuscator. Его можно использовать из командной строки или онлайн. Также мне нравится, и как работает JSNice. Он переименовывает идентификаторы на основании статистической модели, что часто помогает лучше понять код.

Обфускация и анализ

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

Преобразование простого кода в обфусцированный и деобфусцированный

Практическое занятие: анализ обфусцированного кода

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

Прежде чем перейти к рассмотрению инструментов динамического анализа, я бы хотел сказать пару слов о соответствующем образе мышления. Динамический анализ может быть трудоемким процессом. Иногда полезно вспомнить, что любая функция на стороне клиента, которую вы ищете, определенно присутствует в клиентском коде. Например, если веб-приложение подписывает данные перед их отправкой на сервер, ключ подписи ДОЛЖЕН находиться в клиентском коде. Такое отношение помогает вам быть упорнее.

Пример приложения

Приложение, которое я собираюсь проанализировать — это служба проверки связи, описанная выше, но с обфусцированным JavaScript. Пользователь отправляет хост и получает реакцию на команду Ping от сервера. Перехват запроса в Burp Suite показывает, что приложение отправляет JWT, содержащий хост (IP-адрес) целевого сервера.
Picture34.png

Запрос отправляется в Burp Suite

Так как JWT подписаны, невозможно манипулировать значением хоста без аннулирования подписи. Это не позволяет мне работать с запросом в Burp Suite, что необходимо для пентеста. Но поскольку JWT рассчитывается с помощью JavaScript, ключ должен находиться где-то в клиентском коде. Цель заключается в том, чтобы извлечь секретный ключ для проведения надлежащего анализа конечной точки.

Поиск точки входа

Во-первых, я хочу узнать, что происходит при нажатии кнопки [Submit] (Отправить). Для этого я открываю вкладку [Elements] (Элементы) инструментов разработчика и проверяю прослушиватели событий. Это выявляет прослушиватель событий щелчка.
Picture35.png

Прослушиватель событий в инструментах разработчика Chromium

При нажатии на ссылку открывается вкладка [Sources] (Источники) и подсвечивается положение функции прослушивателя. Посмотрите, как инструменты разработчика Chromium преобразовали обфусцированный код, который на самом деле написан внутри одной строки.
Picture36.png

Преобразованный минимизированный JavaScript с выделенной функцией отправки

Я ставлю точку останова на первый оператор в функции отправки и нажимаю кнопку [Submit] (Отправить). Точка останова запускает и приостанавливает выполнение. Это означает, что я нашел правильное положение и теперь могу осуществлять пошаговое выполнение.
Picture37.png

Выполнение приостановлено на точке останова

Определение значение в зоне интереса

В какой-то момент готовый к отправке JWT должен храниться внутри переменной JavaScript. Я использую кнопку [Step over next function call] (Перейти к вызову следующей функции) в инструментах разработчика, чтобы найти это положение. Выделенные ниже строки содержат заголовок JWT, полезные данные и, наконец, закодированный JWT, начиная со строки параметров ey[...]
Picture38.png

Определение места расчета JWT

Подписание должно происходить где-то в строке, в которой рассчитывается JWT. Я ставлю точку останова в этой строке и удаляю старую, чтобы пропустить ненужные операторы во время анализа. После этого я снова нажимаю кнопку [Submit] (Отправить).
Picture39.png

Новая точка останова при расчете JWT

Функция распаковки

Теперь начинается сложная часть. Чтобы продолжить выполнение, я использую кнопку [Step] (Шаг). Это приводит к функции распаковки обфусцированного кода, где символы и строки JavaScript загружаются из массива.
Picture40.png

Функция 0x246a() содержит упакованный массив

Теперь переменная ссылается на весь массив. Это можно увидеть в разделе «Scope» инструментов разработчика справа.
Picture41.png

Упакованный массив, отображаемый в разделе «Scope»

Этот шаг показывает, что будет возвращена 20-я запись массива.
Picture42.png

Функция распаковки делает свою работу

Действительно, возвращается значение JWS с индексом 20.

Picture43.png


Возвращаемая переменная 0x51f223 содержит значение JWS

Восстановление вызова подписания

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

  • sign
  • HS256
6465623837323564656533323462383134656535386133626434353431373866
Picture44.png

Последний шаг распаковки перед расчетом 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")
Picture45.png

Происхождение обфусцированных переменных и вызовов функций

Переход к следующему шагу подтверждает это. В разделе «scope» отображаются те переменные, которые были переданы в функцию.
Picture46.png

Область действия функции KJUR.jws.JWS.sign()

На данный момент самый простой способ — выяснить, взята ли эта функция из библиотеки JavaScript и что именно делает JWS.sign. Быстрый поиск в Google показывает, что библиотека действительно используется. Она называется jsrsasign пользователем GitHub kjur.

В документации показано, что метод генерирует подпись JWS с указанным ключом. Ключ в положении 4 вызова функции в данном случае является шестнадцатеричным значением. Это означает, что длинное целое число, показанное выше — это ключ.
Picture47.png

Документация jsrsasign

Расшифровка ключа

Я декодирую шестнадцатеричный ключ и кодирую результат в base64, так как это то, что мне нужно для JWT Editor расширения Burp Suite.
Picture48.png

Использование xxd и base64 для декодирования и кодирования

Подписание управляемых JWT

В расширении JWT Editor я создаю новый симметричный ключ, поскольку HS256 — это метод симметричного подписания. Я вставляю ключ из предыдущего шага в поле "k".
Picture49.png

Создание нового симметричного ключа в JWT Editor

Я отправляю запрос из истории прокси-сервера Burp Suite в Repeater и переключаюсь на вкладку JSON Web Token. Здесь я заменяю хост 127.0.0.1 на infosec.exchange и нажимаю кнопку [Sign] (Подписать).
Picture50.png

Использование ключа для подписи обрабатываемого запроса

Я отправляю запрос с обновленной подписью на сервер и получаю результаты проверки связи для infosec.exchange. Это означает, что расчет подписи выполнен успешно, и теперь я могу использовать значение по мере необходимости.
 
Использование ключа для подписи обрабатываемого запроса

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

Picture52.png

Результат проверки связи для infosec.exchange в ответе сервера

Используя расширение Burp Suite CSTC, я теперь могу автоматизировать процесс подписания и выполнять активное сканирование цели, которое обнаруживает уязвимость инъекции команд. Но это содержание другой статьи в блоге.

Локальные переопределения

Локальные переопределения — это способ сохранить изменения в JavaScript при загрузке страницы. В определенных ситуациях это может пригодиться для анализа.

Типичные варианты использования:

  • изменение последовательности выполнения кода для обхода защиты на стороне клиента;
  • добавление операторов console.log в содержимое переменных журнала;
  • рефакторинг кода для преобразования в удобочитаемый формат и деобфускации.
Временные изменения в инструментах разработчика

Инструменты разработчика Chromium позволяют изменять содержимое источников прямо на вкладке [Sources] (Источники). В демонстрационных целях я буду использовать описанную выше службу проверки связи. Строки 5 и 6 реализуют фильтр на стороне клиента, который удаляет специальные символы из значения хоста.
Picture53.png

Фильтр на стороне клиента, выделенный на вкладке [Sources] (Источники).

Чтобы обойти фильтр, я закомментировал обе строки и сохранил изменения с помощью Ctrl+S. Знак предупреждения на верхней панели указывает, что эти изменения не являются постоянными.
Picture54.png

Временные изменения кода

Как видите, фильтр был успешно обойден и можно внедрять команды из самого веб-приложения.
Picture55.png

Отключенный фильтр позволяет внедрять команды

Однако после перезагрузки страницы изменения исчезают. Реальные веб-приложения часто перезагружают страницы при навигации по ним. Для эффективного пентестирования приложения рекомендуется сделать изменения в JavaScript постоянными.

Постоянные изменения в инструментах разработчика

Для этой цели в Chromium и Chrome есть так называемая функция локальных переопределений. Это позволяет переопределять ресурсы страницы файлами из локальной папки. Настройте параметры на вкладке [Overrides] (Переопределения) на вкладке [Sources] (Источники).
Picture56.png

Вкладка [Overrides] (Переопределения) на вкладке [Sources] (Источники).

Предоставив Chromium доступ к локальной папке, вы можете сохранять источники, используя кнопку [Save for overrides] (Сохранить для переопределений) в контекстном меню.
Picture57.png

Сохранить файл для переопределения

В результате будет добавлена папка для пути в папку переопределений и источник будет сохранен в ней. Теперь изменения в этом файле являются постоянными, на что указывает фиолетовая точка.
Picture58.png

Постоянные изменения кода

Постоянные изменения в Burp Suite

К сожалению, эту функцию нельзя использовать во встроенном браузере Burp Suite, так как вкладка [Overrides] (Переопределения) остается пустой. Причина этого, по-видимому, в том, что браузер запускается с флагом --disable-file-system. Запрос Функции был отправлен 2,5 года назад.

Но, к счастью, для этого есть BApp! Расширение HTTP Mock позволяет определить ответы, которые будут возвращены вместо фактических. Это работает следующим образом.

Отправьте пару запрос-ответ на расширение.
Picture59.png

Контекстное меню для редактора запросов для имитации HTTP-ответа

Внесите изменения и не забудьте нажать кнопку [Save] (Сохранить).
Picture60.png

Расширение HTTP Mock для Burp Suite

Перезагрузите страницу во встроенном браузере, и вы увидите, что изменения приняты.
Picture61.png

Постоянные изменения кода передаются в браузер

Обход кодовой защиты

Обфускаторы JavaScript предлагают функции для защиты обфусцированного кода от деобфускации и анализа. Например, obfuscator.io предусматривает следующие меры защиты.

  • Функции самозащиты: взламывает код при преобразовании в удобочитаемый формат или деобфускации.
  • Защита от отладки: запрещает использование оператора отладчика.
  • Отключение консольного вывода.
В этом разделе показано, как обойти все эти меры, и даются советы, как это сделать при использовании других обфускаторов.

Настройка

Один за другим я буду применять меры защиты к параметрам по умолчанию obfuscator.io и анализировать результат. На скриншоте ниже показаны варианты обфускации.
Picture62.png

Опции obfuscator.io

Мое демонстрационное приложение использует JavaScript для записи ввода текстового поля на страницу следующим образом:

function process() {
let input = document.getElementById("input").value;
document.getElementById("output").innerHTML = input;
}


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

Функция самозащиты

Этот параметр делает выходной код устойчивым к форматированию и переименованию переменных. Если кто-то попытается использовать средство форматирования исходного кода в соответствии со структурой языка JavaScript для обфусцированного кода, код больше не будет работать, что затруднит его понимание и изменение.

https://obfuscator.io

При посещении страницы в Chromium он зависает, не давая возможности вообще проанализировать происходящее.
Picture63.png

Страница Chromium не отвечает

Следующая попытка: Firefox

Результат другой и более многообещающий: отображаются сообщение об ошибке Uncaught InternalError: too much recursion и трассировка стека, показывающая причину проблемы.
Picture64.png

Сообщение об ошибке в консоли JavaScript Firefox

Строка 41 содержит регулярное выражение (((.+)+)+)+$, которое показывает катастрофический возврат.
Picture65.png

Обфусцированный код с выделенным регулярным выражением

Как видите, функция _0x3b1622 вызывается в строке 43 перед фактическим кодом JavaScript веб-страницы, который начинается в строке 44. Таким образом, обход так же прост, как закомментировать строку 43 (или строку 41 соответственно). В результате страница загружается в Chromium и по-прежнему работает.
Picture66.png

Обфусцированный код с выделенным регулярным выражением

Такой подход можно использовать для любого кода с функциями самозащиты obfuscator.io. Шаблон функции самозащиты определен здесь. В нем жестко закодировано регулярное выражение.
Picture67.png

Шаблон кода с функциями самозащиты от obfuscator.io

В результате обход может быть таким же простым, как удаление этого определенного регулярного выражения, например, с помощью sed:

sed -i 's/(((.+)+)+)+\$//g' file.js

Защита от отладки

Этот параметр делает практически невозможным использование функции отладчика инструментов разработчика.

https://obfuscator.io

Переход на страницу в Chromium при открытых инструментах разработчика сразу приостанавливает выполнение и запускает отладчик. Кроме того, кажется, что вы застряли в анонимной функции, которая снова и снова вызывает отладчик. С закрытыми инструментами разработчика все работает как обычно.
Picture68.png

Защита от отладки запускает отладчик в анонимных функциях

Самый простой способ избавиться от этого — предотвратить вызов анонимной функции, в которой вы застряли. Для этого я рекомендую использовать Стек вызовов инструментов разработчика. Просматривайте конкретные вызовы, пока не найдете тот, который легко закомментировать, ничего не нарушая. Чем дальше вы продвигаетесь по стеку, тем больше вероятность того, что функция подходит для работы веб-страницы. Таким образом, оставайтесь как можно ближе к началу.
Picture69.png

Стек вызовов, предоставляющий ценную информацию

В данном случае я решил удалить оператор else в строке 54, так как он содержит вызов _0x3c9b48(0), что в конечном итоге привело к бесконечным вызовам отладчика. И действительно, теперь страница загружается в Chromium без запуска отладчика.
Picture70.png

Отключение защиты от отладки путем удаления оператора else.

Отключение консольного вывода

Отключает использование console.log, console.info, console.error, console.warn, console.debug, console.exception и console.trace, заменяя их пустыми функциями. Это затрудняет использование отладчика.

https://obfuscator.io

В демонстрационных целях я добавил оператор console.log в функцию процесса, которая должна выводить значение поля ввода на консоль. Однако консольный вывод остается пустым из-за отключенного консольного вывода obfuscator.io.
Picture71.png

Оператор console.log добавлен в обфусцированный код

Имеется несколько способов обойти эту меру.

Удалите символ console из упакованного массива. Это работает, потому что обфусцированный код должен переопределить объект консоли, для чего ему нужно его имя. Тем не менее, упакованный массив можно замаскировать (обфусцировать) таким образом, что это будет просто невозможно.
Picture72.png

Упакованный массив содержит символ console

Шаблон функции для отключения вывода консоли приведен здесь. Он содержит название функций консоли, которые необходимо переопределить: log, warn, info, error, exception, table, trace.
Picture73.png

Методы, которые нужно переопределить из obfuscator.io

Найдите и удалите этот массив из обфусцированного кода. В зависимости от степени обфускации его может быть нелегко идентифицировать
Picture74.png

Обфусцированный массив методов, которые нужно переопределить

Используйте функцию консоли из встроенного iframe. Это, безусловно, самый надежный метод. Он работает путем добавления iframe к текущему документу через JavaScript. У этого iframe есть собственная внутренняя модель DOM, доступ к которой можно получить через свойство contentWindow. Следующий код показывает это:

var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.contentWindow.console.log("This is logged!")


Я добавил этот код в обфусцированный код.
Picture75.png

Приведенный выше код добавлен к обфусцированному коду

Конечно, вы также можете заменить метод console документа на метод из iframe следующим образом:

console = iframe.contentWindow.console;

На скриншоте, приведенном ниже, показано, что консольный вывод снова работает.
Picture76.png

Повторно включенный консольный вывод

Общая рекомендация

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

Всегда пытайтесь запускать и анализировать JavaScript в разных браузерах или движках JavaScript. Как видно на примере выше, результаты могут незначительно отличаться. В этом случае Chromium завис, а Firefox выдал ошибку с важной частью информации: номер строки с рекурсией. Вы также можете использовать такие сервисы, как jsconsole.com.

Напомним, что любая защита на стороне клиента также может быть удалена. В конце концов, это самое интересное во взломе JavaScript.

Завершение


На этом я заканчиваю свою статью «Анализ JavaScript для пентестеров». Это было полезно для вас?

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


Подробнее об этом можно прочитать в разделе Chrome Введение в карты исходного кода JavaScript. ↩︎

Это рассматривается в https://obfuscator.io/ в debugProtection ↩︎
 
Отличная статья ! Было полезно почитать, но некоторые вещи пока кажутся непонятными, но логику улавливаю. А есть возможность, сделать простенький гайд о том, как проанализировать сайт и собрать токен, который генерируется в js?(На примере каких нибудь соц. сетей при запросе на сайт, когда проверяешь статистику об аккаунте. Часто выдает токен, который создается в js) Иногда сталкивался с таким, но и получается иногда это выполнить, но может у вас есть, что предложить в другом формате с лучшими навыками.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх