17.07.2005

«јў»“ј LINUX — ѕќћќў№ё LKM

ѕерехват системных вызовов в Linux традиционно использовалс€ дл€ написани€ различных руткитов и других программ, используемых хакерами дл€ сбора информации и скрытого контрол€ над удаленной системой. ¬ данной же статье € расскажу о том, как использовать данную технику дл€ мониторинга и защиты системы.

ѕерехват системных вызовов в Linux традиционно использовалс€ дл€ написани€ различных руткитов и других программ, используемых хакерами дл€ сбора информации и скрытого контрол€ над удаленной системой. ¬ данной же статье € расскажу о том, как использовать данную технику дл€ мониторинга и защиты системы.

ѕерехват системных вызовов

ћногим известно, что исполнение системных вызовов сводитс€ к выбору нужного слота в массиве sys_call_table, чтению адреса функции из этого слота и исполнению кода, записанного по этому адресу. ѕерехват в таком случае €вл€етс€ заменой адреса оригинальной функции в sys_call_table на адрес нового системного вызова. Ќо €драми Linux 2.6.x адрес sys_call_table более не экспортируетс€. ѕоэтому его придетс€ искать вручную. я воспользуюсь методикой, предложенной dev0id из UkR Security Team. ƒл€ начала следует узнать, какие системные вызовы экспортируютс€ €дром. ƒл€ примера был вз€т вызов sys_close. јдрес же таблицы sys_call_table находитс€ между концом секции кода и концом секции данных, поэтому не составл€ет большого труда простым перебором найти адрес sys_close и вычислить точное местоположение sys_call_table. ƒл€ подробного описани€ см. статью "«ащита от исполнени€ в стеке (ќ— Ћинукс)."
—тоит обратить внимание на перехват вызова execve(). ѕо стандартной схеме перехватить его не получитс€, и самым легким выходом из этой ситуации €вл€етс€ перемещение оригинального вызова в свободный слот sys_call_table.

ћониторинг

ƒл€ ведени€ журнала действий пользовател€(и, возможно, хакера) нам также потребуетс€ перехват системных вызовов.  акие это вызовы - зависит только от того, что именно вы хотите записывать в журнал. ¬ моем примере € перехватываю системные вызовы execve(), open(), и socketcall(). ¬озможен, например, еще мониторинг содержимого файлов с помощью перехвата вызова write().
ѕрежде всего мне понадобилось журналировать запущенные программы. Ёто делаетс€ легче всего - в перехваченном execve() следует просто записать в журнал аргумент filename.

char* temp;
/* выделим пам€ть под буфер */
temp = (char*)kmalloc(strlen(filename)+1, GFP_KERNEL);
copy_from_user(temp, filename, strlen(filename)+1);
/* запишем в журнал */
do_log(temp, EXECVE);
kfree(temp);

“акже будет полезным мониторинг каких-либо файлов, не предназначенных дл€ просмотра/редактировани€ обычными пользовател€ми. ƒл€ этой цели мною был перехвачен вызов open(). «десь все происходит по той же схеме, только добавлена проверка открываемого файла на присутствие в списке файлов, предназначенных дл€ мониторинга.

ƒл€ отслеживани€ сетевых подключений потребуетс€ перехват socketcall(). ƒанный вызов имеет два параметра - команду и специфический дл€ нее набор аргументов. ƒл€ отслеживани€, к примеру, исход€щих подключений требуетс€ проверить команду на SYS_CONNECT, а затем привести второй элемент в массиве args к типу sockaddr и узнать нужный IP и порт из пол€ sa_data.

‘ункци€ do_log служит дл€ добавлени€ записи в журнал. ƒл€ этого используютс€ оригинальные системные вызовы open(), write(), close(), brk(). »х адреса копируютс€ из sys_call_table при инициализации модул€. —охранение адресов оригинальных функций потребуетс€ и дл€ всех остальных перехваченных системных вызовов.

o_open = sys_call_table[__NR_open];
o_write = sys_call_table[__NR_write];
o_close = sys_call_table[__NR_close];
o_brk = sys_call_table[__NR_brk];

ѕри исполнении оригинальных системных вызовов в пространстве €дра необходимо скопировать аргументы в адресное пространство пользовател€. Ёто достигаетс€ за счет следующей схемы. ¬ указателе на текущий процесс current имеетс€ подструктура mm, отвечающа€ за менеджмент пам€ти данного процесса. ¬ ней же имеетс€ указатель на конец сегмента данных brk. ƒл€ изменени€ размера сегмента данных используетс€ функци€ brk(). ћы расширим сегмент данных и с помощью функции copy_to_user скопируем наши аргументы в адресное пространство пользовател€:

unsigned long end;
end = mm->brk;
sys_brk((void *)(end+strlen(pathname)+2));
copy_to_user(end+2, pathname, strlen(pathname+1));

ѕосле этого можно исполнить системный вызов в адресном пространстве €дра. — помощью системного вызова write() следует записать в файл журнала сведени€ о произошедшем событии. ‘айл журнала, естесственно, должен быть скрыт с помощью перехвата функции getdents(). Ќо журналирование - далеко не все, что можно сделать с помощью LKM. ƒанную технику можно примен€ть и дл€ защиты.

ќграничение действий пользователей

¬сем известно, что права root в Unix отличаютс€ от прав администратора, скажем, в Windows XP. ѕользователь root может выполн€ть любые действи€. »менно поэтому нулевой UID есть сама€ главна€ цель хакера. я обь€сню, как, использу€ LKM, можно ограничить в правах любого пользовател€.

«апрет на запуск программ/открытие файлов

«апрет на запуск программ осуществл€етс€ с помощью перехвата execve(), а запрет на открытие файлов с помощью перехвата open(). —оздадим специальную структуру, описывающую режим доступа к программе или файлу:

struct {
char* filename; /* путь к файлу */
struct { /* структура доступа */
int id; /* id пользовател€ */
int pid; /* pid задачи */
char* prog; /* им€ задачи */
int type; /* тип ограничени€(пользователь|pid|им€ задачи) */
int mode; /* режим(запрет|открытие|запуск|открытие и запуск) */
}* list;
}* access;

ѕоле filename указывает на полный путь к файлу. —труктура доступа поддерживает запрет по id пользовател€, по pid и имени задачи. “ут же устанавливаетс€ и режим доступа - полный запрет, только открытие, только запуск, открытие и запуск. ѕо желанию этот список можно расширить, добавив чтение/редактирование и т.д. ѕри запуске execve() или open() следует выполнить поиск данного имени файла в списке, а затем сверить разрешени€. ¬ случае запрета вернуть -EACCESS. ћожно и расширить возможности данного примера, не запреща€ доступ, а введ€ аутентификацию с помощью запуска псевдопрограммы. ƒл€ ограничени€ доступа к файлам простых пользователей проблемы может решить chmod, но дл€ ограничений действий суперпользовател€ можно воспользоватьс€ предложенной схемой.

ќграничение доступа к информации

 ак уже было сказано, права root в Linux позвол€ют выполн€ть любые действи€. Ќо запрет на выполнение дл€ пользовател€ root дает пон€ть о скрытой защите. я предлагаю еще одну схему защиты информации - защита с помощью редиректа.
ƒопустим, имеетс€ какой-либо файл с важной информацией. Ёту информацию используют какие-либо программы, но дл€ остальных этот файл должен быть недоступен. ћы подменим системный вызов open(), чтобы вызывалс€ оригинальный вызов, открывающий другой, скрытый файл, в котором важна€ информаци€ будет отсутствовать. ¬сЄ это в случае открыти€ файла не специализированной программой. »наче будет вызван оригинальный вызов с правильным параметром. “очно так же следует поступить и с вызовом execve() - запускать безопасные бинарные файлы вместо информационно важных. ƒл€ осуществлени€ этого можно использовать модифицированную структуру из предыдущего примера, добавив в нее путь к подмен€ющему файлу.

 онтроль с помощью псевдопрограмм

Ћюба€ защитна€ система должна обеспечивать режим администратора. –ежим, в котором можно производить настройки и где сн€ты ограничени€. “ак как у нас урезаны права суперпользовател€, то следует предусмотреть режим настройки и режим полного доступа. я предлагаю осуществить это с помощью псевдопрограммы с именем, например, /bin/control. ѕсевдопрограмма не €вл€етс€ бинарным исполн€емым файлом, и она не хранитс€ на жестком диске. ¬ перехваченном вызове execve() мы проверим, если параметр filename равен /bin/control. ¬ этом случае мы исполн€ем код, наход€щийс€ в нашем модуле €дра. ≈стесственно, здесь необходима авторизаци€, хот€ бы вида логин/пароль. ƒл€ вывода на экран используетс€ вызов write() с параметром 1(stdout), а дл€ ввода - read() с параметром 0(stdin). я же приведу пример с передачей логина/парол€ через опции командной строки:

/* исполнение псевдопрограммы */
if(!strcmp(filename, "/bin/control")) {
/* проверка логина и парол€ */
if(!strcmp(argv[1], "auth")) if(auth(argv[2], argv[3])) control = 1;
if(control == 0) return -ENOENT;
/* получение опций командной строки и настройка
* редиректов, уровней доступа и т.д.
* ...
*/
/* закрытие сессии */
if(!strcmp(argv[1], "close") control = 0;
/* псевдопрограмма не существует, вернем ENOENT */
return -ENOENT;
}

¬ данный код следует еще вставить обработку команды переключени€ в режим без ограничений, тот режим, в котором снимаютс€ все запреты и редиректы. —ледует ввести переменную safe - если она, допустим, равна нулю, то все проверки в перехваченных вызовах будут игнорироватьс€. ƒанна€ псевдопрограмма не существует, поэтому следует вернуть -ENOENT, если какой-нибудь хакер все же додумаетс€ ее запустить.

«ащита против шеллкода

ќчень большое количество эксплоитов используют шеллкод, то есть скомпилированный набор ассемблерных инструкций. „аще всего, конечно, это вызов /bin/sh с правами эксплуатированной программы. ћногие создатели эксплоитов не утруждают себ€ написанием собственного шеллкода и просто берут готовый. я предлагаю защиту от распространенных шеллкодов с помощью перехвата системных вызовов. ƒл€ защиты от шеллкодов от удаленных эксплоитов следует перехватить socketcall(), а дл€ локальных, например, read() и execve(). ¬ первом случае надо проверить команду на получение данных, скопировать второй аргумент в адресное пространство €дра и сверить наход€щиес€ там данные с наиболее известными шеллкодами, такими как запуск /bin/sh, предоставление удаленного доступа, и т.д. — функцией read(), похожей на recv(), поступить следует так же - вызвать оригинальную функцию и проверить буфер на наличие шеллкода. ≈ще ошибки с переполнением буфера встречаютс€ в аргументах, передаваемых в программу. ¬ таком случае надо провер€ть параметр argv[] в вызове execve(). ¬ случае обнаружени€ данной последовательности байт,€ считаю, не нужно скрывать данную последовательность, потому что ее по€вление может быть чистой случайностью. Ќо если после этого программа, вызвавша€ read() или socketcall(), запустит /bin/sh, можно запретить запуск, вернув -EACCESS. “ака€ примитивна€ техника позволит защититьс€ от многих распространенных шеллкодов.

«аключение

¬ данной статье были рассмотрены некоторые способы применени€ перехвата системных вызовов в Linux с целью мониторинга и защиты информации. Ёто, естественно, далеко не все, что можно осуществить, и вы наверн€ка сможете придумать еще большее количество применений этой техники.
"Ћаборатори€  асперского" - международна€ компани€-разработчик программного обеспечени€ дл€ защиты от вирусов, хакеров и спама. ѕродукты компании предназначены дл€ широкого круга клиентов - от домашних пользователей до крупных корпораций. ¬ активе "Ћаборатории  асперского" 16-летний опыт непрерывного противосто€ни€ вирусным угрозам, позволивший компании накопить уникальные знани€ и навыки и стать признанным экспертом в области создани€ систем антивирусной зашиты.

 омпани€ SoftKey Ц это уникальный сервис дл€ покупателей, разработчиков, дилеров и аффилиатЦпартнеров.  роме того, это один из лучших »нтернет-магазинов ѕќ в –оссии, ”краине,  азахстане, который предлагает покупател€м широкий ассортимент, множество способов оплаты, оперативную (часто мгновенную) обработку заказа, отслеживание процесса выполнени€ заказа в персональном разделе, различные скидки от

јкадеми€ »нформационных —истем (ј»—) создана в 1996 году и за врем€ работы обучила свыше 7000 специалистов различного профил€. ј»— предлагает своим партнерам дес€тки образовательных программ, курсов, тренингов и выездных семинаров. —егодн€ ј»— представлена направлени€ми: Ђ»нформационные технологииї, Ђƒистанционное обучение в области »“ї, Ђ»нформационна€ безопасность, Ђ”правление проектамиї, ЂЅизнес-образованиеї, Ђ—еминары и тренингиї, ЂЁкологические промышленные системыї, Ђ онференцииї, Ђ онсалтингї и Ђ онкурентна€ разведка на основе »нтернетї.
 

или введите им€

CAPTCHA