# Отчет об анализе уязвимостей Eclipse Attack — Montana Network **Модель:** Composer 1 **Компания:** Cursor **Дата:** 07.01.2026 21:09 UTC --- ## Резюме Проведен глубокий анализ сетевого слоя Montana на предмет уязвимостей Eclipse Attack. Обнаружены **5 критических уязвимостей**, которые позволяют злоумышленнику изолировать узел жертвы и контролировать все его исходящие соединения. **Критичность:** 🔴 **КРИТИЧЕСКАЯ** --- ## Обнаруженные уязвимости ### 1. 🔴 КРИТИЧЕСКАЯ: Startup Verification не выполняется **Файл:** `Montana ACP/montana/src/net/startup.rs` **Проблема:** Функция `query_hardcoded_tips()` в `startup.rs` является заглушкой и возвращает пустой вектор без выполнения реальных запросов: ```165:179:Montana ACP/montana/src/net/startup.rs /// Query hardcoded nodes for their chain tips async fn query_hardcoded_tips(&self, addrs: &[SocketAddr]) -> Vec { // This is a simplified implementation // Real implementation would: // 1. Connect to each hardcoded node // 2. Complete handshake // 3. Get their best slice height // 4. Disconnect // For now, return empty - actual network queries would be done // by the Network module when it starts info!("Querying {} hardcoded nodes for chain tips", addrs.len()); // Placeholder: in production, this would make actual network requests // The Network module handles this during start() Vec::new() } ``` **Эксплуатация:** 1. Узел запускается и вызывает `verification_type()`, который возвращает строку "full_bootstrap" 2. В `main.rs` верификация только логируется, но не выполняется и не блокирует запуск: ```84:90:Montana ACP/montana/src/main.rs let verify_type = verification_type(&storage); let chain_age = storage.chain_age_secs().unwrap_or(u64::MAX); let head = storage.head().unwrap_or(0); info!("Startup verification: {} (chain_age={} secs, height={})", verify_type, chain_age, head); info!("Full bootstrap: 100 peers, 25+ subnets required"); ``` 3. Сеть запускается без реальной верификации 4. AddrMan загружается из `addresses.dat` без проверки консенсуса 5. Все исходящие соединения идут к адресам из AddrMan, которые могут быть скомпрометированы **Последствия:** - Полный обход защиты от Eclipse Attack - Узел не проверяет консенсус при запуске - Атакующий может заполнить `addresses.dat` вредоносными адресами заранее **Рекомендации:** 1. Реализовать реальные сетевые запросы в `query_hardcoded_tips()` 2. Вызвать `StartupVerifier::verify()` в `main.rs` и заблокировать запуск при неудаче 3. Добавить проверку результата верификации перед запуском сети --- ### 2. 🔴 КРИТИЧЕСКАЯ: AddrMan можно заполнить через Addr сообщения без проверки разнообразия **Файл:** `Montana ACP/montana/src/net/protocol.rs` **Проблема:** При обработке `Addr` сообщений нет проверки разнообразия подсетей. Атакующий может отправить множество адресов из одной подсети: ```984:1000:Montana ACP/montana/src/net/protocol.rs Message::Addr(addrs) => { if addrs.len() > MAX_ADDR_SIZE { return Err(NetError::Protocol("Too many addresses".into())); } // Rate limit: process only allowed addresses let allowed = peer.rate_limits.addr.process(addrs.len()); if allowed == 0 { debug!("Rate limited addr from {}", peer.addr); return Ok(false); } // Only process up to allowed count let to_process: Vec<_> = addrs.into_iter().take(allowed).collect(); let added = addresses.write().await.add_many(to_process, peer.addr); debug!("Added {} addresses from {} (rate limited to {})", added, peer.addr, allowed); } ``` **Эксплуатация:** 1. Атакующий подключается к жертве (inbound или outbound) 2. Отправляет `Addr` сообщения с адресами из контролируемых подсетей 3. Адреса добавляются в NEW table AddrMan без проверки разнообразия 4. При следующем рестарте (если верификация обойдена), все исходящие соединения идут к атакующему **Последствия:** - Заполнение AddrMan вредоносными адресами - При рестарте узел подключается только к атакующему - Rate limiting не защищает от постепенного заполнения **Рекомендации:** 1. Добавить проверку разнообразия подсетей при добавлении адресов 2. Ограничить количество адресов из одной подсети от одного источника 3. Проверять разнообразие подсетей в AddrMan перед выбором пиров --- ### 3. 🔴 КРИТИЧЕСКАЯ: Netgroup diversity работает только для outbound соединений **Файл:** `Montana ACP/montana/src/net/connection.rs` **Проблема:** Проверка `can_connect()` ограничивает только исходящие соединения. Для входящих соединений проверка выполняется, но атакующий может подключиться через inbound и заполнить AddrMan: ```271:277:Montana ACP/montana/src/net/connection.rs /// Check if we can connect to this address (netgroup diversity) pub async fn can_connect(&self, addr: &SocketAddr) -> bool { let netgroup = get_netgroup(addr); let counts = self.netgroup_counts.lock().await; let current = counts.get(&netgroup).copied().unwrap_or(0); current < MAX_PEERS_PER_NETGROUP } ``` **Эксплуатация:** 1. Атакующий подключается через inbound (до 117 соединений) 2. Каждое соединение может быть из разных IP, но из одной подсети (до 2 соединений на подсеть) 3. Атакующий отправляет `Addr` сообщения с адресами из контролируемых подсетей 4. AddrMan заполняется вредоносными адресами 5. При рестарте все outbound соединения идут к атакующему **Последствия:** - Inbound соединения могут заполнить AddrMan без ограничений по разнообразию - Атакующий может использовать множество IP из одной подсети для обхода ограничений **Рекомендации:** 1. Применить проверку разнообразия подсетей также для адресов, полученных через inbound соединения 2. Ограничить количество адресов из одной подсети в AddrMan 3. Проверять разнообразие подсетей при добавлении адресов в AddrMan --- ### 4. 🟠 ВЫСОКАЯ: Bucket collision в TRIED table может быть использована для атаки **Файл:** `Montana ACP/montana/src/net/addrman.rs` **Проблема:** При коллизии в TRIED table существующий адрес перемещается обратно в NEW table. Это может быть использовано для вытеснения легитимных адресов: ```210:222:Montana ACP/montana/src/net/addrman.rs // Add to tried let bucket = self.get_tried_bucket(addr); let pos = self.get_bucket_position(addr, bucket, false); let idx = bucket * BUCKET_SIZE + pos; // Handle collision if let Some(existing_idx) = self.tried_table[idx] { // Move existing back to new self.move_to_new(existing_idx); } self.tried_table[idx] = Some(addr_idx); self.tried_count += 1; } ``` **Эксплуатация:** 1. Атакующий вычисляет bucket для легитимного адреса в TRIED table 2. Создает адрес, который попадает в тот же bucket и позицию 3. Устанавливает успешное соединение с этим адресом 4. Легитимный адрес вытесняется из TRIED в NEW 5. При следующем выборе пира вероятность выбрать легитимный адрес снижается **Последствия:** - Вытеснение легитимных адресов из TRIED table - Снижение разнообразия подсетей в TRIED table - Увеличение вероятности выбора вредоносных адресов **Рекомендации:** 1. При коллизии в TRIED table не перемещать существующий адрес, а отклонить новый 2. Или использовать более сложную стратегию вытеснения (например, на основе возраста адреса) 3. Добавить проверку разнообразия подсетей при перемещении в TRIED --- ### 5. 🟠 ВЫСОКАЯ: Отсутствие проверки разнообразия подсетей при выборе пиров из AddrMan **Файл:** `Montana ACP/montana/src/net/addrman.rs` **Проблема:** Метод `select()` выбирает адреса случайным образом без проверки разнообразия подсетей: ```233:258:Montana ACP/montana/src/net/addrman.rs /// Select an address to connect to (50/50 new vs tried) pub fn select(&mut self) -> Option { self.select_inner(false) } /// Select an address to connect to with option for new only pub fn select_with_option(&mut self, new_only: bool) -> Option { self.select_inner(new_only) } fn select_inner(&mut self, new_only: bool) -> Option { let mut rng = ChaCha20Rng::from_entropy(); // 50% chance to try new address (or 100% if new_only) let use_new = new_only || rng.gen_bool(0.5); if use_new && self.new_count > 0 { self.select_from_new(&mut rng) } else if self.tried_count > 0 { self.select_from_tried(&mut rng) } else if self.new_count > 0 { self.select_from_new(&mut rng) } else { None } } ``` **Эксплуатация:** 1. Атакующий заполняет AddrMan адресами из ограниченного набора подсетей 2. При выборе пиров `select()` может выбрать несколько адресов из одной подсети подряд 3. `can_connect()` блокирует только после установления соединения 4. Атакующий может контролировать большинство исходящих соединений **Последствия:** - Неравномерное распределение соединений по подсетям - Возможность контроля большинства исходящих соединений при заполнении AddrMan **Рекомендации:** 1. Добавить проверку разнообразия подсетей в `select()` перед возвратом адреса 2. Отслеживать уже выбранные подсети в текущем цикле выбора 3. Приоритизировать адреса из новых подсетей --- ## Общий вектор атаки ### Сценарий успешной Eclipse Attack: 1. **Подготовка:** - Атакующий получает доступ к `addresses.dat` жертвы (через malware, компрометацию сервера, или заполнение через Addr сообщения) - Заполняет NEW table адресами из контролируемых подсетей (1024 buckets × 64 entries = до 65,536 адресов) 2. **Заполнение AddrMan:** - Подключается к жертве через inbound соединения - Отправляет `Addr` сообщения с адресами из контролируемых подсетей - Адреса добавляются в NEW table без проверки разнообразия 3. **Продвижение в TRIED:** - Устанавливает успешные соединения с вредоносными адресами - Адреса перемещаются в TRIED table через `mark_connected()` - Легитимные адреса могут быть вытеснены через bucket collision 4. **Рестарт жертвы:** - Startup verification не выполняется (уязвимость #1) - AddrMan загружается из `addresses.dat` с вредоносными адресами - Все исходящие соединения идут к атакующему 5. **Изоляция:** - Жертва изолирована от легитимной сети - Атакующий контролирует всю информацию, получаемую жертвой - Возможны атаки на консенсус, двойное расходование и т.д. --- ## Защитные механизмы и их обход ### Защита: Full Bootstrap Verification **Статус:** ❌ **НЕ РАБОТАЕТ** - Код существует, но не выполняется при запуске - `query_hardcoded_tips()` возвращает пустой вектор - Верификация не блокирует запуск сети ### Защита: Netgroup Diversity (MAX_PEERS_PER_NETGROUP=2) **Статус:** ⚠️ **ЧАСТИЧНО РАБОТАЕТ** - Работает только для outbound соединений - Не защищает от заполнения AddrMan через inbound - Не проверяется при добавлении адресов в AddrMan ### Защита: Rate Limiting на Addr сообщения **Статус:** ⚠️ **НЕДОСТАТОЧНО** - Ограничивает количество адресов за раз - Не защищает от постепенного заполнения - Не проверяет разнообразие подсетей ### Защита: Bucket System в AddrMan **Статус:** ⚠️ **СЛАБАЯ** - Предотвращает предсказуемое размещение адресов - Но не защищает от заполнения всех buckets вредоносными адресами - Collision handling может быть использован для вытеснения легитимных адресов --- ## Рекомендации по исправлению ### Приоритет 1 (Критический): 1. **Реализовать реальную startup verification:** - Выполнять реальные сетевые запросы к hardcoded nodes - Блокировать запуск сети при неудаче верификации - Проверять консенсус перед загрузкой AddrMan 2. **Добавить проверку разнообразия подсетей в AddrMan:** - Ограничить количество адресов из одной подсети - Проверять разнообразие при добавлении адресов - Приоритизировать адреса из новых подсетей при выборе ### Приоритет 2 (Высокий): 3. **Улучшить bucket collision handling:** - Не перемещать существующие адреса из TRIED при коллизии - Использовать более сложную стратегию вытеснения 4. **Добавить проверку разнообразия в select():** - Отслеживать уже выбранные подсети - Приоритизировать адреса из новых подсетей ### Приоритет 3 (Средний): 5. **Усилить rate limiting:** - Добавить проверку разнообразия подсетей в rate limiter - Ограничить количество адресов из одной подсети от одного источника 6. **Добавить мониторинг:** - Логировать предупреждения при низком разнообразии подсетей - Алерты при подозрительных паттернах заполнения AddrMan --- ## Заключение Сетевой слой Montana содержит **критические уязвимости**, которые позволяют злоумышленнику выполнить успешную Eclipse Attack. Основная проблема заключается в том, что **startup verification не выполняется**, что полностью обходит защиту от Eclipse Attack. Дополнительные уязвимости позволяют заполнить AddrMan вредоносными адресами и контролировать все исходящие соединения жертвы. **Рекомендуется немедленное исправление всех обнаруженных уязвимостей.** --- ## Приложение: Код уязвимостей ### Уязвимость #1: Startup Verification не выполняется ```rust // Montana ACP/montana/src/net/startup.rs:165-179 async fn query_hardcoded_tips(&self, addrs: &[SocketAddr]) -> Vec { // Placeholder: in production, this would make actual network requests Vec::new() // ❌ Возвращает пустой вектор } ``` ### Уязвимость #2: AddrMan заполняется без проверки разнообразия ```rust // Montana ACP/montana/src/net/protocol.rs:998 let added = addresses.write().await.add_many(to_process, peer.addr); // ❌ Нет проверки разнообразия подсетей ``` ### Уязвимость #3: Netgroup diversity только для outbound ```rust // Montana ACP/montana/src/net/connection.rs:272-277 pub async fn can_connect(&self, addr: &SocketAddr) -> bool { // ⚠️ Проверяется только при outbound соединениях // ❌ Не защищает от заполнения AddrMan через inbound } ``` --- **Конец отчета**