Содержание статьи
- Введение, или как плохо жить без песочницы
- Песочницы и особенности их реализации
- Нюанс №1, или одна песочница на всех
- Нюанс №2, или недоизоляция
- Нюанс №3, или «а давайте сделаем еще один велосипед, это так интересно»
- Нюанс №4, или почему плохо пилить сук, на котором сидишь
- Немного о встроенных механизмах безопасности Windows и изоляции процессов с их помощью
- Как правильно запускать процессы под другим пользователем
- Коварный десктоп и изоляция
- Идеальный мир, или какой должна быть песочница
- Ну и в конце
Как изолировать подозрительные процессы в ОС Windows и не сломать саму ОС? Как создать надежную и совместимую с Windows программную песочницу без аппаратной виртуализации и перехватов функций ядра, но с использованием документированных встроенных механизмов безопасности ОС? Мы расскажем о наиболее часто встречающихся проблемах, с которыми сталкиваются разработчики (а в итоге — потребители) программных песочниц. Ну и разумеется, предложим свое решение :).
Введение, или как плохо жить без песочницы
Среди профессионалов есть несколько аксиом, о которых они не любят говорить. А что говорить об аксиомах? Они есть и есть. Вроде всем понятно, как дважды два. Например, одна из них — сигнатурные антивирусы не защищают. Ну то есть не защищают, и все тут. Сказано-пересказано об этом масса всего и много-много раз. С примерами, красивыми презентациями, танцами и плясками. Да и эпидемии всякой гадости типа Ransomware служат одним из доказательств неэффективности сигнатурных и эвристических технологий. Всякого рода крипторы и обфускаторы успешно решают задачу защиты уже давно известной малвари от обнаружения, и в течение некоторого времени эта малварь не детектируется антивирусами. Этого времени вполне достаточно, чтобы кому-то стало плохо, а кому-то хорошо.
То есть речь даже не о 0day: можно взять старую известную бородатую малварь, поморфить ее, убрать поведенческие сигнатуры (работа на пару дней для лентяя) и юзать ее снова, а потом еще раз, и еще, пока не надоест или пока не посадят. При этом люди, которые продали лекарство от того, чтобы это самое «плохо» никогда не наступало, вроде как и ни при чем; они с серьезными лицами публикуют какой-нибудь бюллетень и рассказывают о гигиене в интернете, забывая при этом сказать, что если эту самую гигиену соблюдать полностью — то антивирусы, тем более платные, практически и не нужны.
Песочницы и особенности их реализации
Итак, антивирусы не спасают, а иногда ломают то, что уже есть. «Давайте подойдем к защите с другой стороны и будем изолировать процессы друг от друга», — сказал кто-то бесконечно умный. Действительно, здорово, когда подозрительные процессы выполняются в некоторой изолированной среде, называемой песочницей. Выполняемая в песочнице малварь не может покинуть ее пределов и навредить всей системе. Этот могло бы быть решением, однако в существующих реализациях песочниц есть нюансы...
Далее мы как раз и будем обсуждать все тонкости построения песочниц, знание которых тебе обязательно пригодится, когда понадобится выбрать средство изоляции процессов или HIPS (Host-based Intrusion Prevention System — система предотвращения вторжений для рабочих станций).
Нюанс №1, или одна песочница на всех
Большинство песочниц на самом деле не обеспечивают изоляцию процессов. По правде говоря, в большинстве реализаций защищаемая система делится на две части — доверенную и недоверенную. В доверенной части выполняются обычные процессы, а в недоверенной — изолируемые. То есть все изолируемые процессы выполняются в одной песочнице, имеют доступ друг к другу и к ресурсам друг друга, используют тот же реестр и ту же файловую систему.
Таким образом, малварь может закрепиться в самой песочнице и стартовать эпизодически с одним из изолируемых приложений (или с несколькими изолируемыми приложениями, или с любым из них). При этом часто песочницы не логируют действия изолируемых процессов. Действия, на которые ругаются HIPS, в песочницах вполне себе проходят без малейших реакций с поправкой на изоляцию, что не очень хорошо.
Как проверить, что изоляция устроена именно таким образом? Очень просто! Запусти два приложения в песочнице. Например, notepad.exe и wordpad.exe. Создай с помощью notepad.exe текстовый файл 1.txt.
Разумеется, данный файл будет сохранен не на рабочем столе, а в «виртуальной» директории. Попробуй открыть его Wordpad’ом (рис. 3).
Итак, созданный одним изолированным приложением файл можно открыть с помощью другого изолированного приложения. Скажем прямо, изоляция уже не очень. Но может, по крайней мере от записи будет какая-то защита? Меняем содержимое (рис. 4).
И сохраняем. А теперь попробуем открыть файл 1.txt с помощью notepad.exe. Конечно же, запустим notepad.exe в песочнице (рис. 5).
И вот то, о чем мы говорили. Два изолированных приложения не изолированы друг от друга. Получается, что такую изоляцию делали не совсем понятно для чего. Даже шифровальщик, не получив доступ к локальным папкам на компьютере, может пошифровать все в завиртуализованной директории, а если «повезет», то и на сетевых ресурсах, так как настройки для песочницы одни для всех изолируемых приложений.
Нюанс №2, или недоизоляция
Да, изолируемые процессы не могут достучаться до доверенной части системы... но в большинстве реализаций только на запись. То есть они могут читать отовсюду практически без ограничений и часто имеют доступ к сети. Это сделано, видимо, для большей совместимости, но это нельзя назвать изоляцией.
Попробуй провести простенький опыт с песочницей на свой выбор. Создай директорию на жестком диске. Скажем, такую: E:\Photos
. Помести в нее, например, фотографию (рис. 6).
Запусти Internet Explorer в песочнице и попробуй отправить данное изображение, скажем, на rghost.
Ну и как? Получилось? Если опыт удался, то это не очень хорошо. Еще хуже, если в песочнице отсутствует возможность задать директории, к которым изолированные приложения не будут иметь доступ. И совсем нехорошо, если изолированные приложения могут читать данные из директорий текущего пользователя.
Виртуализация файловой системы и реестра в большинстве реализаций построена по принципу «копирование по требованию». То есть если файл нужно просто прочитать, то он читается из исходной директории при отсутствии аналога в виртуальной директории. Если такой же файл присутствует в виртуальной директории, то изолируемое приложение будет работать с ним. То же можно сказать про виртуальный реестр. Ну и понятно, что при попытке записать файл по реальному пути он запишется в виртуальную файловую систему. Почти всегда.
Таким образом, если малварь «изолирована» в такой песочнице, то она сможет иметь полный доступ ко всем другим «изолируемым» процессам, практически ко всем данным в системе на чтение и к завиртуализованным (сохраняемым изолированными приложениями) данным (которые часто общие для всех изолируемых приложений) на запись.
Нюанс №3, или «а давайте сделаем еще один велосипед, это так интересно»
Нас всегда удивляла поразительная способность некоторых антивирусных компаний решать задачи в лоб, не задаваясь вопросом, а надо ли решать эту задачу вообще, надо ли решать ее именно так, надо ли ломать что-то одно, чтобы построить что-то другое. Создается такое ощущение, что все решения банально и бездумно слизываются друг у друга. Впрочем, деньги есть — ума не надо, как говаривал кто-то из классиков. Если запустить утилиту Wincheck от Red Plait, то можно увидеть, как именно реализованы механизмы изоляции, и заодно ответить на вопрос, почему качество изоляции под 64-битные версии Windows заметно слабее, чем под 32-битные.
Wincheck — шустрая, достаточно надежная и бесплатная утилита, которая позволяет искать rootkit’ы в системе. Cкачиваем Wincheck, запускаем и можем увидеть что-то типа этого:
SDT entry C (ZwAdjustPrivilegesToken) hooked 872DB50E \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 16 (ZwAlpcConnectPort) hooked 872DB91A \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 17 (ZwAlpcCreatePort) hooked 872DB8C8 \SystemRoot\system32\DRIVERS\dirtyguard.sys!
...
...
...
SDT entry 170 (ZwSystemDebugControl) hooked 872DABCC \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 172 (ZwTerminateProcess) hooked 872DA534 \SystemRoot\system32\DRIVERS\dirtyguard.sys!
SDT entry 173 (ZwTerminateThread) hooked 872DA302 \SystemRoot\system32\DRIVERS\dirtyguard.sys!
Большинство реализаций песочниц используют rootkit-технологии, перехватывая солидную часть функций ядра. В этом листинге приведены банальные хуки записей таблицы дескрипторов системных сервисов (SSDT), которая, кстати, охраняется PatchGuard (но об этом позже). И так делают многие.
К слову, тот факт, что у некоторых производителей антивирусных решений из флагманских продуктов пропала песочница, можно объяснить тем, что PatchGuard начал контролировать Shadow SSDT, которая отвечает за работу с окнами и располагается в win32k.sys
. Ведь сбежать из песочницы можно и через SetWindowsHookEx
, например. Чтобы перекрыть этот способ, надо перехватить NtUserSetWindowsHookEx
в Shadow SSDT.
Глядим на результат Wincheck и видим:
Shadow SDT: 90C16000, limit 339
win32k_sdt[14] (NtGdiBitBlt) hooked, addr 872DF818 \SystemRoot\system32\DRIVERS\dirtyguard.sys
win32k_sdt[125] (NtGdiDeleteObjectApp) hooked, addr 872E0160 \SystemRoot\system32\DRIVERS\dirtyguard.sys
...
...
...
win32k_sdt[588] (NtUserSetWinEventHook) hooked, addr 872E046A \SystemRoot\system32\DRIVERS\dirtyguard.sys
win32k_sdt[595] (NtUserSystemParametersInfo) hooked, addr 872DE56A \SystemRoot\system32\DRIVERS\dirtyguard.sys
Есть масса статей на тему того, что это rootkit-технологии и это небезопасно, но кого это когда-то останавливало... Своим путем, через тернии и жесткий хукинг ядра, разработчики песочниц пытаются реализовать механизмы контроля доступа Windows заново! Сейчас они находятся где-то на уровне Windows 2000, сильно опережая ее по забагованности. И это логично, так как несколько даже очень талантливых разработчиков не смогут учесть всех нюансов тех вещей, которые создавались годами и тестировались миллионами пользователей.
При этом практически никто не использует штатные отлаженные встроенные механизмы безопасности самой ОС. Вместо этого их пытаются обойти и предлагают взамен непонятный суррогат, что вряд ли поможет против серьезной угрозы, а иногда может и навредить, так как поверхность атаки увеличивается.
В середине 2013 года мы протестировали способ побега из песочниц на основе инжекта в explorer.exe. Это был PowerLoader. Подавляющее большинство песочниц не устояло. В некоторых потребовалось сделать банальные вещи, вроде снять юзермодные хуки или вызвать напрямую ядро, и вопрос был решен. Хорошо сопротивлялись только две, одна из которых на текущий момент не развивается, а другую убрали из флагманского продукта (антивирус). Это говорит о том, что способ, основанный на перехватах, защищает только от известных разработчикам способов побега из песочниц!
Хотя не будем столь критичны. Есть решения, которые научились использовать механизмы безопасности ОС, не ломая их. Итак, если ты не видишь хуков ядра, то есть вероятность, что производители более грамотно подошли к вопросу изоляции.
Давай попробуем проверить. Для начала скачаем бесплатную утилиту Process Hacker. Запустим изолированный Internet Explorer, после чего — Process Hacker (рис. 9).
На скриншоте мы видим выделенные темным процессы. Эти процессы запущены из-под анонимного пользователя (NT AUTHORITY\ANONYMOUS LOGON) и имеют уровень целостности (Integrity Level) Untrusted, а их Logon SID — S-1-5-7. Да-да, речь идет о Sandboxie. Только они так делают. Подробнее об уровнях целостности и механизмах безопасности Windows мы настоятельно рекомендуем почитать в книге Марка Руссиновича и Дэвида Соломона Windows Internals. Сейчас же скажем, что процесс запущен таким образом, что не имеет прав доступа практически ни к чему, и, если бы не одно но, процесс был бы выгружен из памяти операционной системой, а говоря проще, свалился бы. Тем не менее он не падает, а работает. И изолирован, без хуков ядра и без аппаратной виртуализации (тоже хуки, но об этом позже). Как же так происходит? Посмотрим повнимательнее.
Раз уж мы, помимо всего прочего, делаем некий мини-обзор хакерских утилит, то нельзя пройти мимо GMER. Скачаем, запустим и поглядим. Хуков ядра нет. Уже хорошо. А что же есть? Как процесс, который запущен так, что не должен работать, работает? И здесь на помощь приходят те же хуки, но в пользовательском режиме. Типа таких:
.text C:\Program Files\Internet Explorer\iexplore.exe[2852] ntdll.dll!NtAdjustPrivilegesToken 7565268 10 Bytes JMP 73AAB7E0 C:\Program Files\Sandboxie\SbieDll.dll
То есть в каждом приложении, запущенном в песочнице, огромная масса перехватов (сплайсинг) на уровне пользователя, которые через механизмы межпроцессного взаимодействия проксируют запросы в центр принятия решений. По всей видимости, в данном случае это сервис SbieSvc (впрочем, там и в драйвере логики немало). А данный центр, сверяясь с базой, принимает какие-то разрешительные или запретительные решения. При этом, если избавиться от хуков, приложение перестанет работать, так как ему запрещено практически все. Иными словами, данное решение реализует в чистом виде принцип «Что не разрешено, то запрещено».
Однако в таком подходе есть ряд минусов, связанных, во-первых, с быстродействием (каждый перехват должен быть обработан), во-вторых, с совместимостью с антивирусными решениями (они не очень любят хуки, даже пользовательского режима), в-третьих, с совместимостью с программами, защищенными протекторами (там своя песня, и они тоже очень не любят хуки, иногда предательски от них избавляясь). Ну и «портянка» разрешительных правил весьма велика, что влечет за собой проблемы совместимости и быстродействия.
Нюанс №4, или почему плохо пилить сук, на котором сидишь
В большинстве реализаций изолируемое приложение запускается под тем же пользователем, что и неизолируемое. Для реализации изоляции недостаточно фильтровать работу с файловой системой и реестром. Надо отслеживать открытие handle’ов, работу с COM-объектами, с ALPC в целом, с окнами и много чего еще, что штатно умеет делать Windows, в частности SRM (Security Reference Monitor). По своей старой традиции производители защитных технологий решают эти задачи хуками. Мы уже подробно писали об этом выше.
При этом другая защитная технология, PatchGuard, с хуками как раз борется. Впрочем, производители песочниц уже нашли решение в своем стиле, задействовав механизмы виртуализации. Там же по ссылке можно увидеть, почему это решение, мягко говоря, не очень хорошее. И именно по этой причине разработчикам зачастую непросто реализовать свою изоляцию для каждого из процессов.
Очень часто маркетологи антивирусных компаний (и не только) подают нам использование аппаратной виртуализации как некую фичу, которая кардинально увеличит безопасность. Это сейчас тренд такой, активно подогреваемый нами, системными программистами. Мы же любим большие, сложные, наукоемкие и многобюджетные проекты! Но на самом деле в большинстве случаев уход на уровень гипервизора — это побег от PatchGuard, не более того. Возможность ставить хуки безнаказанно. Во всяком случае, пока :). Ну и да, это во всех отношениях дорого, начиная с требования к железу и заканчивая дороговизной разработки. Гипервизоры писать недешево. За все платит потребитель, разумеется.
Немного о встроенных механизмах безопасности Windows и изоляции процессов с их помощью
Как мы заметили выше, на рынке крайне мало решений, которые в принципе используют встроенные механизмы безопасности Windows. То, о котором мы говорили, использует их, но весьма оригинальным способом — только для запрещения. Этот путь чреват рядом проблем.
А как еще можно использовать эти механизмы для изоляции процессов? На самом деле можно запускать приложения под разными пользователями, и большую часть работы сделают встроенные механизмы разделения доступа Windows. Хотя, конечно же, мини-фильтры на файловую систему и реестр все-таки нужны.
Используя штатные механизмы дискреционного контроля доступа (манипуляции с DACL объектов), мы можем регулировать права доступа субъектов (наших изолированных программ, запущенных под новыми пользователями) к объектам (файловой системе, реестру). Помимо этого, мы можем совершенно документированно и безопасно регулировать права доступа к станции, права доступа к рабочему столу, системные привилегии и уровень доверия.
Однако и здесь есть ряд нюансов, если их не учитывать, можно свести безопасность решения к нулю либо понизить его стабильность.
Как правильно запускать процессы под другим пользователем
Есть такой факт: начиная с Windows Vista механизм Run As «сломан». И хоть это не совсем новость, но пока не все об этом знают. Проведем простой эксперимент. Запустим Process Explorer. Запустим notepad.exe. Кликнем два раза в окне Process Explorer на notepad.exe. Зайдем во вкладку Security и выберем там вкладку Permissions (рис. 10).
Мы видим некоторую неизвестную учетную запись, имеющую SID S-1-5-5-0-80701. Это Logon SID. Разрешения процесса для Logon SID: Query limited information, Query information, Read memory, Terminate, Synchronize, Read. Разрешения токена для Logon SID: Assign as primary token, Duplicate, Impersonate, Query, Query source and Read. Разрешения потока для Logon SID: Query limited information, Query information, Get context, Synchronize and Read.
Все это означает, что если процесс стартует под другим пользователем посредством Run As (который запускает процесс с текущим Logon SID), то в большинстве случаев поток данного процесса сможет получить токен другого процесса (целевого, запущенного под исходным пользователем), имперсонировать контекст безопасности целевого процесса и получить все права, которыми обладает целевой процесс, что будет означать выход из изоляции.
Таким образом, при анализе средства изоляции процессов нужно обращать внимание на Logon SID, с которым был запущен изолируемый процесс. Даже если процесс запущен под другим пользователем, то в случае совпадения Logon SID побег из песочницы может быть возможен.
Существует всего три документированных способа запустить процесс от имени другого пользователя:
- CreateProcessWithLogonW. На вход принимает логин и пароль, создает процесс с тем же Logon SID. Не подходит.
- CreateProcessAsUser. Создает процесс с тем же Logon SID. Не подходит.
- CreateProcessWithTokenW. Единственная подходящая функция.
Именно CreateProcessWithTokenW необходимо использовать для запуска приложений под другим пользователем. Это единственно верное решение.
Коварный десктоп и изоляция
Начиная с Vista в ОС Windows появился защитный механизм UIPI (User Interface Privilege Isolation). Этот механизм регулирует обмен сообщениями между приложениями с разными уровнями доверия (Integrity levels). А теперь вспомним старушку Windows XP.
На рис. 11 видно, что если приложение запущено под другим пользователем, то SetWindowsHookEx не отработает для приложений, запущенных, например, под текущим пользователем. Изоляция работает! Но в Windows XP :(. Поглядим, что происходит в более молодых ОС семейства Windows (рис. 12).
А здесь ситуация гораздо более интересная. Если UIPI включен (переменная gbEnforceUIPI равна TRUE), то проверка пользователей, под которыми запущены приложения, на эквивалентность опускается!
Иными словами, если ты запускаешь приложение под другим пользователем в Windows Vista и выше на том же рабочем столе, что и приложения других пользователей, и при этом для приложения выставлено право доступа рабочего стола DESKTOP_HOOKCONTROL
, то побег из такой песочницы становится тривиальным. Право доступа рабочего стола DESKTOP_HOOKCONTROL
разрешает перехватывать любые оконные сообщения и позволяет устанавливать оконные перехваты. Если отключить DESKTOP_HOOKCONTROL
, то большой ряд приложений перестанет работать. Например, рантайм MS Visual Studio требует, чтобы присутствовало это право доступа. В противном случае все приложения, скомпилированные с помощью MS Visual Studio, не будут функционировать.
Решений проблемы не особо много:
- Использовать хуки (проблема с PatchGuard и все те прелести, о которых мы говорили выше).
- Запускать приложение с выключенным UIPI, который прибит гвоздями к UAC (User Account Control), что автоматически означает отключение UAC — одного из важнейших механизмов безопасности Windows (путь не для тех, у кого руки растут из плеч). И да, мы видели такое в одном не особо популярном решении по изоляции процессов :).
- Запускать изолируемые приложения на отдельных рабочих столах (путь для просветленных системных программистов, познавших Истину).
То есть если песочница вообще не использует отдельных рабочих столов в процессе изоляции приложений, то дело нехорошо попахивает одним из двух первых пунктов.
Но и с отдельными рабочими столами поджидают подводные камни. Оказывается, не все приложения любят запускаться на другом рабочем столе. Некоторые особо вредные принципиально проверяют, на каком рабочем столе они запускаются. Например, Firefox может некорректно обновляться на отдельном рабочем столе, завершаясь сразу после запуска. Связано это с тем, что процесс обновления запускается явно на winsta0/default, что приводит к невозможности запуска из-за отсутствия прав доступа. Если это запатчить или запускать на текущем рабочем столе, все будет работать. К счастью, для Firefox и его форков можно отключить DESKTOP_HOOKCONTROL
, что не делает необходимым запуск на отдельном рабочем столе.
Yandex-браузер (версия 13.12.1599.12987) содержит библиотеку C:\Users\xxx\AppData\Local\Yandex\YandexBrowser\Application\30.0.1599.12987\browser.dll
. В ней где-то рядом с ChromeBrowserMainParts::PreMainMessageLoopRunImpl
есть процедура, в которой вызывается GetStartupInfo
, из нее берется lpDesktop
(в ней обычно WinStation\Desktop
), через поиск "\" вырезается имя рабочего стола и сравнивается с именем рабочего стола, полученного через OpenInputDesktop
, то есть, по сути, с текущим активным рабочим столом. Если не совпадает, инициализация считается проваленной, браузер завершается.
Отвлечемся от главной темы и заметим, что так сравнивать некорректно. Нельзя проверять только имя рабочего стола, надо учитывать и рабочую станцию. Более того, реальный рабочий стол может быть и вовсе не тот, что вернула GetStartupInfo
. По понятной причине такой подход не дает стартовать Yandex-браузер на отдельном рабочем столе. Специалисты Yandex сказали, что это какая-то хитрая защита от прокликивания. Менять ее особо не стали с тех пор. Возможно, немного усовершенствовали. К счастью, Yandex-браузер не требует права доступа DESKTOP_HOOKCONTROL
, что позволяет запускать его изолированно на текущем рабочем столе.
Таким образом, чтобы песочница была более универсальной, в ней попросту необходим некоторый набор правил для наиболее популярных приложений — уменьшить вовлеченность пользователя в волшебный мир настройки продукта под себя. Как пример, Firefox и Thunderbird приблизительно после версии 37 не будут корректно отображать окна, если в разрешениях рабочего стола не окажется DESKTOP_JOURNALRECORD
и DESKTOP_JOURNALPLAYBACK
. О таких мелочах песочнице желательно бы знать заранее.
Идеальный мир, или какой должна быть песочница
В результате своих исследований мы поняли, что надежная песочница должна обладать следующими свойствами:
- С максимальной эффективностью использовать встроенные механизмы безопасности платформы, на которой она работает.
- Уметь изолировать каждый процесс в отдельно настраиваемой изолированной среде.
- Давать возможность регулировать доступ для каждого из процессов к объектам файловой системы и реестра, сменным носителям информации, сети, задавать привилегии и уровень доверия. То есть она должна быть максимально настраиваемой под конкретное поле применения.
- Быть относительно легкой в использовании.
- Журналировать свою работу. Желательно делать это в журнале ОС.
- Не использовать хуков ядра ни при каких обстоятельствах.
- Не ломать существующие механизмы безопасности ОС и вообще саму ОС.
- Иметь постоянно расширяемую базу первоначальных настроек, чтобы как можно меньше вовлекать пользователя в процесс администрирования.
Все эти требования мы попытались воплотить в жизнь в тулзе под именем ReHIPS. Она абсолютно бесплатна, и поэтому можешь вполне легально ее скачать и поиграться с ней. Кто знает, быть может, удача улыбнется тебе и именно ты сможешь найти способ удрать из нашей песочницы. В любом случае ты всегда можешь прислать нам отзыв о своих впечатлениях от использования продукта, за что мы будем очень благодарны.
Ну и в конце
Задача изоляции процессов только на первый взгляд кажется не такой уж сложной, но на деле тут скрывается большое число подводных камней. Каждый вендор решает эту задачу по-своему, кто-то более успешно, кто-то менее. Теперь ты представляешь все слабые и проблемные места современных песочниц, поэтому, когда понадобится запустить потенциально опасное приложение, сможешь подобрать соответствующий инструмент для его изоляции от основной системы.