Содержание статьи
Совсем скоро писатели троянов и прочей нечисти всерьез возьмутся за пингвина и наберутся знаний, чтобы создать по-настоящему опасные вирусы. Даже уже существующих троянов не так просто отличить от легальных программ, зато их всегда можно поместить в карантин.
Многие даже очень грамотные пользователи UNIX-подобных систем считают свои ОС неуязвимыми к разного рода программной заразе. И одна из главных причин такой уверенности — традиция устанавливать софт через проверенные, подписанные сертификатами репозитории, которые по определению не могут содержать зловредных программ.
Вторая причина — разделение прав, благодаря которому малвари довольно трудно навредить системе или прописаться в автозагрузку (пользователи Windows любят наделять себя правами админа, в UNIX это не только не поощряется, но часто и вовсе запрещено).
Можно долго говорить о том, насколько абсурдна такая уверенность; о 95% пользователей Ubuntu, которые спокойно устанавливают любой подсунутый им deb-пакет; о до смешного простом способе перехвата пользовательского пароля с помощью программ типа xspy; об автозагрузке KDE и GNOME, прописаться в которую можно, создав скрипт, состоящий из одной строки... Но это статья о другом. В конце концов, все мы люди, и никто из нас не застрахован от ошибок, к тому же иногда очень трудно отказать себе в запуске программы, скачанной из непроверенного источника.
Руки прочь от файлов
Один из наиболее популярных способов помещения приложения в изолированную среду — это так называемые песочницы, которые в Linux представлены системным вызовом chroot, во FreeBSD — технологией jail (тюрьма), в Solaris — зонами (или, говоря языком маркетологов, контейнерами).
Все это отличные способы изолировать софтину от основной системы так, чтобы она не смогла ей навредить. Однако у всех трех технологий есть несколько недостатков, которые делают их неудобными для использования на домашнем компе. Все они требуют создания полной копии существующей системы, на что тратится время, дисковое пространство (что на сегодняшний день, правда, не так важно) и нервы. Все технологии относительно просты в использовании, но требуют чтения документации и вникания в детали. Кроме всего прочего, голый chroot сам по себе никогда не являлся инструментом обеспечения безопасности, а потому уязвим для многих типов атак.
Программу можно запустить в виртуальной машине, но эта процедура опять же потребует создания выделенной машины, скачивания/ создания образов, перемещения приложения между реальной и виртуальной машиной. В общем, проблем и возни предостаточно.
Нам же требуется простое средство, с помощью которого можно в одну-две команды послать приложение туда, откуда оно уже не выберется. И здесь мы должны задуматься о том, какую функциональность приложения следует ограничивать. Ведь если программе запретить абсолютно все, ее полезность окажется нулевой (более того, мы даже не сможем понять, зловредна ли она вообще).
Чаще всего пользователи беспокоятся за сохранность своих данных, поэтому многим из них становится все равно, какую именно информацию троян сможет отослать в интернет или к какой бот-сети подключить машину.
На первый взгляд кажется, что защитить конфиденциальные данные просто. Для этого можно использовать дополнительную учетную запись, выступающую в роли карантина: создаем пользователя (который не состоит ни в каких системных группах) и запускаем команду от его имени с помощью sudo. Работая с правами этого пользователя, программа не сможет прочитать или модифицировать твои файлы паролей, ключей и так далее, не сможет прописаться в автозагрузку KDE или GNOME. А если установить правильные права доступа на файлы своего основного пользователя (600, например), то и содержимое всех обычных файлов окажется в сохранности.
К сожалению, такой «псевдокарантин» не спасет систему от серьезной заразы, способной использовать локальные уязвимости и архитектурные недостатки Linux, поэтому придется ограничивать программу не только в возможности просматривать локальные файлы пользователей, но и лишать ее других ресурсов (скажем, сетевого обмена данными, возможности запуска других приложений или работы с локальными сервисами). Но обо всем по порядку.
Режем все
Хорошая, эффективная система изоляции приложений должна быть построена на многоуровневой основе, которая могла бы предотвратить компрометацию системы в случае выхода приложения за границу одного из уровней.
Так, например, запустив приложение, подозреваемое в хищении личной информации, под именем непривилегированного пользователя, мы создадим лишь один уровень защиты, и, если зловред найдет способ получить права другого пользователя в системе, то он легко прочтет все его важные данные и отправит их злоумышленнику любым из доступных способов. Но если мы создадим второй уровень защиты в виде запрета на использование определенных сетевых протоколов или вообще отключим все сетевые возможности приложения — кражи не произойдет (если, конечно, программа не сможет обойти и этот запрет). Проблема только в том, что в ядре Linux нет универсального способа ограничения приложений в доступе к ресурсам.
В свое время предпринималось множество попыток создания таких универсальных механизмов и продвижения их в ядро, но в результате пользователи получили не универсальность, а разброс технологий. Так что сегодня Linux насчитывает как минимум четыре «карантинные системы»:
1.Хостовые системы обнаружения вторжений (HIDS), такие как SELinux и AppArmor, позволяют очень тонко контролировать потребности приложений в ресурсах, но они слишком сложны в использовании рядовыми юзерами. Это учли Дэн Уэлш и Эрик Пэрис, создавшие утилиту sandbox, которая использует уже подготовленные жесткие политики для запуска приложений в песочнице.
2.Системный вызов ptrace позволяет отслеживать то, какими системными вызовами пользуется приложение, перехватывать их и блокировать в случае необходимости. Это наиболее популярный и единственный полностью кроссплатформенный способ ограничения, используемый в таких утилитах, как plash, sydbox и systrace, но у него есть минус — излишняя медлительность.
3.Пространства имен. Операционная система Plan 9 оставила свой отпечаток в Linux не только в виде виртуальной файловой системы procfs и кодировки UTF-8, но и в виде системного вызова clone() и так называемых пространств имен. Любой процесс Linux, начиная с ядра 2.4.19, может создать подпроцесс с совершенно иным представлением файловой системы. Если, например, родитель и все остальные процессы видят файловую систему как содержимое раздела /dev/sda1, смонтированного к корню, плюс /dev/sda5, смонтированного к /home, и procfs к /proc, то потомок может видеть вместо этого /dev/sda2, смонтированный к корню, плюс /dev/sda7, смонтированный к /root, и пустой каталог /proc.
Причем то, как будет выглядеть файловая система для потомка, полностью определяет родитель. Механизм пространств имен позволяет поместить процесс в свой обособленный файловый мирок, который не будет виден всем остальным процессам. В ядрах ветки 2.6 к файловому пространству имен были добавлены пространства имен процессов, сети и IPC. Так что теперь процесс может видеть не только другую ФС, но и другой набор системных процессов, сетевые интерфейсы (с собственными настройками маршрутизации и файрвола) и очереди IPC. На пространствах имен основана система LXC и простой, но удобный скрипт, написанный Стефаном Грабером (stgraber.org).
4.Режим seccomp позволяет полностью запереть приложение в самом себе, так что при попытке использовать любой системный вызов, кроме exit(), а также read() и write() в отношении уже открытых файловых дескрипторов, оно будет уничтожено. Для нас в таком жестком ограничении нет ничего полезного, но оно необходимо для работы некоторых клиентов GRID (которые представляют собой небольшие программы, получающие данные на вход и посылающие результат их обработки на выход), а также для изоляции плагинов и вкладок в браузере Google Chrome.
Кроме способности создавать многоуровневые системы изоляции, хорошая песочница должна уметь определять, какие действия должны быть разрешены приложению по умолчанию. Любой школьник понимает, что было бы глупо запрещать web-браузеру создавать сетевые соединения и в то же время разрешить ему выполнять системный вызов exec() или читать файл /etc/passwd, но как объяснить это программе, реализующей песочницу? Существует четыре возможных варианта:
1.Возложить работу по составлению правил на плечи пользователя, как это делают SELinux и AppArmor. Это самый эффективный и гибкий вариант, у которого есть всего один серьезный недостаток — сложность. Даже матерые сисадмины считают процесс составления правил SELinux нелегким занятием, что уж говорить о тех, кто просто решил запустить браузер в песочнице.
2.Мета-правила. Позволить пользователю самому составлять правила, облегчив задачу с помощью упрощения и группировки правил. Например, вместо массы разных правил типа «разрешить открывать TCP-соединения к порту такому-то такого-то хоста» сделать одно большое правило «разрешить сетевой доступ». Гибкость и безопасность системы снизятся, зато удобство использования резко возрастет. В SELinux такое возможно.
3. Самообучение. Во время первых нескольких запусков приложения песочница анализирует потребности программы в ресурсах и сама составляет правила. Это удобно на сервере, администратор которого беспокоится о безопасности своих сетевых сервисов, однако в том случае, когда мы изначально не можем доверять приложению, этот вариант не подходит.
4.Сигнализировать пользователю каждый раз, когда приложение пытается задействовать ресурсы ОС. Этот вариант очень похож на тот, который используют многие файрволы Windows, выводящие окно с вопросом «Разрешить/Запретить» при инициализации нового сетевого соединения. Разумный вариант, который, тем не менее, требует от пользователя достаточно глубоких знаний в области архитектуры операционных систем.
Теперь, когда мы разобрались с тем, что должна уметь и как функционировать хорошая песочница, посмотрим на то, что нам могут предложить программисты. В следующих трех разделах мы изучим три разные реализации песочниц для приложений: утилиту sandbox, использующую возможности SELinux для помещения приложений в карантин; systrace, опирающийся на системный вызов ptrace; и простой python-скрипт, использующий пространства имен.
Sandbox — SELinux с человеческим лицом
Sandbox — это утилита, созданная для облегчения запуска непроверенных приложений в песочнице SELinux с максимальным уровнем изоляции. В отличие от «голого» SELinux, утилита не требует какой бы то ни было первоначальной настройки и может быть использована абсолютно любым пользователем, независимо от его уровня подготовки.
Функциональность приложения, запущенного под управлением sandbox, оказывается сильно ограниченной. В частности, программа не сможет открыть или создать ни один файл; доступ к сетевым функциям ОС будет полностью отрезан; несколько программ, запущенных под управлением sandbox, не смогут воздействовать друг на друга. В то же время программа сможет подгружать библиотеки, работать с уже открытыми файловыми дескрипторами, писать в текущий терминал и получать доступ к временному хранилищу данных, расположенному в оперативной памяти. Благодаря столь жестким ограничениям, sandbox оказывается почти бесполезным при запуске простых консольных утилит. Все, что можно сделать, это просто заставить непроверенную команду обработать какие-либо данные.
Например:
$ cat /etc/passwd | sandbox cut -d: -f1 > /tmp/users
Утилита cut, запущенная внутри песочницы, сможет получить доступ к стандартным входным и выходным потокам, а потому спокойно обработает представленные ей данные (содержимое /etc/passwd) и благополучно запишет их файл /tmp/users благодаря перенаправлению выходного потока (оно будет осуществлено уже за пределами песочницы). Если же мы попытаемся открыть файл /etc/passwd, находясь внутри песочницы, ничего не получится:
$ sandbox cut -d: -f1 /etc/passwd > /tmp/users
/bin/cut: /etc/passwd: Permission denied
Какие-то более серьезные запросы приложения также будут отклонены. И это могло бы свести полезность sandbox к нулю, если бы он не был основан на SELinux. Дело в том, что политику жесткого ограничения, применяемую утилитой, можно изменить и привести к нужному нам виду (политика носит имя sandbox_t, и ее можно отредактировать с помощью простой графической утилиты system-config-selinux, входящей в состав дистрибутива Fedora). Более того, sandbox можно заставить использовать совершенно другую политику, просто указав ее имя в качестве аргумента опции '-t'. Но это уже для тех, кто разбирается в SELinux.
Совсем иначе обстоит дело с графическими приложениями, которые sandbox также умеет вполне безопасно запускать внутри песочницы. Для этого предусмотрен флаг '-X', использование которого приводит к нескольким коренным изменениям в поведении утилиты. Во-первых, происходит запуск X-сервера Xephyr, который работает внутри уже существующей X-сессии и используется в качестве изолятора запускаемого в песочнице приложения от корневого X-сервера. Внутри Xephyr происходит запуск менеджера окон Matchbox, который растягивает окно приложения на весь экран («экран» только в рамках Xephyr, который сам работает внутри окна корневого X-сервера).
Чтобы приложение смогло получить доступ к домашнему каталогу и каталогу /tmp, но не смогло прочитать хранящиеся в них файлы и навредить, предпринимается серия защитных действий:
- В рамках рандомно выбранного контекста SELinux создаются два пустых каталога в $HOMEDIR и /tmp.
- Происходит запуск SETUID-утилиты /usr/sbin/seunshare, в качестве аргументов которой передаются имена созданных каталогов, ID контекста SELinux и имя запускаемой программы.
- Утилита seunshare использует пространства имен (те самые, о которых мы говорили выше) для монтирования пустых каталогов поверх настоящих каталогов $HOMEDIR и /tmp.
- В новом файловом пространстве имен происходит запуск программы, которая теперь может обращаться только к виртуальному X-серверу и работать с «ненастоящими» каталогами /home и /tmp.
Для запуска графических приложений используется несколько иная политика SELinux: sandbox_file_t, которая открывает возможность работы с файлами домашнего каталога и каталога /tmp, хотя во всем остальном приложение остается сильно ограниченным в функциональности. Если же приложение должно получить доступ к сетевым возможностям, его следует запускать, указав в качестве политики sandbox_web_t (возможность использовать протокол HTTP) или sandbox_net_t (полный доступ к сетевым возможностям):
$ sandbox -X -t sandbox_web_t firefox google.com
Утилита уже включена в состав SElinux и может быть получена путем установки пакета selinux-policy версии не ниже 3.6.12 и пакета policycoreutils версии не ниже 2.0.62.
Systrace — обучаемая песочница
Несмотря на то, что главным бэкэндом к systrace является реализация механизма «system call interposition», доступная в NetBSD и OpenBSD, а также в виде патча в Linux, он вполне нормально работает с ptrace-бэкэндом (который хоть и тормозит, но полностью кроссплатформенный и не страдает от уязвимостей) и может предложить пользователям несколько уникальных характеристик, недоступных в других системах.
Главное отличие systrace от множества аналогов (в том числе sandbox) заключается в возможности контролируемого пользователем самообучения, когда утилита напрямую спрашивает юзера о необходимости запрета или разрешения определенных типов системных вызовов. Это позволяет очень гибко контролировать процесс исполнения программы и давать ей только то, что реально нужно. Кроме того, это отличный инструмент исследователя, который проще и нагляднее стандартного strace.
В большинстве дистрибутивов Linux systrace нет, но он по умолчанию включен в OpenBSD, а также очень прост в установке из исходников:
$ sudo apt-get install build-essential libevent-1.4-2 libevent-dev
$ wget http://www.provos.org/uploads/systrace-1.6g.tar.gz
$ tar -xzf systrace-1.6g.tar.gz
$ ./configure --prefix=/usr/local && make
$ sudo make install
Запускать приложения под управлением systrace просто. Достаточно указать имя программы в качестве аргумента:
$ systrace ls
По умолчанию утилита пытается задействовать возможности графической программы xsystrace для вывода вопросов на экран. Однако xsystrace не входит в стандартную поставку systrace, поэтому придется принудительно заставить утилиту использовать текстовый режим:
$ systrace -t ls
Теперь при каждой попытке выполнить системный вызов подопытная программа будет остановлена, а на экран выведен вопрос о том, стоит ли его запрещать. Ответом может быть достаточно сложное условное выражение, учитывающее аргументы системного вызова, но для простоты можно использовать слова «permit» (разрешить) и «deny» (запретить).
Понятно, что даже самые простые программы в течение цикла своей работы могут выполнить десятки и сотни системных вызовов, и будет очень сложно вручную фильтровать их все.
Поэтому в systrace предусмотрен режим «полного доверия», когда программа запускается с разрешением всего и генерирует файл правил, который затем можно отредактировать, запретив опасные системные вызовы — такие, например, как exec().
Чтобы запустить systrace в этом режиме, достаточно указать флаг '-A':
$ systrace -A ls
Файл правил можно найти в каталоге ~/.systrace:
$ ls -l /home/j1m/.systrace/
-rw------- 1 j1m j1m 631 2011-01-04 12:24 bin_ls
Однако надо понимать, что «режим доверия» придуман для того, чтобы защитить программы от возможных атак, и для запуска подозрительного кода он не подходит, в этом случае придется самостоятельно запрещать или разрешать системные вызовы по мере выполнения программы.
Пространства имен или sandbox-2
Sandbox, написанный Стефаном Грабером, — это крохотная утилита командной строки, которая использует пространства имен, чтобы запереть подопытное приложение в песочнице. Она выгодно отличается от всех своих аналогов тем, что вместо запретов использует метод виртуализации ресурсов, помещая программу в изолированную среду исполнения, которая не соприкасается с основной системой.
Возможно, это не самый безопасный метод, но он весьма способствует удобству использования утилиты, которое сводится просто к запуску подопытной программы без необходимости делать правки правил в том случае, если программе потребуется какой-то дополнительный ресурс.
В сущности, утилита выполняет всего пять простых действий:
- Создает новый пустой каталог (назовем его $NEWROOT) и монтирует к нему корневой каталог системы в режиме copy-on-write (для этого используется оверлейная файловая система aufs).
- Монтирует каталог /home к $NEWROOT/home.
- Создает новое пространство имен для всех возможных ресурсов.
- Подключает procfs к каталогу $NEWROOT/proc.
- Делает chroot в каталог $NEWROOT и запускает приложение.
В результате программа оказывается в довольно интересной среде, которая внешне похожа на среду обычной системы, но отличается от нее тем, что модификация любого файла, за исключением файлов каталога /home (хотя это настраивается), будет видна только самому приложению. Другие процессы и каналы коммуникации IPC не будут видны из песочницы, а значит, не смогут быть эксплуатированы. Программа сможет изменить сетевые настройки песочницы, но эти изменения также не отразятся на основной системе.
Утилита не включена ни в один дистрибутив, поэтому ее придется получать и компилировать самостоятельно:
$ sudo apt-get install bzr
$ bzr branch lp:~stgraber/+junk/sandbox
$ cd sandbox; make
$ sudo make install
После этого запускаем графический интерфейс sandbox-gui, с помощью которого можно изменить некоторые настройки песочницы (поддержка сети, подключение реальных каталогов /home и /tmp) и запустить приложение. Программа требует права суперпользователя, поэтому придется ввести свой пароль.
Выводы
Несмотря на отсутствие единого стандартного интерфейса для ограничения приложений в возможностях, Linux обладает всем необходимым для безопасного запуска приложений в карантине.
Описанные в статье инструменты — лишь малая часть того, что было создано за многие годы, так что если тебе не приглянулась ни одна из систем, всегда можно найти достойную альтернативу.
Начиная еще с версии 2.2, ядро Linux реализует механизм под названием capabilities, позволяющий наделять приложения полномочиями суперпользователя только для выполнения строго определенных функций (например, приложение может выполнять перезагрузку системы, но не способно получить доступ к другим возможностям суперпользователя).
Info
- Для тестовых запусков приложений, требующих права суперпользователя, можно использовать утилиту fakeroot, позволяющую создать иллюзию того, что программа запущена пользователем root (становится возможным изменить любой файл системы).
- QubesOS (qubesos.org) — Linuxдистрибутив, полностью построенный на идее изоляции процессов друг от друга с помощью виртуализации.
- Утилиту sandbox, основанную на SELinux, легко заставить примонтировать выбранные каталоги к каталогам / home/$USER и /tmp. Для этого есть опции '-H' и '-T': «sandbox -H ~/fakehome -T ~/ faketmp vi».