8.3 KiB
Модель: GPT-5.2 Компания: OpenAI Дата: 07.01.2026 18:11 UTC
Задача
Найти практический вектор Eclipse Attack на сеть Montana (P2P), опираясь на код в /Montana ACP/montana/src/net/.
TL;DR (главная уязвимость)
Заявленная защита “full bootstrap verification (100 peers, 25+ /16 subnets, hardcoded median check)” фактически не исполняется в текущей реализации:
main.rsтолько логирует «Startup verification … Full bootstrap…», но не вызываетStartupVerifier/BootstrapVerifier.Network::start()запускает listener/connector/maintenance и сразу добавляет user-suppliedseedsвAddrMan, после чегоconnection_loopначинает диалить адреса без какого-либо гейта “verification passed”.net/startup.rsсодержит заглушкуquery_hardcoded_tips()→Vec::new(), то есть даже если verifier вызывать, он не сможет набратьMIN_HARDCODED_RESPONSES.
Следствие: Eclipse сводится к классике — poisoning AddrMan + перехват outbound.
Доказательства по коду (ключевые места)
1) Startup verification не выполняется
montana/src/main.rs:
- Есть лог о “Startup verification … Full bootstrap…”, но дальше код сразу делает
Network::new()иnetwork.start()(без вызова verifier).
montana/src/net/protocol.rs:
Network::start()спаунитlistener_loop,connection_loop,maintenance_loop, затем добавляетseedsчерезadd_seed()и возвращаетOk(()).- Никакой проверки, что “startup verification passed”, нет.
2) StartupVerifier — заглушка
montana/src/net/startup.rs:
query_hardcoded_tips()возвращаетVec::new()(placeholder), аtotal_peersв результате =responses.len()с комментарием “Would be 100 in full implementation”.
3) Addr poisoning реально влияет на выбор outbound
montana/src/net/protocol.rs:
connection_loopвыбирает адрес черезaddrman.select()(50/50 NEW vs TRIED) и затем коннектится.
4) TRIED заполняется после handshake (это хорошо), но это делает eclipse ещё проще при отсутствии bootstrapping
montana/src/net/protocol.rs:
- На
Message::Verackвызываетсяaddresses.write().await.mark_connected(&peer.addr);— перевод адреса в TRIED происходит после успешного handshake.
5) Addr rate-limit легко обходится Sybil’ом (лимит per-connection)
montana/src/net/rate_limit.rs:
AddrRateLimiter: burst 1000, sustained 0.1 addr/sec.- Лимитер хранится в
Peer→ лимит на соединение, а не глобальный. - Значит атакующий открывает много соединений (входящих/исходящих) и с каждого отправляет по 1000 адресов → быстро забивает NEW таблицу.
Практическая схема Eclipse Attack (эксплуатация)
Предпосылки
- Жертва запускает узел и имеет хотя бы 1–2 канала к атакующему (например, через
--seedsили просто подключившись к атакующему узлу). - Атакующий контролирует набор IP’ов, желательно распределённых по разным /16 (или IPv6-префиксам так, как считает
get_netgroup).
Шаг 1 — “засорить NEW” адресной книгой атакующего
- Атакующий устанавливает соединение с жертвой и доводит handshake до
Verack(иначе нельзя слатьAddr). - Сразу после handshake отправляет
Addrс максимумом адресов (до 1000 в сообщении). - Повторяет через множество Sybil-соединений: каждый
Peerимеет свойAddrRateLimiter→ снова 1000 burst.
Результат: AddrMan::new_table (1024 buckets × 64) — до ~65k слотов — может быть существенно заполнена адресами атакующего.
Шаг 2 — перевести атакующие адреса в TRIED
- Т.к.
connection_loopвыбирает адреса из AddrMan (50/50 NEW/TRIED) и нет гейта на bootstrap-verification, жертва начнёт диалить адреса из отравленного AddrMan. - Атакующие узлы принимают соединение и корректно отвечают handshake.
- На
Verackадрес переводится вTRIED(mark_connected).
Результат: со временем TRIED (256×64 = 16384 слота) тоже становится “атакующий-доминант”.
Шаг 3 — рестарт жертвы и «полный eclipse»
После рестарта жертва снова будет выбирать peers из AddrMan (частично TRIED) и практически все outbound уйдут на атакующего.
Ограничения и как атакующий их обходит
- MAX_OUTBOUND = 8, MAX_PEERS_PER_NETGROUP = 2 (т.е. максимум 2 исходящих на один /16).
- Для полного перехвата 8 outbound нужно иметь ≥4 разных netgroup (/16) и по 2 адреса в каждом.
- MAX_CONNECTIONS_PER_IP = 2 — не даёт одному IP занять много слотов.
- Но атакующий использует несколько IP.
- Факт отсутствия “100 peers / 25+ subnets / hardcoded median check” делает стоимость атаки на порядки ниже, чем заявлено в комментариях.
Почему это именно Eclipse (а не просто Sybil)
Потому что цель — монополизировать видимость сети жертвы за счёт контроля всех/почти всех outbound + доминирования адресной книги, а не просто “иметь много узлов”. Здесь это достигается через AddrMan poisoning и отсутствие обязательной startup-верификации.
Рекомендации (high level)
- Сделать startup verification реальным гейтом:
- до запуска
connection_loopсобрать 100 ответов, проверить 25+ /16, проверить hardcoded-median, и только затем разрешать outbound.
- до запуска
- Убрать/жёстко ограничить доверие к
--seeds:- сейчас
add_seed()bypass’ит часть проверок и фактически даёт атакующему якорь.
- сейчас
- Глобальный и per-IP/per-netgroup лимит на Addr ingestion:
- текущий лимит per-peer легко обходится множеством соединений.
- (Если включать feelers) Feeler должен проходить protocol handshake, иначе “TCP-only reachability” снова откроет прямой путь к TRIED poisoning.
Приложение: ключевые файлы
montana/src/net/protocol.rs— выбор outbound (AddrMan::select), обработкаAddr, перевод вTRIEDнаVerack.montana/src/net/addrman.rs— NEW/TRIED таблицы, bucket logic.montana/src/net/startup.rsиmontana/src/net/bootstrap.rs— заявленная модель защиты, но сейчас не интегрирована/частично заглушена.montana/src/net/rate_limit.rs— per-peer токенбакеты.