Исследование безопасности десктопных приложений на основе Electron
Electron — фреймворк с открытым исходном кодом для создания кросс-платформенных десктопных приложений с помощью JavaScript, HTML и CSS. Это крутая технология, но с ней связаны многие ИБ-риски.
В статье я разберу основы безопасной работы с этим фреймворком и расскажу:
- как анализировать структуру десктоп-приложений на Electron и находить в них уязвимости;
- какие распространенные ошибки допускают при работе с фреймворком и насколько он защищен.
Начнем с инструментов и методов, с помощью которых я провожу анализ кода приложений. Затем продемонстрирую конкретные примеры эксплуатации уязвимостей на примере специальных приложений-мишеней: DVEA, Electro-xxs и Notable.
Привет, Хабр! Меня зовут Александр, я работаю специалистом по информационной безопасности. Эта статья подготовлена на основе доклада, который я делал на программе стажировки, организованной компанией Бастион.
Atom, Visual Studio Code, WordPress Desktop, Github Desktop, Basecamp3, Mattermost — лишь несколько примеров приложений, созданных с использованием Electron. В основе фреймворка лежит сочетание Chromium (обеспечивает отображение контента с использованием HTML5, CSS3 и JavaScript) и Node.js (предоставляет среду выполнения для JavaScript, открывая разработчикам доступ к серверным функциям). Это дает фреймворку целый ряд преимуществ.
В то же время Electron наследует ряд проблем безопасности 1.97 256GB, характерных для обычных веб-приложений, и при этом обладает куда большими возможностями. Фреймворк имеет доступ к файловой системе, пользовательским скриптам и т. д. Взять, например, приложение, в котором есть несколько вкладок или окон. Возможен сценарий, когда вредоносный код выполняется на одной вкладке или в одном окне и приводит к негативным последствиям в других вкладках или приложениях.
Для решения этой проблемы в Electron используется мультипроцессорная модель. Она включает в себя два основных типа процессов:
- Main Process, управляющий основными аспектами приложения;
- Renderer Process, отвечающий за рендеринг содержимого в окнах приложения и выполнение кода JavaScript.
Разделение процессов обеспечивает изоляцию и независимость между различными компонентами приложения, что повышает уровень безопасности и предотвращает нежелательное взаимодействие между вкладками. По сути, main process может создавать renderer process, обеспечивая тем самым контролируемую и безопасную среду выполнения для каждого окна.
Примерно так выглядит связь между renderer process и окном браузера в Electron:
- Renderer Process загружает и отображает страницу или HTML-контент в окне приложения;
- В окне браузера отображается визуальный интерфейс приложения (например, если стартовой страницей является index.html, в окне браузера будет выводиться содержимое из файла index.html).
Для гибкой настройки Electron существуют webPreferences — параметры, связанные с определением различных характеристик веб-страницы.
Пример webPreferences. nodeIntegration: true — страница имеет доступ к Node.js API
Среди них есть параметры, которые дают новые возможности для renderer process. Так, для nodeIntegration дает разрешение на использование Node.js внутри процесса отображения, а contextIsolation отвечает за управление прямым доступом к API Node.js из кода страницы. Посмотрите, как Electro-XSS меняет поведение в зависимости от настроек webPreferences.
Активный доступ к Node.js API, который дает nodeIntegration в сочетании с XSS в renderer process, открывает возможность для Remote Code Execution дистанционного выполнения кода, несмотря на мультипроцессорную модель Electron.
nodeIntegration + XSS в renderer process = RCE
Для автоматизированного анализа кода и поиска подобных ошибок в Electron широко используют популярные статические и динамические анализаторы.
Однако существуют и менее известные, но более узкоспециализированные утилиты, как консольные, так и снабженные графическим интерфейсом.
Electronegativity
Этот инструмент предназначен для выявления неправильных конфигураций и антипаттернов безопасности в приложениях.
Мажорные релизы Electronegativity можно установить из хранилища пакетов NPM при помощи консольной команды: $ npm install @doyensec/electronegativity -g
Затем, чтобы проверить приложение на на наличие уязвимостей, введите: $ npm audit
Пример запуска сканирования: electronegativity -i DVEA
Electronegativity представляет структуру кода в древовидном формате и анализирует его иерархическую структуру, проверяет структуру веб-страницы (DOM) и формирует подробный отчет.
Кроме того, стоит обратить внимание на ElectroNG — специализированный сканер безопасности, разработанный создателями Electronegativity для проведения автоматизированных проверок.
NodeJsScan
Это бесплатный и открытый сканер статического кода. Он способен выявлять небезопасные паттерны в коде Node.js и HTML-шаблонах и предоставляет удобный веб-интерфейс для управления обнаруженными проблемами безопасности. Одно из его преимуществ — легкая интеграция с различными CI/CD конвейерами: Github Actions, Gitlab CI/CD, Travis и т. д.
Запуск инструмента выглядит следующим образом:
- Загрузка последней версии образа Nodejsscan из Docker Hub.
docker pull opensecurity/nodejsscan:latest
- Запуск контейнера (порт 9090) для веб-интерфейса Nodejsscan.
docker run -it -p 9090:9090 opensecurity/nodejsscan:latest
- Переход на страницу Nodejsscan.
http://localhost:9090
Протестируем Nodejsscan на примере уязвимого приложения Damn Vulnerable ElectronJS App (DVEA).
Сканер выдает развернутый отчет:
Выбрав интересующую нас уязвимость через Hide Code, можно получить детальную информацию, об ее особенностях:
Njsscan (CLI)
Этот статический анализатор обнаруживает уязвимости, при помощи сопоставления паттернов из библиотеки libsast.
Для установки введите команду: pip install njsscan
Njsscan использует семантический поиск шаблонов кода с помощью инструмента semgrep.
Команды semgrep запускаются с различными конфигурациями (rulesets), которые определяют, какие конкретные проверки и тесты будут выполнены.
$ semgrep --config=r/javascript.browser.security.insufficient-postmessage-origin-validation.insufficient-postmessage-origin-validation --config=r/javascript.lang.security.audit.incomplete-sanitization.incomplete-sanitization --config=r/javascript.browser.security.dom-based-xss.dom-based-xss --config=r/typescript.react.security.audit.react-css-injection.react-css-injection --config=r/typescript.react.security.audit.react-href-var.react-href-var --config=p/owasp-top-ten __CODE_DIRECTORY__
Snyk CLI
Snyk представляет собой облачный инструмент безопасности, предназначенный для поиска потенциальных уязвимостей в коде приложения в режиме реального времени. Умеет он и обнаруживать ошибки в образах контейнеров Kubernetes, а также небезопасные конфигурации Terraform и Kubernetes.
Snyk CLI устанавливается при помощи npm: sudo npm install snyk@latest -g
Проверка работоспособности Snyk CLI выполняется через команду: snyk --help
Чтобы начать использовать Snyk CLI, необходимо произвести аутентификацию с помощью: snyk auth
Затем можно начинать сканировать проект:
Например, перейдем по ссылке со snapshot:
Результат сканирования немного предсказуем:
Попробуем другое демонстрационное приложение — Electro-XSS:
Здесь Snyk также находит уязвимости:
Общий порядок действий при тестировании десктопных Electron-приложений разбивается на три этапа.
Попробуем подробнее рассмотреть эту последовательность.
Декомпиляция
Ручное тестирование приложения начинается с распаковки файлов в формате .asar. Их можно обнаружить в каталоге resources. Если у вас установлен NodeJS, используйте команду npm, чтобы убедиться в корректности работы менеджера пакетов Node.
Для этого используйте команду npm install --engine-strict asar, которая отвечает за установку приложения asar при помощи менеджера пакетов npm.
Команда npm install -g asar отвечает за установку asar глобально.
С помощью команды asar extract app.asar app извлеките исходный код из архива.
Модификация и перекомпиляция
Если нужно модифицировать исходный код и применить изменения в момент запуска приложения, повторно упакуйте файл app.asar.
Я модифицировал функцию createWindow, добавив новую отладочную строку, как показано на скрине.
Настройка каналов связи
Чтобы осуществить захват трафика и затем модифицировать какой-либо запрос в процессе работы приложения, потребуется настроить прокси через Burp Suite. Здесь можно действовать двумя путями.
Проксирование на уровне ОС
Такой подход полезен, когда вам нужно видеть HTTP/HTTPS-запросы, выдаваемые целевым приложением. В этом случае добавляется электронный аргумент командной строки. Если мы вернемся к функции createWindow, то увидим уже два примера:
createWindow — функция, показывающая commandLine.appendSwitch. В 58 строке находится уже добавленный переключатель: app.commandLine.appendSwitch('proxyserver','127.0.0.1:8080');
Далее папка app снова упаковывается в файл app.asar, и Electron загружает chromium (не забудьте установить сертификат CA certificate Burp, как показано здесь).
После настройки прокси вы увидите, как весь HTTP/HTTPS-трафик переходит в burp, и сможете заняться запросами на стороне сервера.
Проксирование на уровне приложения
Реализация этого подхода будет зависеть от архитектуры конкретного приложения и используемых в нем технологий. Рассмотрим процесс настройки прокси-сервера для перехвата трафика Windows-машины в Burp Suite.
Первым делом необходимо настроить системный прокси с помощью команды: netsh winhttp set proxy proxy-server="http=127.0.0.1:8080;https=127.0.0.1:8080" bypass-list="*.local"
Так вы установите системный прокси-сервер для HTTP и HTTPS трафика на адрес 127.0.0.1 (localhost) и порт 8080. Файлы *.local будут обходить прокси. Чтобы убедиться, что настройки были применены, выполните: netsh winhttp show proxy
В выводе вы должны увидеть настроенные параметры прокси.
Затем убедитесь, что прокси-сервер в Burp Suite запущен и настроен на прослушивание того же порта (в данном случае, 8080).
Экспортируйте сертификат Burp и откройте Консоль с помощью команды mmc.
Перейдите в меню «Файл→Добавить/удалить оснастку…», а затем — в раздел «Сертификат → Добавить → Учетная запись компьютера → Далее».
Выберите «Локальный компьютер», нажмите кнопку «Готово» и подтвердите выбор сертификата:
Перейдите в раздел «Сертификаты → Все задачи → Импорт». Выберите путь к импортированному сертификату Burp и нажмите кнопку «Далее».
Выберите «Разместить все сертификаты в следующих хранилищах» и нажмите кнопку «Далее». Нажмите «Finish».
Наконец, убедитесь, что сертификат BurpSuite был установлен.
И запустите перехват трафика.
При разработке и тестировании приложений следует учитывать возможность возникновения целой россыпи различных проблем.
Рассмотрим некоторые из них подробнее
Cross Site Scripting
Этот вид атак представляет собой внедрение вредоносного кода в приложение. Cross-Site Scripting XSS-атаки на приложения на основе Electron в целом напоминают атаки на веб-приложения. Однако есть некоторые отличия в механизмах передачи вредоносного кода. Обозначим их.
- В десктоп-приложениях обычно отсутствует отдельная строка ввода URL, как в веб-браузерах. Как описано в разделе методологии, можно использовать DevTools Desktop-приложения для взаимодействия с API.
- Electron позволяет приложениям взаимодействовать с локальной файловой системой пользователя. Если входные данные из файловой системы недостаточно защищены, это может привести к возникновению патчей XSS.
- Работа с протоколами: Electron поддерживает основные протоколы (например, HTTP, HTTPS), а также позволяет создавать собственные протоколы. Если эти протоколы не защищены должным образом, они также могут стать причиной возникновения XSS-уязвимостей.
Эти атаки могут привести к краже данных пользователей, сеансов аутентификации, перенаправлению на вредоносные сайты, внедрению вредоносных скриптов и другим нежелательным последствиям. Предотвращение XSS включает в себя фильтрацию и экранирование ввода данных, использование безопасных API и регулярные обновления политик безопасности.
Запуск сценария эксплуатации процесса в поле ввода выглядит следующим образом:
Этот код будет выполняться в desktop-приложении пользователя, если оно некорректно обрабатывает или экранирует поступающие данные. В этом конкретном примере тег используется для вставки изображения, и при возникновении ошибки загрузки изображения (атрибут onerror) выполняется JavaScript-код alert (1), который отобразит диалоговое окно с сообщением »1».
XSS to RCE
Примеры payload’ов для RCE в данном контексте:
- Для Linux и MacOS
- Для Windows
В обоих случаях используется тег и событие onerror, которое вызывает require ('child_process').execSync () для выполнения команд в системном шелле. Подобные команды могут быть использованы злоумышленниками для выполнения произвольного кода на стороне клиента и создают серьезную угрозу для безопасности приложения.
Для защиты рекомендуется правильно настраивать политику безопасности контента (Content Security Policy, CSP) и ограничивать использование nodeIntegration.
Вот как это выглядит на практике:
- Пример payload для Windows:
- Примеры payload’ов для Linux и MacOS:
```
Эксплуатация XSS и RCE на примере DVEA
Damn Vulnerable ElectronJS App — уязвимое приложение ElectronJS, разработанное специально для обучения ИБ-специалистов и разработчиков. Оно спроектировано так, чтобы демонстрировать основные уязвимости, характерные для среды ElectronJS.
Чтобы запустить DVEA, используйте следующую команду:
cd DVEA
npm i
electron .
Запуск с использованием npm:
npm install yarn
npm start
DVEA позволяет увидеть примеры уязвимостей, включая XSS. Например, внутри приложения можно использовать такой payload:
Этот код выведет окно с предупреждением, содержащим произвольный текст. Тут же, если ввести правильную полезную нагрузку в renderer process DVEA, можно реализовать RCE-атаку:
Демонстрация атак на примере Electro-XSS
Как следует из названия, это приложение-мишень предназначено уже для демонстрации уязвимостей на основе XSS.
Посмотрим на сценарий эксплуатации XSS, когда нужно внедрить код JavaScript в поле ввода страницы. Такой код может использоваться для атак на пользователя, например, в целях кражи сессионных данных.
Запустим сценарий эксплуатации процесса: XSS:
RCE:
При выполнении этого JavaScript-кода запустится калькулятор GNOME. Такие сценарии могут использоваться для атак на приложение, особенно в случае, если параметр nodeIntegration установлен в true, что позволяет выполнять код Node.js в процессе отображения.
RCE на примере Notable
XSS-атаки позволяют злоумышленникам внедрять и запускать свой код на стороне клиента. Например, в случае использования Node.js в приложении, построенном на Electron, может возникнуть угроза удаленного выполнения кода RCE (Remote Code Execution). Пример XSS-пейлоада, с помощью которого можно внедрить в уязвимое приложение команду на запуск калькулятора:
При отсутствии фильтрации или ограничений ввода злоумышленник может внедрять произвольные команды в различные поля приложения. Например, использование команды ls позволяет просмотреть содержимое текущего каталога и получить дополнительную информацию о файловой системе.
Чтобы увидеть, как это происходит, на практике, запустим приложение Notable version 1.5.0 и введем полезную нагрузку.
После выхода из режима редактирования автоматически запустится команда полезной нагрузки. Останется только произвести эксплуатацию уязвимости:
Анализ логики работы десктопного приложения перед началом тестирования играет ключевую роль в оценке его безопасности.
Reversing Electron Application — это метод, направленный на получение доступа к исходному коду приложения, который дает возможность более глубокого и детального анализа. Построение методологии пентеста в значительной степени зависит от этого этапа, поскольку доступ к исходному коду позволяет определить поток работы приложения, выявить логические цепочки и понять его поведение и внутреннее устройство.
В конечном счете это ускоряет и облегчает поиск и анализ потенциальных уязвимостей.
Методология пентеста на примере Notable
Для того, чтобы попрактиковаться и провести детальное исследование, используем notable версии 1.5.0 и скачаем файл Notable-1.5.0.dmg и Notable-1.8.4.AppImage. Не забудьте дать права на использование этого приложения:
Смонтируем образ приложения (во временную папку):
И откроем эту папку:
В этой папке уже можно видеть файлы и ресурсы приложения.
Далее необходимо скопировать файл app.asar в рабочую папку и изменить формат файла .dmg (для этой цели подойдет приложение HFSleuth).
Нам необходимо запустить эту программу с указанием двоичного файла Notable-1.5.0.dmg.
Найдем файл Notable.app.
И попробуем войти в него.
Загрузим файл app.asar (архив, содержащий исходный код для electron-приложения), записав его во временную папку.
Command: asar extract app.asar dest-folder
Для того чтобы провести реверс-инжиниринг файла необходимо:
Файл будет извлечен в обратном порядке. Далее потребуется запустить Sublime-text, чтобы просмотреть содержимое файлов:
Если требуется найти уязвимые места в зависимостях, пригодится npm audit.
Чтобы решить эту проблему, необходимо отключить блокировку пакетов:
- package.json содержит различные метаданные, а также сведения о зависимостях и версиях;
- The main process выступает в качестве точки входа в приложение;
- Index.html — это фронтенд приложения.
Методология пентеста на примере Electro-XSS
Теперь посмотрим на работу по анализу уязвимостей на примере Electro-XSS.
Развернув приложение, запустим Sublime-text, чтобы просмотреть его файлы:
Здесь можно увидеть точку входа в приложение:
В файле package.json можно увидеть поле main и найти точку входа.
Мы уже знаем, что параметры webPreferences отвечают за настройку веб-страницы. Когда параметр nodeIntegration установлен в true, Node.js API становится доступен. Если в приложении существует уязвимость типа Cross-Site Scripting (XSS), успешная атака позволит злоумышленнику выполнить произвольный код на стороне клиента, осуществив Remote Code Execution (RCE). В таких случаях рекомендуется использовать contextIsolation вместе с preload для предварительной загрузки скриптов:
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: true,
preload: path.join(__dirname, 'preload.js'),
},
});
Для примера попробуем изменить параметр nodeIntegration на false:
npm install
npm run eletro-xss
С помощью Ctrl+Shift+I откроем Devtools:
И попробуем через консоль получить доступ к API интерфейсу узлов.
Поскольку получить доступ не удается, попробуем отключить приложение и использовать nodeIntegration, , а затем отправим команду на перезапуск. Наша задача здесь — вызвать API interfaces node, что позволит удаленно выполнять любой код (RCE):
Попытка успешна. Смысл такой интеграции узлов состоит в том, чтобы вызвать API interfaces node для эксплуатации RCE.
Для обеспечения безопасности Electron-приложений стоит строго следовать рекомендациям по безопасности из репозитория Electron на github.
Ключевые шаги включают:
- значение nodeIntegration в false и contextIsolation в true;
- валидацию ввода в полях форм;
- контроль над процессом создания новых окон (или их блокировка, если они не нужны);
- использование актуальной версии Electron.js;
- проверку всех параметров безопасности, установленных по умолчанию (особенно при использовании сторонних фреймворков);
- контроль интеграций с Node.js.
Дополнительной мерой может служить блокировка лишних привилегий, предоставленных приложению. Это уменьшит поверхность атаки и предотвратит нежелательный доступ к системным ресурсам.