Автор: (c)Крис Касперски ака мыщъх
Многопользовательские операционные системы (к которым относятся Linux и xBSD) содержат огромное количество ошибок, приводящих к утечкам данных и утрате конфиденциальности, что особенно актуально для "промышленных" серверов, обслуживающих тысячи пользователей, имеющих право на установку своих собственных программ (например, PHP-скриптов). В ходе широкомасштабного исследования, проведенного мыщъх'ом, выяснилось, что никсы содержат фундаментальные уязвимости, позволяющие злоумышленникам получать доступ к удаленным данным других пользователей, среди которых помимо мусора зачастую находится очень много интересных вещей!
Во времена MS-DOS для восстановления ошибочно удаленных файлов помимо энуреза использовался следующий трюк. Создавался файл, открытый на запись, делался seek до конца диска, после чего файл закрывался, вбирая в себя все свободное пространство, над которым, конечно, еще предстояло поработать, но нефрагментированные текстовые файлы "вытягивались" без проблем.
Linux и BSD таких вольностей уже не допускают и заботливо "подчищают" выделяемое дисковое пространство, забивая файл нулями, предотвращая захват удаленных данных, принадлежащих другим пользователям. На этом можно было бы поставить жирную точку и закончить статью (как говориться, "на нет - и суда нет"), но механизм затирания реализован криво, с большим количеством ошибок, то исправляемых, то вновь появляющихся в новых ядрах, а потому представляет интерес раскурить эту тему, погрузившись в клубы благородного дыма, испускаемого медленно тлеющими распечатками исходных текстов ядра и сопровождающих его библиотек.
Вот с библиотек мы и начнем.
Рисунок 1. Мыщъх в тусклом свете норы своего одиночества, где клавиатура - самый близкий друг, намного более отзывчивый, чем жена, которая в очередной раз ушла.
Для работы с файловым вводом-выводом большинство программистов использует функции библиотеки libc - fopen(), fseek(), etc, являющиеся достаточно тонкими обертками вокруг системных вызовов open, lseek и т.д. Насколько тонкими? Хороший вопрос!!! Разработчики libc (и ее аналогов, поставляемых вместе с компиляторами типа gcc) никак не встанут на путь единой половой ориентации, бросаясь из крайности в крайность. То они в порыве энтузиазма начинают чистить выделяемое файлу пространство сами, то перекладывают эту заботу на lseek, которая как бы выполняет подчистку еще внутри ядра...
"Как бы", потому что lseek явным образом не гарантирует подчистки выделяемого пространства и выполняет его далеко не везде, не всегда и существует тысяча исключений, при которых подчистка умышленно не выполняется. Почему так, мы расскажем ниже, а пока обратим внимание на то, что подавляющее большинство современных высокоуровневых библиотек (в том числе и входящих в интерпретируемые языки типа Perl, Python, PHP) самостоятельной подчистки не выполняют и потому для реализации атаки спускаться на уровень системных вызовов совершенно необязательно (хотя и желательно, для нейтрализации возможных побочных эффектов).
Рисунок 2. Пример реализации fseek(), не заботящейся о подчистке данных и молчаливо перекладывающей эту заботу на плечи системного вызова lseek, услугами которого она, собственного говоря, и пользуется.
Операционные системы семейства Linux и BSD реализуют унифицированную политику ввода-вывода, обеспечивая единый интерфейс взаимодействия как между файлами, так и между (псевдо)устройствами, что существенно упрощает программирование, попутно сокращая количество системных вызовов, но вместе с тем порождает серьезную проблему, нарушающую стройную концепцию безопасности.
Очевидно, что при работе с устройствами (например, дисковыми томами) функция lseek не может, просто не имеет права, заниматься "подчисткой", поскольку это чревато глобальными разрушениями данных. С другой стороны, lseek должна гарантировать, что при выделении файлу новых кластеров, их содержимое будет "отцензурированно", то есть забито нулями, во избежании попадания конфиденциальных данных в лапы к врагу.
Вот так и разрушается единообразие доступа ко всем файлам и устройствам. Абстрагироваться от природы объекта, над которым выполняется операция позиционирования, уже не получается, и приходится фаршировать ядерный код дополнительными проверками, за правильность реализации которых никто и никогда не ручался.
Теоретически ядро операционной системы при любых операциях позиционирования никогда не должно отдавать пользователю неподчищенные кластеры, содержащие данные, принадлежащие ранее удаленным файлам и простейшие тесты на вшивость (открыл файл, выполнил позиционирование на пару мегабайт, записал несколько байт, закрыл файл) показывают, что политическая ситуация Багдада спокойна, как слон в нирване и поводов для беспокойства как будто бы и нет.
Но разве серьезные тесты так выполняются?! Давайте испробуем более хитрые комбинации, вводящие ядро в замешательство и заставляющие его отдавать нам чужие данные, чего по логике вещей происходить не должно, но... все-таки происходит!
Разные ядра имеют разные дыры и в процессе экспериментов (проводимых с удаленными серверами) мыщъх далеко не всегда мог определить, с каким именно ядром он имеет дело, а потому приводит лишь голый псевдокод без указания жертвы, на которой он работает. Как уже говорилось выше, конкретные реализации могут использовать либо системные вызовы (если они доступны на том языке, который установлен на удаленном сервере), либо высокоуровневые файловые функции ввода-вывода.
В принципе, возможна комбинация, при которой "дыра" в ядре перекрывается библиотечной реализацией fseek, самостоятельно подчищающей выделяемые файлу данные, но... тут уж ничего не поделаешь. Это на локальной машине мы можем выбирать все, что нам заблагорассудится, а вот при атаках на сервера приходится использовать, что дают. К счастью, серверов сейчас намного больше одного и если не ставить перед собой цель поломать какой-то конкретный сервер, а просто хочется чего-нибудь захачить (неважно, что), то приятное времяпрепровождение обеспечено.
Администраторам же остается только посоветовать применить нижеперечисленные алгоритмы на себе и при необходимости предпринять защитные меры.
Рисунок 3. man по lseek.
Выстрел первый. Предупреждающий. В смысле - дохляк полный. Но иногда он все-таки срабатывает:
Выстрел второй. Уже прицельный и работающий на достаточно большом количестве операционных систем и библиотек;
Выстрел третий. Контрольный. Срабатывает довольно часто, хоть иногда и промахивается.
Выстрел четвертый. Вариация на предыдущую тему с той лишь разницей, что на шаге №3 мы записываем не ноль байт, а хотя бы один.
Выстрел пятый. Пуля со смещенным центром тяжести. Довольно эффективна для SuSE Linux и некоторых BSD-подобных операционных систем (предположительно, FreeBSD):
Выстрел шестой. Последний. Если не поможет и он, то поцеловав холодную сталь ствола, падем на землю и ждем смерти, согласно композиции The Last Ammunition (какая к черту холодная сталь, когда мы только что выпустили из пистолета весь наш боезапас?!), ну да ладно, не будем придираться. В хакерстве, как и в лирике, есть свои нюансы.
Разумеется, помимо вышеописанных способов, существуют и другие трюки. Их достаточно много, даже слишком много, чтобы перечислять, тем более, что все вертится вокруг одного и того же механизма. Записи нуля байт или одного байта и запутанной схемы позиционирования по файлу в надежде, что ядро "рюхнется" и забудет подчистить выделяемые кластеры.
Один тонкий момент. Часть ядер содержат ошибку, при которой в закрытом файле оказываются одни нули, но если этот файл читать до его закрытия, то... можно получить доступ к информации, к которой мы доступа иметь не должны. Идея, надеюсь, понятна?!
К сожалению, написать универсальный грабер дискового пространства так и не получилось и вот почему - если мы выбираем неверный прием (обрабатываемый ядром), то данные, принадлежащие ранее удаленным файлам, необратимо затираются нулями и к моменту, пока мы подберем "ключ", никаких полезных данных на диске, скорее всего, уже не останется, однако сервер не стоит на месте, а продолжает активно работать - пользователи создают и удаляют файлы, так что после того, как правильный "патрон" найден, им можно пользоваться вновь и вновь, беззастенчиво тыря информацию у своих соседей по серверной площадке.
Рисунок 4. Шесть пуль в хакерском кармане.
Имея монопольный доступ к атакуемой машине, не ограниченный никакими квотами, мы можем захватить хоть все свободное пространство целиком, а в нем... сказать, что до хвоста всего интересного - означает не сказать ничего, поскольку там просто кладезь критических данных, позволяющих нам повысить уровень своих привилегий или просто похулиганить.
Достаточно многие администраторы хранят пароли, назначаемые пользователям, открытым текстом в специальных файлах, доступным только им одним (в самом деле - ситуация, в которой пользователь забыл свой пароль, более чем типична, а поскольку Linux/BSD хранят не пароли, а их хэши, то единственный выход - назначить пользователю новый пароль, но тогда он его точно забудет). Даже если файл с паролями ни разу не удалялся, все равно - в процессе открытия/модификации/сохранения операционная система могла перенести его на новое место, создать резервную копию, временный файл, автоматически удаляемый после редактирования и т.д. Словом, возможностей много.
Электронная почта - это вообще Клондайк. Чего там только не бывает! А файлы после их прочтения удаляются довольно часто (редко кто хранит всю почтовую базу целиком), а если еще на атакуемой машине расположен почтовый сервер, складывающий все сообщения в папку типа inbox, откуда администратор забирает их по протоколу POP3, это прямой путь к root'у.
Впрочем, монопольный доступ к чужой машине в реальной жизни практически никогда не встречается, особенно если на этой машине работает администратор.
Рисунок 5. Мыщъх грабит свою локальную машину.
Прежде, чем обсуждать аспекты атаки на Web-аккаунты, поговорим о такой неприятной для хакеров штуке, как квотирование дискового пространства, где мы уже не можем сделать seek на размер нашей совести, поскольку объем квоты наверняка меньше. Ну, и что интересного мы там найдем?!
На самом деле - много чего! Квотирование лишь лимитирует суммарный размер всех файлов, принадлежащих данному пользователю, но не закрепляет за ними какой-то конкретный региона дискового пространства и потому операционная система может выделять нам любой. Ну... или практически любой, как правило, наилучшим образом соответствующий размеру данного файла плюс небольшой "зазор", на "вырост". Таким образом, делая seek на различные расстояния, мы каждый раз захватываем разные блоки данных. А если учесть, что карта свободного пространства сильно нагруженной дисковой системы постоянно меняется, то у нас есть все шансы "перепахать" все свободное дисковое пространство, естественно тут же возвращая захваченные данные назад, во избежание превышения отпущенной нам квоты.
Лучше всего делать это в бесконечном цикле, чтобы овладевать чужими файлами сразу же после их удаления, пока они не будут затерты кем-то еще. Конечно, подобная дисковая активность редко остается незамеченной и администратор запросто может вызвать нас на ковер, а в случае бесплатного Web-хостинга - вообще закрыть аккаунт безо всяких предупреждений, но... кто не рискует, тот не пьет шампанского!
Теперь перейдем непосредственно к технике захвата чужих аккаунтов. Что содержится в удаленных данных? В основном - исходные тексты скриптов, зачастую содержащие грубые ошибки (т.е. дыры), которые остается только найти и заюзать. Кстати, пароли, жестко прошитые в теле скрипта - вполне обычные дело, встречающееся практически повсеместно.
Следом идут данные пользователей, среди которых числятся иногда и сами держатели аккаунта. Что-то типа тестового входа, зачастую с тем же самым паролем, что управляет основным аккаунтом. Именно так мыщъх в короткое время захватил сотни Web-аккаунтов, правда, не желая никому причинять вреда, тут же вернул их обратно.
Про номера кредитных карт, адреса электронной почты и бла-бла-бла вряд ли стоит говорить. Случается, что их шифруют, но это один случай из миллиона. Практически всех их держат открытым текстом. Конечно, использовать такую информацию в общем случае незаконно, но... написать пользователям, насколько "надежен" сайт, которому они доверили свои данные - сам Джа велел.
Кстати говоря, наибольший интерес представляют провайдеры, предоставляющие как бесплатный, так и платный Web-хостинг, что позволяет нам, зарегистрировавшись под левым ником, атаковать сайты серьезных организаций. Провайдеры, предоставляющие чисто халявный хостинг, неинтересны по той простой причине, что серьезные пользователи обходят их стороной, а радоваться, как круто ты взломал домашнюю страничку Васи Пупкина - ну, какой в этом кайф?! Соответственно, чисто коммерческие провайдеры (не имеющие тестового входа) идут лесом, поскольку регистрация аккаунта не только требует денег, но и в большинстве случаев раскрывает наше инкогнито, а вот это уже реально нехорошо. Спрятать свой IP-адрес можно (и нужно) тысячами способов, но скрыть личность отправителя платежа - довольно проблематично даже с использованием Web-Money или прочих электронных кошельков, которые на самом деле никакие не анонимные. Другой вопрос, что искать хакера, не причинившего реального вреда, никто не будет, но... никаких гарантий у нас нет, впрочем мы сильно отклонились в сторону от основного разговора, а ведь взлом аккаунтов не всегда проходит легко и приятно.
Рисунок 6. Атака на сервер под надзором админа.
Мелкие провайдеры зачастую держат на одной машине и Web- и (My)SQL- сервер, и диск у них всего один (в смысле - логический диск, а физически это может быть и многодисковый RAID). Базара нет - сломать их сможет даже пионер, правда если движок сайта основан на (My)SQL, то трем лыжи салом. Или готовим скипидар. И грызем лед зубами, а все потому, что база данных - это довольно сложное сооружение, живущее по своим законам и удаленные записи не возвращаются в пул свободного дискового пространства. Другими словами, до них добраться мы не сможем.
Однако целиком весь сайт в (My)SQL'е никто не держит и множество скриптов расположено в дисковых файлах и эти файлы редактируются, затачиваясь под себя и под конкретный замысел дизайнера. Именно в этих файлах чаще всего и встречаются жестко прошитые пароли на доступ к базе данных, которая (при знании пароля) доступна не только владельцу аккаунта, но и всем праздно шатающимся хакерам.
Захват аккаунтов по прежнему остается возможным, даже если (My)SQL-сервер физически расположен на другой машине, а сама база хранится на диске, к которому у нас нет прямого доступа, да он нам и не нужен!!! Нам вполне достаточно того небольшого количества скриптовых файлов, хранящихся на пользовательских дисках.
А сколько этих дисков?! Зависит от конфигурации сервера, но даже сравнительно небольшие серверы имеют множество жестких дисков - десятки, а то и сотни!!! Если администратор сервера принял решение объединить их в RAID (неважно - программный или нет), то можно считать, что нам крупно повезло, поскольку мы имеем шансы захватить данные любого из пользователей.
Хуже, если диски монтируются на файловую систему, что в каком-то смысле упрощает администрирование и делает систему намного более масштабируемой, однако в таком случае мы ограничены только тем диском, на котором находятся наши файлы и файлы наших "соседей" по лестничной площадке, хостящихся на том же самом физическом диске. Естественно, шансы заполучить интересный контент при этом существенно уменьшаются, особенно на BSD-системах, которые делят диск на группы цилиндров, группируя файлы, принадлежащие одному пользователю, в пределах одной зоны. Количество зон, как правило, невелико и в одной зоне живет множество пользователей, но все-таки... возможности по захвату свободного пространства тают прямо на глазах.
Единственный выход - создавать множество аккаунтов, надеясь на то, что они окажутся расположенными на разных дисках, обеспечивая тем самым покрытие большой площади для захвата данных.
Безопасность операционных систем и серверов - весьма неустойчивая вещь, подобная снежной лавине. Мелкие ошибки, накапливаясь со временем, постепенно уплотняются и образуют мощные осадочные пласты, готовые прийти в движение, сметая любые преграды на своем пути.
Самые коварные ошибки проектирования - те, которые неоднократно обсуждались, все о них как бы помнят, но в то же время уже давно забыли. Захват свободного пространства посредством позиционирования как раз к таким ошибкам и принадлежит. Казалось бы, проблема не стоит выведенного яйца и была решена еще в незапамятные времена, но... это в теории. А на практике в эксплуатации находится огромное количество серверов, работающих на уязвимых операционных системах и допускающих удаленные атаки по одному из сценариев, описанных выше.