Смекни!
smekni.com

Разработка и анализ эффективности средств отражения распределенных атак (стр. 9 из 13)

}


Здесь вызывается функция RegisterPlugin, предоставляемая snort, которая говорит snort о том, что если в правиле встречаются опции, относящиеся к модулю tcp_syn_flood, то необходимо вызвать функцию TcpSynFloodInit, находящуюся в файле sp_tcp_syn_floodы.с. Реализация функции инициализации будет описана ниже.

3. добавить вызов функции SetupTcpSynFlood() в plugbase.c:

· Всекции /* custom detection plugin */ добавить

#include "detection-plugins/sp_tcp_syn_flood.h"

· В теле функции InitPlugIns() добавить вызов функции SetupTcpSynFlood();

4. добавить элемент PLUGIN_TCP_SYN_FLOOD к перечислению (enum) доступных модулей Snort

Для того чтобы наш модуль был успешно интегрирован в snort осталось реализовать функцию TcpSynFloodInit(). Это статическая функция, в которой необходимо выделить память для используемых модулем структур данных, проинициализировать эти структуры данными, указанными в правиле. Здесь стоит отметить, что для каждого правила, в котором заданы параметры для модуля tcp_syn_flood будет вызвана эта функция.

Здесь так же стоит отметить, что модуль tcp_syn_flood должен генерировать события Snort связанные с началом и окончанием атаки. Для этого его надо зарегистрировать в качестве генератора событий Snort. Сделать это проще простого. Надо добавить соответствующие объявления в файл ./src/generatos.h:

#define GENERATOR_TCP_SYN_FLOOD 224

#define SYNFLOOD_STARTED "(tcp syn flood: PREVED)"

#define SYNFLOOD_FINISHED "(tcp syn flood: PAKA)"


В первой строчке мы регистрируем модуль в качестве генератора событий, а дальше идет объявление сообщений, которые могут записываться в логи. Как уже было сказано, нам необходимо генерировать два события.

Перед тем как продолжить описание реализации модуля на языке C, следует описать внутреннее устройство модуля и используемых в нем структур данных. Модуль tcp_syn_flood предназначен для обнаружения и возможного отражения TCPSYN атаки. Обе эти функциональности представлены отдельными подмодулями: TcpConnEstTimeChecker и TcpSynFloodPreventionModule. Первый из них предназначен именно для обнаружения атаки с помощью описанной разделе 3 методики. Он является решающей системой, которая генерирует события snort говорящие о том, что TCP атака началась или закончилась. Второй подмодуль предназначен для накопления "положительной" статистики работы защищаемого сервера при условии атаки. Под положительно статистикой здесь понимается учет количества успешно установленных TCP соединений сервера с клиентами. Дифференциация клиентов производится по признаку их IP адресов и значения поля TTLв приходящих от них пакетах. Если атака начинается, то накопление статистики приостанавливается, а на основании уже накопленных данных модуль принимает решения от каких клиентов пропускать SYN пакеты к серверу, а от каких нет.

5.3.1 Структура модуля TcpConnEstTimeChecker

Полное название этого модуля TcpConnectionEstimateTimeChecker. Он предназначен для определения количества "просроченных" tcp соединений и сравнения их количества с допустимым порогом.

Стоит напомнить, что в соответствии с изложенной в разделе 3 методикой необходимо вести учет количества полуоткрытых TCP соединений, которые находятся в этом состоянии дольше определенного промежутка времени. Значения указанных параметров этот модуль берет из правила Snort:

· server_timeout_sec – таймаут отведенный на сервере для установки TCP соединений. Значение этого параметра задается в секундах

· max_overdue_count – максимально допустимое количество полуоткрытых на сервере соединений

· max_overdue_count_diviation – разброс максимально допустимого количества полуоткрытых на сервере соединений. Это значит, что модуль будет генерировать событие "TCPSYN атака началась" после того, как на сервере будет (max_overdue_count + max_overdue_count_diviation) полуоткрытых соединений и, соответственно "TCPSYN атака закончилась", когда количество таких соединений станет меньше чем (max_overdue_count -max_overdue_count_diviation)

· overdue_time_sec – время после которого полуоткрытое соединение считается "просроченным". Значение этого параметра задается в секундах

· check_period_sec – период с которым модуль должен проверять текущее количество полуоткрытых соединений. Как будет показано дальше, операция проверки количества просроченных соединений требует больше вычислительных ресурсов, чем просто обработка пакетов. Если этому параметру установить довольно большое значение, то атака будет обнаружена позже, а если маленькое значение, то больше ресурсов системы будет расходоваться на более частые проверки.

Для того чтобы минимизировать затраты памяти и увеличить быстродействие было принято решение не хранить время прихода пакетов в явном виде. Для определения "возраста" полуоткрытых соединений используется довольно хитрая структура данных: массив бинарных деревьев. Такой массив показан на рис.5.1.


Рисунок 5.1 Массив бинарных деревьев, используемый TcpConnEstTimeChecker.

В узлах каждого из деревьев хранится информация об отдельном полуоткрытом TCP соединении. Эта информация представлена следующей структурой:

typedef struct _TimeCheckerTreeNodeData

{

ubi_trNodeNode;

// Номер acknowledgement последовательности (для Syn+Ack и Ack пакетов)

u_int32_t SeqAckNumber;

}

TChTreeNodeData;

Для более тесной интеграции со Snort была использована готовая реализация бинарных деревьев, поставляемая с системой. Эта реализация так же используется другими модулями. Это позволило значительно сократить время разработки модуля, повысить эффективность его реализации и уменьшить количество возможных багов. Более того, в случае если в этой реализации деревьев имеются баги, то использующий их модуль автоматически подхватит исправления, если будет установлен на более поздние версии Snort. В Snort есть несколько реализаций бинарных деревьев: ubi_BeenTree и ubi_SplayTree. Они приведены к единому интерфейсу, который позволяет работать с ними независимо от текущей реализации. То какие деревья используются указывается лишь включением соответствующего заголовочного файла. В данный момент использованы Splay деревья. В дальнейшем возможно сравнение производительности системы, основанной на другой реализации.

Как видно из приведенного фрагмента кода в структуре объявлены два поля:

· Node – узел бинарного дерева. Это поле должно идти первым в объявлении структуры. Это обусловлено оптимизацией во внутренней реализацией бинарных деревьев Snort

· SeqAckNumber – значение последовательности Acknowledgement в SYN+ACK пакетах исходящих от защищаемого сервера.

При получении данным модулем SYN+ACK пакета исходящего от защищаемого сервера в дерево, имеющее нулевое смещение в массиве помещается соответствующий элемент. Этот элемент соответствует полуоткрытому на сервере соединению.

Как уже отмечалось выше, в целях минимизации используемой памяти и увеличения быстродействия, для каждого узла не хранится время его создания. "Возраст" узла определяется индексом дерева в массиве деревьев, в котором он находится. С периодичностью задаваемой вышеописанным параметром правила check_period_sec массив деревьев сдвигается на 1 дерево вправо. При этом последнее дерево удаляется, освобождая выделенную под него и его элементы память, а элемент массива с нулевым смещением инициализируется новым деревом.

Размерность массива деревьев определяется при инициализации модуля в функции InitTcpConnEstTimeChecker как:

int rootNodesCount = ceil(serverTimeout / _checkPeriod);

При такой организации внутренних структур данных "возраст" полуоткрытых соединений, которым соответствуют узлы в i-м дереве массива определяется как (_checkPeriod * i) . Узлы самого правого в массиве дерева, которые при сдвиге удаляются соответствуют полуоткрытым на защищаемом сервере соединениям для которых истек таймаут отведенный на установку соединений и они закрываются автоматически.

При получении модулем ACK пакета, предназначенного серверу, производится поиск узла дерева, соответствующего полуоткрытому соединению. Поиск производится по ключу (AcknowledgenumberACK пакета – 1). После того как узел найден, он удаляется из дерева, что соответствует закрытию полуоткрытого соединения на сервере.

Как было показано в разделе 3, основным критерием по которому принимается решение о начале или окончании атаки, это количество "просроченных" полуоткрытых соединений. В терминах вышеизложенных структур данных это количество элементов в деревьях, индексы которых больше определенного граничного значения, определяемого так же в функции инициализации модуля InitTcpConnEstTimeChecker:

// the index of the first node with overdued connections

checker->firstOverduedNodeIndex = checker->overdueTime / checker->checkPeriod;

Так же стоит отметить, что модуле реализована обработка RST пакетов приходящих как от защищаемого сервера, так и от клиента.

Описанный выше модуль в программной реализации представлен в виде следующей структуры и функций работы с ним:

typedef struct _TcpConnEstTimeChecker

{

/*** Опцииправила ***/

// time in seconds after which the half-open connection is overdue

int overdueTime;

// period in seconds to check the number of overdue half-open connections

int checkPeriod;

// the max allowed number of half-open connections

int overdueUpperBound;

// the diviation of overdueUpperBound

int overdueUpperBoundDiviation;

/*** Внутренниеданные ***/

// the number of root nodes in the array

int rootNodesCount;

// the array of root nodes

ubi_btRoot* rootNodes;

// the index of the first node, which contains overdued connections

int firstOverduedNodeIndex;

// time when the last shift was made

time_t lastShiftTime;

// Indicates if Syn Flood atack presents

int atackState;

}

TcpConnEstTimeChecker;

/***Интерфейс***/

void InitTcpConnEstTimeChecker(TcpConnEstTimeChecker* checker, int _overdueTime,

int _checkPeriod, int _overdueUpperBound, int _overdueUpperBoundDiviation,

int _serverTimeout);

void DeInitTcpConnEstTimeChecker(TcpConnEstTimeChecker* checker);

int TcpConnEstTimeChecker_ProcessPacket(TcpConnEstTimeChecker* checker, Packet* p, int packetType);

int ShiftRootNodes(TcpConnEstTimeChecker* checker, int GenerationCount);


5.3.2 Структура модуля TcpSynFloodPreventionModule

Как упоминалось в разделе 2, на данный момент общедоступные методы противодействия TCPSYN атаке не отличаются эффективностью и обоснованностью выбора значений конкретных параметров при настройке системы. Т.к. на данный момент авторами не разработана модель, позволяющая построить математически обоснованную методику противодействия атаке, то было принято решение реализовать эту функциональность без соответствующей базы. При этом одним из требований к разработке является возможность работы с модулем посредством определенного интерфейса, не зависящего от конкретной реализации. Такое требование в дальнейшем позволит легко подключать различные (более эффективные) реализации. В объектно-ориентированных языках такой подход обычно реализуется соответствующими механизмами (наследование, интерфейсы и т.д.), однако язык С, на котором написан сам Snort таких возможностей не предоставляет. Поэтому приведение к единому интерфейсу реализовано по аналогии с бинарными деревьями Snort, в виде макроопределений, которые используются для работы с модулем. Такой подход затрудняет разработку и отладку приложения, но приводит к большему быстродействию конечного продукта.