Створення і тестування Firewall в Linux, Частина 2.3. Завершуємо Firewall. Обробляємо трафік в userspace

Зміст першої частини:

Створення лабораторії, архітектура Netfilter, char device, sysfs1.1 — Створення віртуальної лабораторії (щоб нам було де працювати, я покажу як створити віртуальну мережу на вашому комп'ютері. Мережа буде складатися з 3х машин Linux ubuntu).
1.2 – Написання простого модуля в Linux. Введення в Netfilter та перехоплення трафіку з його допомогою. Об'єднуємо всі разом, тестуємо.
1.3 – Написання простого char device. Додавання віртуальної файлової системи — sysfs. Написання user interface. Об'єднуємо всі разом, тестуємо.

Зміст другої частини:

2.1 — Введення у другу частину. Дивимося на мережу і протоколи. Wireshark.
2.2 — Таблиці Firewall. Transport Layer. Структури TCP, UDP. Розширюємо Firewall.
2.3 — Розширюємо функціональність. Обробляємо дані user space. libnetfilter_queue.
2.4 — Бонус. Вивчаємо реальну Buffer Overflow атаку і запобігаємо за допомогою нашого Firewall'а.

Частина 2.3 – Введення
Ми закінчили минулий частина на

if(dest_port == HTTP_PORT || src_port == HTTP_PORT) {
printk("HTTP packet\n");
}

У цій частині ми подивимося, як легко послати пакети з kernel space user space і назад. Для прикладу ми візьмемо саме HTTP з'єднання і додамо блокування окремих сайтів. У бонусної частини ми розглянемо існуючу вразливість і, як приклад, як з нею боротися.

libnetfilter_queue.
До нашого везінню, є вже готове рішення – низькорівнева бібліотека, написана на C, для простого взаємодії з netfilter userspace libnetfilter_queue. Як можна здогадатися з назви, коли ми отримуємо черговий пакет і викликається встановлена нами hook_function, є можливість прийняти пакет (return NF_ACCEPT), викинути (return NF_DROP), а є можливість помістити його в спеціальну черга (queue), просто виконавши:

#define HTTP_QUEUE_NUMBER 1
return NF_QUEUE_NR(HTTP_QUEUE_NUMBER);

HTTP_QUEUE_NUMBER – це певний мною номер черги, куди послати пакет (може бути кілька черг, що зручно для простого поділу пакетів за типами)
NF_QUEUE_NR – це макрос, який необхідно використовувати (він робить побітові зсуви, щоб повернути потрібне число).
За промовчанням, можна написати:

return NF_QUEUE

і тоді всі пакети будуть надіслані в чергу з номером нуль. Тобто єдине, що нам необхідно поміняти в коді, щоб послати всі пакети пов'язані з http протоколом, це додати цю строчку:

if(dest_port == HTTP_PORT || src_port == HTTP_PORT) {
printk("HTTP packet\n");
return NF_QUEUE_NR(HTTP_QUEUE_NUMBER);
}

Тепер необхідно ці пакети отримати user space. І ось тут нам допоможе libnetfilter_queue.

Для того, щоб ним скористатися, необхідно завантажити та встановити потрібні бібліотеки. Докладно про установку – тутвсе необхідне для установки.

Всередині також є простий приклад, а можливі проблеми при установці вирішуються за допомогою пошуковика (у мене потрібно було встановити ще один пакет). Давайте подивимося на найбільш важливі моменти з прикладу. У функції main() (нагадую, що ми зараз user space!) відбуваються різні ініціалізації

int main(int argc, char **argv)
{
...
printf("opening library handle\n");
h = nfq_open();
if (!h) { exit(1); }
...
qh = nfq_create_queue(h, 0, &cb, NULL);
if (!qh) { exit(1); }
...
for (;;) {
if ((rv = recv(fd, buf, sizeof(buf), 0)) >= 0) {
printf("pkt received\n");
nfq_handle_packet(h, buf, rv);
continue;
}
...

Тут важливо вказати правильний номер QUEUE. У прикладі це нуль, але ми його змінюємо на наш qh = nfq_create_queue(h, HTTP_QUEUE_NUMBER, &cb, NULL);
Якщо все ініціалізації пройшли успішно, то ми потрапимо в нескінченний цикл, де будемо чекати вступу нових пакетів з kernel. Коли пакет надходить, викликається функція cb, покажчик на яку ми також передали в nfw_create_queue:

static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,
struct nfq_data *nfa, void *data)
{
u_int32_t id = print_pkt(nfa);
printf("entering callback\n");
return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
}

Функція ж в свою чергу викликає print_pkt, якій передають покажчик на структуру, що містить пакет, який до нас прийшов. Кожен пакет, отримує згідно з порядком вступу – номер id, який повертає print_pkt (на яку ми подивимося ось-ось), і в кінці викликається функція вердикту даного пакету – в даному випадку NF_ACCEPT – прийняти.

Тепер до «серця» приклад – print_pkt:

static u_int32_t print_pkt(struct nfq_data *tb)

В оригінальному прикладі є безліч функцій для доступу до різних даними через nfq_data *tb, ми ж розглянемо доступ до даних на тих рівнях, які нам добре знайомі з попередніх частин – Application, Transport, Network

Ось повний код функції:

static u_int32_t print_pkt(struct nfq_data *tb)
{
int i = 0;
int packet_len = 0;
unsigned char *data = NULL;
int ip_src_array[4] = {0};
int ip_dst_array[4] = {0};

packet_len = nfq_get_payload(tb, &data);

ip_dst_array[3] = data[16];
ip_dst_array[2] = data[17];
ip_dst_array[1] = data[18];
ip_dst_array[0] = data[19];

ip_src_array[3] = data[12];
ip_src_array[2] = data[13];
ip_src_array[1] = data[14];
ip_src_array[0] = data[15];

char ip_dst_str[20] = {0};
char ip_src_str[20] = {0};

ip_hl_to_str(ip_dst_array, ip_dst_str);
ip_hl_to_str(ip_src_array, ip_src_str);

printf("src_ip = %s, dst_ip = %s", ip_src_str, ip_dst_str);

printf("\n");
if( packet_len >= 0x34 ) {
for(i = 0x34; i < packet_len; ++i){
if(data[i] >= '' && data[i] <= '}')
printf("%c", (int)data[i]);
}
}

printf("End of packet checking\n");
return NF_ACCEPT;
}

Ну, тут вже не повинно бути питань.

Компілюємо, перевіряємо:

Так виглядають всі частини, http – сервер який буде прослуховувати трафік http, interface – управління пристроєм, module – наш firewall. Компілюємо http сервер, запускаємо firewall, запускаємо http сервер:



Окремо заходимо host2 і в цей раз для інтересу підключаємося до host1 на порт 80 і посилаємо команду GET, щоб отримати головну сторінку:



Дивимося результати, що пише http сервер. Видно надіслану нами команду GET, а після неї і всю сторінку, яку нам послав host1 у відповідь на прохання.



Тепер вже абсолютно очевидно, як, наприклад, заблокувати доступ до певних сайтів.

Завершення першої і другої частини
Ми почали першу частину з того, що побудували просту мережа з трьох комп'ютерів з операційною системою linux. Далі ми написали простий пристрій, яке вміло рахувати кількість пакетів, які проходять через мережеві карти комп'ютера, а також разрежать і забороняти їх подальшу передачу. Все це відбувалося на рівні ядра операційної системи. Для того, щоб нам було зручно управляти пристроєм і отримувати від нього інформацію, ми додали user interface, які вмів посилати команди нашого пристрою і приймати від нього дані. Так ми отримували статистику в зручній нам формі.

Другу частину ми почали з вивчення мереж і протоколів, дослідивши деталі деяких з них. І далі застосували наші знання, істотно розширивши можливості firewall і отримавши доступ до всієї необхідної інформації, яка міститься в пакеті на нас цікавлять OSI рівнях. Все це відбувається в kernel space.

У цій частині ми послали деякі з пакетів в user space, тим самим значно розширивши зручність написання коду і його функціональність, відкривши для себе можливість використання безлічі доступних готових С\С++ бібліотек. Треба зазначити, що, зробивши це, ми «заплатили» в продуктивності, але при цьому не стали навантажувати ядро операційної системи і переклали аналіз трафіку на користувацьку аплікацію. Тепер робота firewall стала набагато повільніше, але в нашому випадку це не принципово.

Сучасні ж firewall – це дуже складний hardware-software комплекс, де все заточене під швидкість і якість, і який встановлюється на головний вхід у великі мережі і обробляє гігабайти в секунду. Можна уявити, що навіть незначне погіршення продуктивності (наприклад, обробки пакета) в гігабайтах буде обходитися дуже дорого. Тому в них постійно шукають слабкі ланки і працюють над поліпшенням їх продуктивності. Крім того, в завдання сучасних firewall входить не тільки перевірка правил, але і набагато більш складні завдання: відображення різних типів DOS атак, вірусів, запобігання витоку інформації, спроб злому комп'ютерів, доступу до підозрілих сайтів і скачування підозрілого контенту. Найпростіший приклад як бонусної статті ми розглянемо в останній частині – завершальної.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.