Уязвимость VSCode и «объективный» взгляд на Git
Привет, Хабр!
Меня зовут Владислав Кормишкин, я аналитик-исследователь угроз кибербезопасности в компании R-Vision. Сегодня я хотел бы рассказать вам об одном из самых популярных редакторов кода — VSCode, и о том, как злоумышленники могут использовать его в своих целях. На сегодняшний день известно о 229 миллионах установок расширений через встроенный магазин, содержащих вредоносный код. Уязвимости в редакторах кода используются давно, в том числе и APT (Advanced Persistent Threat) с применением социальной инженерии, как это было и с Visual Studio.
В этой статье мы рассмотрим архитектурные особенности VSCode, связанные с безопасностью исполнения кода. Мы также проанализируем уязвимость CVE-2023-46944 и объясним, почему, несмотря на то, что разработчик GitKraken пропатчил её в 2023 году, она всё ещё может быть потенциально опасной из-за особенностей работы с Git. Кроме того, мы расскажем, как именно эта уязвимость была пропатчена, и предложим правило для её обнаружения с использованием языка VRL и плагина R-Object.
Функции безопасности VSCode
Visual Studio Code (VSCode) — мощная и востребованная интегрированная среда разработки (IDE), созданная на базе платформы Electron. Она объединяет в себе возможности Node.js и браузера Chromium, предоставляя разработчикам дополнительную гибкость. VSCode наследует модель процессов от Electron, разделяя функциональность на различные процессы для повышения безопасности. Таким образом, процессы рендеринга, выполняющиеся во встроенном браузере Chromium, имеют ограниченные привилегии, в то время как основной процесс, работающий на Node.js, обладает повышенными привилегиями и может напрямую взаимодействовать с операционной системой. Несмотря на обширный открытый исходный код (почти 800 тысяч строк кода), официальные сборки от Microsoft содержат проприетарные компоненты.
VSCode Workspace Trust — это функция безопасности, представленная в Visual Studio Code в 2021 году. Она помогает предотвращать запуск вредоносного кода и возможных уязвимостей из недоверенной среды. Недоверенной средой считается директория, которая была открыта впервые или для нее не был определен тип доверия. Выбрать его можно во всплывающем окне:
В доверенной рабочей области доступны все функции и расширения приложения, а в ненадёжной — некоторые функции и расширения могут быть недоступны. Ограничения касаются расширений, которые взаимодействуют с файловой системой, запускают сценарии или имеют доступ к сетевым ресурсам. Для предотвращения непреднамеренного выполнения потенциально опасного кода в ненадежных рабочих областях будут ограничены функции отладки, запуска задач и команды терминала. Подразумевается, что, отметив область как недоверенную, можно будет проверить весь находящийся в ней код и после проверки установить значение на доверенное.
Звучит немного идеалистично, не правда ли?
Давайте подробнее рассмотрим, как с этим связана наша уязвимость.
Уязвимость в GitLens (CVE-2023-46944)
В 2023 году была обнаружена уязвимость CVE-2023-46944, в которой используется расширение GitLens для запуска вредоносного кода из недоверенной среды. Но как же так происходит? В документации мы видим, что расширения VSCode могут иметь три типа работы со средами — true
, false
и limited
:
capabilities:
untrustedWorkspaces:
{ supported: true } |
{ supported: false, description: string } |
{ supported: 'limited', description: string, restrictedConfigurations?: string[] }
Первые два типа настроек либо позволяют использовать расширения в недоверенных средах, либо блокируют их работу. Третий тип настроек предоставляет возможность ограниченного использования расширений. Задается этот тип в файле проекта package.json
. При этом определенные параметры могут автоматически ограничиваться с помощью свойства RestrictedConfigurations
, где в массиве можно указать Setting ID, и этот параметр будет ограничен в недоверенной среде:
В расширении GitLens определение таких свойств отсутствует, но на скриншоте видно, что поддерживается тип limited, а это значит, что часть функционала будет работать в недоверенных средах:
В версии GitLens до 14.0.0 отсутствовала проверка на нахождение в недоверенной среде. При открытии репозитория расширение обращалось к Git для инициализации проверки, что позволяло запускать вредоносный код. Начиная с версии Git 2.37.0 была добавлена интересная функция core.fsmonitor
. Если мы обратимся к документации с помощью Web Archive и посмотрим описание функции за 2021 год, то увидим следующее:
Подразумевается, что в переменной fsmonitor
могут содержаться команды.
Для чего же нужна функция core.fsmonitor
и как ее использовать? Для этого давайте разберемся, что из себя представляет Git config, так как в нем содержится функция fsmonitor
.
Git config — это файл конфигурации для Git, в котором хранятся настройки, специфичные для конкретного репозитория. Он создается при инициализации репозитория git init
, его можно редактировать вручную или с помощью команд. Git поддерживает конфигурацию из трех источников, и каждый уровень заменяет предыдущий: системный /etc/gitconfig
, глобальный ~/.gitconfig
и локальный репозиторий .git/config
. На этом этапе нам интересен файл .git/config
.
Функция fsmonitor
позволяет при вызовах git status
, git add
, git diff
и других команд, для которых требуется информация о файлах в локальном репозитории, использовать индексацию, чтобы не перебирать все файлы. Это значительно ускоряет работу Git с большими репозиториями. В fsmonitor
можно как записать значение типа boolean
для включения или отключения функции, так и указать исполняемый файл. Для запуска произвольного файла (калькулятора в примере ниже), .git/config
файл должен выглядеть следующим образом:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
fsmonitor = calc.exe
Посмотрим, как же работает наша уязвимость.
Эксплуатация уязвимости
Давайте проверим, что произойдет, если в версии VSCode 19.0 запустить нелегитимный код, используя расширение GitLens 13.6.0, как это описано в PoC для уязвимости CVE-2023-46944:
Возникает вопрос, как же происходит запуск команды из fsmonitor
? При открытии рабочей области с файлами и папками в VSCode, Git пытается определить, является ли эта область репозиторием. И если он обнаруживает в ней знакомые папки и файлы, такие как .git
, HEAD
или config
, то пытается ее инициализировать ее и проверить, есть ли изменения в открытом репозитории. То есть при открытии директории в VSCode (в нашем случае нажатие на файл README.md
) происходит запуск команд по синхронизации с удаленным репозиторием.
Посмотрим, как это будет выглядеть в событиях с использованием вредоносных команд, а не просто с запуском калькулятора. Для этого возьмем полезную нагрузку типа Fetch Payload, так как нагрузки, которые не используют команды для запуска, в данном случае не работают. В файле .git/config
это выглядит следующим образом:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
fsmonitor = "powershell -w hidden -nop -e <payload> #
В <payload>
используется запуск утилиты curl
(инструмент командной строки для передачи данных по различным сетевым протоколам).
Анализ событий
При открытии файла происходит выполнение ряда команд по проверке изменений в открытом репозитории.
Проанализируем последовательность событий с помощью R-Vision SIEM:
Здесь нас интересует выделенная команда ls-files -- README.md
, ее выполнение как раз и триггерит вызов функции fsmonitor
. Аналогичные события происходят и в Windows.
Мы видим, что родительским процессом для чтения README.md
от Git является процесс code
:
Событие запуска полезной нагрузки выглядит следующим образом:
В поле spid
(означает родительский процесс) находится значение dpid
(PID текущего процесса) из события запуска Git, значит, запуск /bin/sh
является дочерним процессом от Git. Теперь мы понимаем, что это выглядит как цепочка процессов.
Это уязвимость 2023 года, и можно подумать, что раз в VSCode все расширения по умолчанию обновляются, то и беспокоиться не о чем. Однако это не совсем так. Посмотрим, как обстоят дела с устранением этой уязвимости.
Патчинг
Патч для уязвимости можно посмотреть в этом коммите. Изменения в проекте были сделаны в сборке 14.0.0. Для устранения уязвимости были добавлены дополнительные проверки на нахождение в недоверенной среде — Untrusted Workspace
. Ранее в статье мы выяснили, что в Gitlens не было проверки на тип среды, и расширение запускало Git для инициализации репозитория и выполнения операций индексации файлов.
В файл src/git/errors.ts был добавлен экспортируемый класс WorkspaceUntrustedError
. Он представляет собой тип ошибки, который можно генерировать и перехватывать специально для обработки случаев, когда операции Git не разрешены из-за ненадежной рабочей области:
Класс WorkspaceUntrustedError
также используется в src/env/node/git/git.ts. Этот файл отвечает за взаимодействие расширения GitLens с самим Git. В классе Git
, который используется для обработки и запуска команд, добавлена проверка типа рабочей области. Проверка происходит с помощью API функции VSCode isTrusted
, которая используется в расширениях со значением limited
. В начале статьи мы узнали, что значение limited
позволяет реализовать работу отдельных функций расширения в недоверенной среде (подробнее в документации).
Файл src/git/gitProviderService.ts абстрагирует взаимодействие с Git, предоставляя согласованный API для остального расширения GitLens и взаимодействия с репозиториями, независимо от конкретной реализации или среды. В него были добавлены следующие проверки:
Алгоритм проверок следующий:
Изначально любая рабочая область является недоверенной, только если ранее не было выбрано обратное.
С помощью выражения
!workspace.isTrusted
проводится проверка на принадлежность рабочей области к недоверенной.Функция
onDidGrantWorkspaceTrust
ожидает изменения состояния доверия рабочей области.Если рабочая область является доверенной и содержит папки для инициализации репозитория, то она инициирует обнаружение репозитория.
Если рабочая область является доверенной и в рабочей области нет папок для инициализации репозитория, она возвращает значение
emptyDisposable
, которое не вызывает никаких операций и является заглушкой.
Таким образом, в GitLens добавили проверку на тип активного Workspace. Ну вот, вроде бы все проверки добавлены. Но как обстоят дела сейчас?
Если мы попробуем открыть наш репозиторий с вредоносным кодом в обновленном GitLens, то код и правда не запустится, но только при выборе недоверенной среды. Если мы выберем среду как доверенную, код у нас благополучно исполнится.
Давайте честно, как часто при скачивании репозитория вы проверяете все его файлы на подозрительные строки? Если использовать git clone <repository>
, вы не скачаете .git/config
файл и, скорее всего, не столкнетесь именно с этой уязвимостью. Но что делать, если это будет репозиторий, который был получен из другого источника?
Откроем вредоносный репозиторий в VSCode без плагина GitLens, но в доверенной среде:
У нас снова происходит запуск вредоносного кода:
слева видно, что запуск происходит при выборе недоверенной среды во всплывающем окне;
после открытия файла
README.md
в правом нижнем окне происходит запускsh.exe
и командной строки сpowershell.exe
;в правом верхнем окне происходит получение оболочки reverse-shell, в данном случае
cmd/windows/http/x64/shell_reverse_tcp
.
В продемонстрированном PoC используется аналогичная команда запуска кода с помощью PowerShell
.
Содержимое файла .git/config
:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
fsmonitor = "powershell -w hidden -nop -e <payload> #"
Детектирование
В связи с тем, что для предотвращения этой активности нет какого-то простого решения, можно использовать правила корреляции для Windows и Linux, построенные на детектировании цепочки событий, приведенной ранее.
Построим детект следующей цепочки событий:
Первое событие — запуск VSCode. Он является родительским для процесса Git.
Второе событие — запуск Git с командой перечисления файлов
ls-files
.Третье событие — запуск оболочки
sh
с исполнением команд через-c
.
На примере R-Vision SIEM посмотрим, как наши правила детектирования обнаруживают такую вредоносную активность:
Windows
Правило корреляции для Windows, написанное на языке R-Object в R-Vision SIEM:
Правило корреляции для Windows
id: e65f4f66-e0ee-4eef-8d98-513d184fae84
name: Эксплуатация уязвимости CVE-2023-46944 в расширении GitLens для VSCode на Windows
version: 1.0.0
date: 2024-05-29
author: Vladislav Kormishkin, R-Vision
status: stable
type: correlation_rule
severity: high
description: Правило срабатывает при открытии Git репозитория в VSCode, как в доверенной, так и в недоверенной среде, где в файле .git/config в функции fsmonitor указана вредоносная команда. Данная активность относится к уязвимой версии расширения GitLens до 13.6.0 включительно, ей присвоен номер CVE-2023-46944. Также данная активность может возникнуть при открытии вредоносного Git репозитория в доверенной среде, но с пропатченой версией GitLens или вообще без установленного расширения, в версиях редактора старше 1.63.1.
reference:
- https://packetstormsecurity.com/files/178227/GitLens-Git-Local-Configuration-Execution.html
tags:
- Execution
- attack.T1203
data_source:
- Windows
- Security
- EventID_4688
- Sysmon_Operational
- EventID_1
known_false_positives:
- "Пока неизвестно"
group_by:
- dvchost
filter: !vrl |
.dvendor == "Microsoft" &&
includes(["1", "4688"], .externalId)
aliases:
event_code:
filter: !vrl |
oldFileName = downcase(to_string(.oldFileName) ?? "-")
sproc = downcase(to_string(.sproc) ?? "-")
flag = false
if ends_with(sproc, "\\code.exe") ||
oldFileName == "electron.exe" {
flag = true
}
flag
event_git:
filter: !vrl |
cmd = downcase(to_string(.cmd) ?? "-")
oldFileName = downcase(to_string(.oldFileName) ?? "-")
dproc = downcase(to_string(.dproc) ?? "-")
sproc = downcase(to_string(.sproc) ?? "-")
flag = false
if (ends_with(sproc, "\\git.exe") ||
oldFileName == "git.exe") &&
contains(cmd, "ls-files") {
flag = true
}
flag
event_sh:
filter: !vrl |
cmd = downcase(to_string(.cmd) ?? "-")
oldFileName = downcase(to_string(.oldFileName) ?? "-")
dproc = downcase(to_string(.dproc) ?? "-")
sproc = downcase(to_string(.sproc) ?? "-")
flag = false
if ((ends_with(sproc, "\\git.exe") ||
oldFileName == "git.exe") &&
ends_with(dproc, "\\sh.exe") &&
starts_with(cmd, "sh -c")) {
flag = true
}
flag
select:
alias: event_code
join:
alias: event_git
on:
- eq: {event_code: .dpid, event_git: .spid}
join:
alias: event_sh
on:
- eq: {event_git: .dpid, event_sh: .spid}
ttl: 60
on_correlate: !vrl |
. |= compact({
"rt" : %event_sh.rt,
"dvendor" : %event_sh.dvendor,
"dversion" : %event_sh.dversion,
"dhost" : %event_sh.dhost,
"dproc" : %event_sh.dproc,
"dvchost" : %event_sh.dvchost,
"oldFilePath" : %event_sh.oldFilePath,
"duser" : %event_sh.duser,
"suser" : %event_sh.suser,
"sntdom" : %event_sh.sntdom,
"sproc" : %event_sh.sproc,
"accessMask" : %event_sh.accessMask,
"externalId" : %event_sh.externalId,
"oldFileName" : %event_sh.oldFileName,
"fname": %event_sh.fname,
"dntdom" : %event_sh.dntdom,
"cmd" : %event_sh.cmd,
"sourceServiceName" : %event_sh.sourceServiceName,
})
.msg = "На узле " + (to_string(.dvchost) ?? "-") + " пользователем " + (to_string(.duser) ?? "-") + " домена " + (to_string(.dntdom) ?? "-") + " от процесса Git с родительским процессом VSCode запущена команда " + (to_string(.cmd) ?? "-") + ", данное поведение может указывать на эксплуатацию уязвимости CVE-2023-46944"
Linux
Правило корреляции для Linux, написанное на языке R-Object в R-Vision SIEM:
Правило корреляции для Linux
id: 62697bd1-1731-4437-bc31-572815389f29
name: Эксплуатация уязвимости CVE-2023-46944 в расширении GitLens для VSCode на Linux
version: 1.0.0
date: 2024-05-30
author: Vladislav Kormishkin, R-Vision
status: stable
type: correlation_rule
severity: high
description: Правило срабатывает при открытии Git репозитория в VSCode, как в доверенной, так и в недоверенной среде, где файле .git/config в функции fsmonitor указана вредоносная команда. Данная активность относится к уязвимой версии расширения GitLens до 13.6.0 включительно, ей присвоен номер CVE-2023-46944. Также данная активность может возникнуть при открытии вредоносного Git репозитория в доверенной среде, но с пропатченой версией GitLens или вообще без установленного расширения, в версиях редактора старше 1.63.1.
reference:
- https://packetstormsecurity.com/files/178227/GitLens-Git-Local-Configuration-Execution.html
tags:
- Execution
- attack.T1203
data_source:
- Linux
- Auditd
- Execve
known_false_positives:
- "Пока неизвестно"
group_by:
- dvchost
filter: !vrl |
.dvendor == "Linux" &&
.cs4 == "execve"
aliases:
event_code:
filter: !vrl |
dproc = downcase(to_string(.dproc) ?? "-")
flag = false
if ends_with(dproc, "/code/code") {
flag = true
}
flag
event_git:
filter: !vrl |
cmd = downcase(to_string(.cmd) ?? "-")
dproc = downcase(to_string(.dproc) ?? "-")
flag = false
if ends_with(dproc, "/bin/git") &&
contains(cmd, "ls-files") { #перечисление файлов
flag = true
}
flag
event_sh:
filter: !vrl |
filePath = downcase(to_string(.filePath) ?? "-")
dproc = downcase(to_string(.dproc) ?? "-")
cmd = downcase(to_string(.cmd) ?? "-")
flag = false
if contains(filePath, "/bin/sh") &&
contains(cmd, "-c"){
flag = true
}
flag
select:
alias: event_code
join:
alias: event_git
on:
- eq: {event_code: .spid, event_git: .dvcpid}
join:
alias: event_sh
on:
- eq: {event_git: .spid, event_sh: .dvcpid}
ttl: 60
on_correlate: !vrl |
. |= compact({
"rt" : %event_sh.rt,
"dvendor" : %event_sh.dvendor,
"dversion" : %event_sh.dversion,
"dhost" : %event_sh.dhost,
"dproc" : %event_sh.dproc,
"dvchost" : %event_sh.dvchost,
"oldFilePath" : %event_sh.oldFilePath,
"duser" : %event_sh.duser,
"suser" : %event_sh.suser,
"sntdom" : %event_sh.sntdom,
"sproc" : %event_sh.sproc,
"accessMask" : %event_sh.accessMask,
"externalId" : %event_sh.externalId,
"oldFileName" : %event_sh.oldFileName,
"fname": %event_sh.fname,
"dntdom" : %event_sh.dntdom,
"cmd" : %event_sh.cmd,
"sourceServiceName" : %event_sh.sourceServiceName,
})
.msg = "На узле " + (to_string(.dvchost) ?? "-") + " пользователем " + (to_string(.suser) ?? "-") + " от процесса Git с родительским процессом VSCode запущена команда " + (to_string(.cmd) ?? "-") + ", данное поведение может указывать на эксплуатацию уязвимости CVE-2023-46944"
Заключение
В этой статье мы рассмотрели возможности Workspace Trust и способы предотвращения выполнения потенциально вредоносного кода. Также мы обсудили причины возникновения уязвимости CVE-2023-46944.
Мы выяснили, что если открывать вредоносные репозитории в доверенной среде, то патч не поможет, так как эта активность связана с функционалом интегрированного расширения Git. Для ее отслеживания можно использовать правила корреляции на основе цепочки событий.
К сожалению, в этой ситуации нет простого решения, чтобы полностью обезопасить себя. Однако мы можем выделить основные рекомендации:
проверять все, что вы открываете в редакторе кода, если это было скачано из неизвестного или ненадежного источника;
отключить по умолчанию интеграцию с Git.
Буду рад, если статья оказалась для вас полезной! Задавайте вопросы и пишите комментарии.
Автор: Владислав Кормишкин (@Watislove), аналитик-исследователь угроз кибербезопасности R-Vision.