Показано с 1 по 2 из 2.

ЗАЩИТА LINUX С ПОМОЩЬЮ LKM

  1. #1
    Senior Member Репутация Репутация Репутация Репутация Репутация Репутация Репутация Репутация Репутация Репутация Репутация Аватар для SDA
    Регистрация
    07.01.2005
    Адрес
    Москва
    Сообщений
    7,168
    Вес репутации
    3162

    ЗАЩИТА LINUX С ПОМОЩЬЮ LKM

    Перехват системных вызовов в 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 с целью мониторинга и защиты информации. Это, естественно, далеко не все, что можно осуществить, и вы наверняка сможете придумать еще большее количество применений этой техники.
    http://www.securitylab.ru/

  2. Реклама
     

  3. #2
    Full Member Репутация
    Регистрация
    18.06.2005
    Адрес
    Украина, Одесса
    Сообщений
    119
    Вес репутации
    42
    класс! мониторить вызовы правда можно и strace`ом, но заменить execve() и open() это круто (-;
    printk("VFS: Busy inodes after unmount.""Self-destruct in 5 seconds. Have a nice day...\n");
    linux-2.3.99-pre8/fs/super.c
    Registered Linux User #377584

Похожие темы

  1. Беcсигнатурная защита от смс-троянов с помощью KMS
    От apq в разделе Лечение и защита мобильных устройств
    Ответов: 0
    Последнее сообщение: 02.11.2010, 18:46
  2. Защита Internet Explorer с помощью IEController
    От SDA в разделе Другие программы по безопасности
    Ответов: 0
    Последнее сообщение: 09.08.2010, 15:15
  3. Ответов: 0
    Последнее сообщение: 11.12.2008, 18:59
  4. Анализируем траффик в Linux с помощью Darkstat
    От Синауридзе Александр в разделе Linux
    Ответов: 2
    Последнее сообщение: 05.08.2008, 19:21
  5. защита Linux
    От egik в разделе Сетевые атаки
    Ответов: 1
    Последнее сообщение: 22.03.2005, 00:09

Свернуть/Развернуть Ваши права в разделе

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •  
Page generated in 0.00116 seconds with 19 queries