Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Sunday, February 1, 2009

Автостопом по крипто апи.



Пребью
Криптографии в разработке системных и не только приложениях принято уделять повышенное внимание. Особенно сейчас, когда набитость сундуков многих благородных сэров, как и степень их свободы, напрямую зависит от степени конфеденциальости их информации. Чтобы секономить время и не ходить лишний раз к проктологу восьпользуемся тем что, большинство стойких и эффективных алгоритмов уже реализованно професионалами этого дела. Плод их трудов открыто предоставляется на комерческой и бесплатной основе. Разработчики операционных систем тоже не сидят сложа руки предоставляя разработчикам специальные API. В этой статье мы хотим рассказать об исползования таких API в модуле ядра для операционной системы Linux, котороый будет шифровать исходящий трафик и расшифровывать входящий.

Летс гоу.
А начнем пожалуй с подготовки системы. Для начала надо убедится что система готова к созданию разного рода приложений, а конкретно к разработке модулей ядра. Помимо присутсвия GCC надо еще убедиться что ядро было скомпилированно с поддержкой модульности и криптографических API. Про установку GCC и включение модульности написано много мануалов поэтому не будем тратить ни времени ни чернил, в информации по созданию модулей недостатка тоже не чувствуется, так что приступим сразу к делу. Крипто API включаются выставлением параметров CONFIG_CRYPTO* в конфигурационном файде ядра (/usr/src/.config). А самый легкий способ проверки их доступности это попытаться скомпилировать пример который находится все в техже исходниках ядра. Заходим в папку с иходниками ядра, дальше как наверное не трудно догадаться в дирректорию crypto и компилируем tcrypt.c. Если он компелируется без фатальных ошибок и даже работает, то собираемся с духом и идем дальше.

Вот из ит.
Теперь надо немного разобраться чтоже всетаки мы подключили и где это найти. Исходники этого всего дела лежат в тойже директории что и упомянутый выше tcrypt.c, а сам он является примером в котором показано как использовать все реализованные в Cryptographic API алгоритмы. Пример этот хорошо коментирован, а если что-то остается не совсем понятным то всегда можно заглянуть в лежащие рядом исходники самих Cryptographic API. Но как говорится сказать или даже написать проще чем сделать. На практике довольно часто можно свалиться в штопор в самых неожиданных местах, и потом довольно долго по частям собирать то что всего пять минут назад летело со скоростью звука. К сожалению ошибки всегда банальны, но постоянны, и каждый раз вспоминать как приклеивается один и тоже кусок становится утомительным. Сделать один раз инструкцию и современем её дополнять гораздо эффективнее и проще. Относительно наших API можно сказать тоже самое. Нормального мана на русском языке по ним нам найти не удалось, а повторно вкуривать одну и туже траву это не тру. Поэтому хотим оставить своего рода рабочие записки и надеемся что это еще комунибудь поможет.

Литл море
Предположим мы хотим реализвать немного устаревший но достаточно быстрый и провереный алгоритм DES. У этого алгоритма существует несколько режимов:

* ECB (Electronic Code Book) - режим все блоки шифруются независимо друг от друга и не сцепляются;
* CBC (Cipher Block Chaining)- режим в котором результат шифрования предыдущего блока используется для шифрации текущего.

Под блоками тут подразумевается часть шифротекста. Так с виду один и тот же алгоритм может быть реализован в разных режимах. Если кому интересно, то в качестве домашнего задания можно еще самостоятельно покурить CFB и OFB.
Однако, мы отвлеклись от темы. Для своих целей выберем режим CBC как более надежный и приступим сделав наброски будующего кода.
Как это часто бывает начнем с написания и описания ключевых структур и переменных.

struct completion comp;
struct scatterlist sg[8];
struct crypto_ablkcipher *tfm;
struct ablkcipher_request *req;


Переменная comp служит для синхронизации выполнения между нашей функцией и непосредственно шифрованием. Суть этого станет ясна чуть ниже. А пока рассмотрим саму структуру. Она объявлена в include/linux/completion.h и содержит всего два поля:
struct completion {
unsigned int done;
wait_queue_head_t wait;
};

Первое это флаг выполнения, а второе указатель на очередь ожидающих задач.

Следующая переменная sg описана в include/linux/scatterlist.h. Это структура платформо зависимая, и на i386 платформе она определена следующим образом:
struct scatterlist {
struct page *page;
unsigned int offset;
dma_addr_t dma_address;
unsigned int length;
};

Заполнение полей этой структуры вполне можно поручить функции sg_init_one(). В этой функции определяется страница памяти, с которой "начинается" buf, и определяется смещение указателя buf относительно адреса начала страницы.
tfm и есть наша основная структура, в файле include/linux/crypto.h находится ее описание
struct crypto_ablkcipher {
struct crypto_tfm base;
};


Не очень информативно поэтому посмотрим чуть выше, где находится
struct crypto_tfm {
u32 crt_flags;
union {
struct ablkcipher_tfm ablkcipher;
struct aead_tfm aead;
struct blkcipher_tfm blkcipher;
struct cipher_tfm cipher;
struct hash_tfm hash;
struct compress_tfm compress;
} crt_u;
struct crypto_alg *__crt_alg;
void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
};

Уже что-то неправдали? первым полем идут флаги, о некоторых из них расскажем чуть ниже. Далее объединение более низкуровневых структур для разных типов криптографических задач, прямой доступ к которым использовался в предыдущих версиях. В crypto_alg содержится указатель на структуру. в которой есть все необходимое для корректной работы непосредственно алгоритма шифрования. А именно имя самого алгоритма, приоритет и т.д. /*про это можно подробнее отсюда http://diploma-thesis.siewior.net/html/diplomarbeitch4.html*/

Следующая переменная req содержит указатель на структуру запросов struct ablkcipher_request.
struct ablkcipher_request {
struct crypto_async_request base;
unsigned int nbytes;
void *info;

struct scatterlist *src;
struct scatterlist *dst;

void *__ctx[] CRYPTO_MINALIGN_ATTR;
};


С помошью этой прменной мы обращамся к страницам памяти где находится шифрованный и расшифрованый текст.

Когда мы разобрались с основными переменными можно приступать к написанию самого кода. Начнем с инициализации переменной comp с помощью функции init_completion().
init_completion(&comp);

Эта функция обнулят флаг done, и добвляет comp в голову очереди wait_queue_head_t, о которых рассказывалось выше. Подробнее о механизме работы этой структуры и о том для чего мы ее используем будет рассказано еще чуть ниже ;).

Теперь выделим память дла нашей оновной пременной.
tfm = crypto_alloc_ablckhipher ("cbc(des)", 0, CRYPO_TFM_REQ_WEAK_KEY);

В качестве первого параметра мы передаем имя алгоритма, второй параметр это тип шифрования (вспомните объединение crt_u в опиании структуры), третьим параметром мы передам флаги, переданный флаг означает что алгоритм должен принимать даже слабые ключи.

Далее выделям память для ablkcipher_request
req = ablkcipher_request_alloc (tfm, GFP_KERNEL);

С первым параметром думаю все понятно и так. Непосредственно память под структуру выделяется функцией kmalloc, которая в качестве параметра принимает флаг, говорящий о том как именно стоит выделять память. Флаг GFP_KERNEL (GFP - Get Free Page) резервирует блок памяти, выделяя страницы памяти по мере обращения к ним. Существует и другие флаги например:
GFP_ATOMIC выделяет требуемую память немедленно (при необходимости вытесняя другие страницы на диск);
GFP_BUFFER никогда не вытесняет другие страницы, и если запрошенная память недоступна, с выделением наступает облом.
Фактически приходится выбирать между GFP_ATOMIC и GFP_KERNEL. Обычно используют GFP_KERNEL, так как он ведет себя не столь агрессивно.

Потом установим ключ которым будем шифровать
ret = crypto_ablkcopher_sekey (tfm, key, strlen(key));

Эта функция возвращает 0 в случае успеха и код ошибки больший нуля в случа неудачи. К првому параметру думаю опять не возникает никаких вопросов, да и с остальными ничего сложного: второй - указатель на строку с ключем, третий - длина.

Теперь позаботимся о расположении текста который будем шифровать
sg_init_one (&sg[0], text, strlen(text));

Стоит отметить что текст должен быть кратен восьми, иначе при попытке зашифровать\дешифровать получите ошибку -21 (что означает «некорректные входные данные»). То как с этим боремся мы можно посмотреть в приложенном коде. Сама же функция просто заполняет страницы памяти текстом, который собираемся зашифровать или дешифровать :).

Подготовим запрос для шифрования/дешифрования сообщив ему с какими страницами и текстом какой длины предстоит работать
u8  iv = {0xff, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10};
ablkcipher_request_set_crypt (req, sg, sg, strlen(text), iv);

Последним параметром передается инициализирующий вектор, который также необходим для шифрования и расшифрования и служит своего рода открытым ключем. Его можно вычислять каким либо образом от самого ключа, просить пользоватля вводить его или делать постоянным как в нашем случае для упрощения алгоритма.

Теперь приступаем непосредствнно к шифрованию/дешифрованию
ret = enc ? crypto_ablkcipher_encrypt (req) : crypto_ablkcipher_decrypt(req);

Сами функции и их параметр в пояснении думаю не нуждаются, если не понятно перечтайте еще раз внимательно о req. Ну с ret все совсем понятно скажите вы. А вот и нет :). Да ret в случае удачного выполнения действительно будет равна 0. Но вот остальные значения не всегда означают ошибку.

Теперь пришло время рассказать совсем подробно о роли переменной comp и этом механизме.
switch (ret){
case 0:
break;

case -EINPROGRESS: case -EBUSY:
printk ("\nwait\n");
ret = wait_for_completion_interruptible (&comp);
if (!ret) {
INIT_COMPLETION(comp);
break;
}

default:
printk ("failred err=%d", -ret);
goto out;
}

Вспомните описание структуры completion, непонятные слова которые мы говорили о флаге выполнения и очереди задач. Дело в том что криптоалгоритмы не так быстры как хотелось бы. Но как же так скажите вы как мы можем вернутся в вызвавщую функцию до того как завершилась вызываемая. Дело в том что авторы этой библиотеки очень хитрые люди и оптимизировали работу криптоалгоритмов в расчете на распаралеливание вашего алгоритма и с учетом что к шифруемым дешифруемым двнным будут обращатся другие алгоритмы. Поэтому и получилось так что мы можем вернутся в вызвавшую функцию до того как шифрование/дешифрование завершится. Для того чтобы избежать обращения к еще не зашифрованным/ не расшифрованым данным мы и используем структуру completion. Механизм работы этой структуры и функций с ним связанным идентичен механизму работы блокирующих семафоров. Когда работа криптоалгоритма будет завершена он выставит поле done в 1 и наша функция продолжит работу. Если вы знакомы с семафорами то вопросов не возникнет, если нет то настоятельно рекомендуем познакомится.

Далее можно для проверки распечатать зашифрованый/дешифрованный текст или делать с ним все что угодно.

Дуй ит нау
Сам модуль можно найти здесь. Код подробно прокоментирован и не должен вызывать затруднений. По сути он добалвляет свою фукцию (nethook) по обработке полученных и отправленных пакетов на определенное устройство, символьное имя которого задается параметром device_name. А за шифрование/дешифрование отвечает функция my_des, в ней пременено все о чем мы писали выше. Инструкция по сборке и установке лежит рядом. Дерзайте ;)

Sunday, April 13, 2008

Ядерный подход к безопасности Wi-Fi сетей


Введение.
В настоящее время тема безопасности в Wi-Fi сетях чрезвычайно популярна. В сети существует огромное количество статей посвященных тому как легко ломается WEP и тому как быть с WPA. Но практически нет статей посвященных тому как же все таки защитится от этого. Мы в этой статье постараемся поделится нашими исследованиями в этой области.
Замечание: тех кто не видит ничего страшного в том что ваш сосед скачает за ваш счет пару клипов, просим вспомнить о таких вещах как снифинг, рассылка спама от вашего имени, компрометация точки доступа, создание фиктивных DNS серверов и прочих веселостях.
Страшный сон вардрайвера

Представьте себе что вы начитались всех этих статей о безопасности. И вот вы выходите в поле вооруженные Kismet'ом, Aircrack'ом, Ettercap'ом и прочими могучими вещами. Вы находите свою жертву, сеть вашего соседа Васи Пупкина. Все идет по сценарию, вы получаете ключ и подключаетесь к сети. Пинг есть! Все отлично и замечательно... Только вот работать с этой сетью вы не можете, вообще никак... вы не можете выйти в интернет, хотя traceroute показывает весь путь. Вы не можете залезть на шару к Васе Пупкину хотя пинг есть, вы не можете перехватить ни одного пароля.
Вот и любой нормальный человек на вашем месте подумает "Что за фигня?.."
Что это была за фигня.
Реализовать весь этот кошмар не только возможно но и достаточно просто. Необходимо на все машины в сети поставить фильтр-драйвер aka stelth patch, который будет перехватывать пакеты на сетевом уровне, брать полезную нагрузку aka data этого пакета и преобразовывать ее по заданному вами алгоритму. Почему именно сетевой уровень? Дело в том что на этом уровне контрольная сумма считается только для заголовка, позволяя нам преобразовывать данные на свой вкус. (Конечно контрольная сумма считается через CRC32 который вполне возможно сломать. но мы думаем что это стает темой следующей статьи. Пока нам хватит и этого)
Реализация.
Здесь описывается создание специального модуля ядра для Unix - like ОС.
Почему для линукс? Ну во - первых потому что ядро основано на модульном принципе и с этими модулями довольно просто обращаться; во - вторых это открытые исходные коды которые помогли разобраться с взаимодействием системных сокетов с ядром; в третьих в никсах подобные действия не называются мерзким хаком и не преследуются уголовно.
Модуль ядра представляет собой специальным образом откомпилированную программу. На нее налагаются определенные ограничения подробнее о которых можно найти в сети.
То что у нас получилось можно скачать здесь. Алгоритм кодирования самый простой, но его естественно можно заменить. Для того чтобы настроить и запустить модуль необходимо в /etc/ip_hack.conf прописать имя своего беспроводного устйства и запустить /etc/init.d/ip_hack. Вот и все настройки ;)
В качестве примечания наверно стоит сказать что модуль должен находится как на стороне рабочей станции так и на точке доступа. Практически на всех точках доступа в качастве операционной системы стоит *nix. И естественно так как в каждой системе рано или поздно найдется баг в каждой точке есть возможность смены системы (обычно ето делается перепрошивкой). Зная как перепрошить свою точку доступа и имея на неё прошивку вы можете самостоятельно внедрить в прошивку свой модуль и залить её на девайс.

P.S.
О том как перепрошить точку доступа постараемся рассказать в одной из будущих сатей.