20 KiB
Отчет об анализе уязвимостей 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 является заглушкой и возвращает пустой вектор без выполнения реальных запросов:
/// Query hardcoded nodes for their chain tips
async fn query_hardcoded_tips(&self, addrs: &[SocketAddr]) -> Vec<PeerChainInfo> {
// 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()
}
Эксплуатация:
- Узел запускается и вызывает
verification_type(), который возвращает строку "full_bootstrap" - В
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");
- Сеть запускается без реальной верификации
- AddrMan загружается из
addresses.datбез проверки консенсуса - Все исходящие соединения идут к адресам из AddrMan, которые могут быть скомпрометированы
Последствия:
- Полный обход защиты от Eclipse Attack
- Узел не проверяет консенсус при запуске
- Атакующий может заполнить
addresses.datвредоносными адресами заранее
Рекомендации:
- Реализовать реальные сетевые запросы в
query_hardcoded_tips() - Вызвать
StartupVerifier::verify()вmain.rsи заблокировать запуск при неудаче - Добавить проверку результата верификации перед запуском сети
2. 🔴 КРИТИЧЕСКАЯ: AddrMan можно заполнить через Addr сообщения без проверки разнообразия
Файл: Montana ACP/montana/src/net/protocol.rs
Проблема:
При обработке Addr сообщений нет проверки разнообразия подсетей. Атакующий может отправить множество адресов из одной подсети:
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);
}
Эксплуатация:
- Атакующий подключается к жертве (inbound или outbound)
- Отправляет
Addrсообщения с адресами из контролируемых подсетей - Адреса добавляются в NEW table AddrMan без проверки разнообразия
- При следующем рестарте (если верификация обойдена), все исходящие соединения идут к атакующему
Последствия:
- Заполнение AddrMan вредоносными адресами
- При рестарте узел подключается только к атакующему
- Rate limiting не защищает от постепенного заполнения
Рекомендации:
- Добавить проверку разнообразия подсетей при добавлении адресов
- Ограничить количество адресов из одной подсети от одного источника
- Проверять разнообразие подсетей в AddrMan перед выбором пиров
3. 🔴 КРИТИЧЕСКАЯ: Netgroup diversity работает только для outbound соединений
Файл: Montana ACP/montana/src/net/connection.rs
Проблема:
Проверка can_connect() ограничивает только исходящие соединения. Для входящих соединений проверка выполняется, но атакующий может подключиться через inbound и заполнить AddrMan:
/// 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
}
Эксплуатация:
- Атакующий подключается через inbound (до 117 соединений)
- Каждое соединение может быть из разных IP, но из одной подсети (до 2 соединений на подсеть)
- Атакующий отправляет
Addrсообщения с адресами из контролируемых подсетей - AddrMan заполняется вредоносными адресами
- При рестарте все outbound соединения идут к атакующему
Последствия:
- Inbound соединения могут заполнить AddrMan без ограничений по разнообразию
- Атакующий может использовать множество IP из одной подсети для обхода ограничений
Рекомендации:
- Применить проверку разнообразия подсетей также для адресов, полученных через inbound соединения
- Ограничить количество адресов из одной подсети в AddrMan
- Проверять разнообразие подсетей при добавлении адресов в AddrMan
4. 🟠 ВЫСОКАЯ: Bucket collision в TRIED table может быть использована для атаки
Файл: Montana ACP/montana/src/net/addrman.rs
Проблема:
При коллизии в TRIED table существующий адрес перемещается обратно в NEW table. Это может быть использовано для вытеснения легитимных адресов:
// 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;
}
Эксплуатация:
- Атакующий вычисляет bucket для легитимного адреса в TRIED table
- Создает адрес, который попадает в тот же bucket и позицию
- Устанавливает успешное соединение с этим адресом
- Легитимный адрес вытесняется из TRIED в NEW
- При следующем выборе пира вероятность выбрать легитимный адрес снижается
Последствия:
- Вытеснение легитимных адресов из TRIED table
- Снижение разнообразия подсетей в TRIED table
- Увеличение вероятности выбора вредоносных адресов
Рекомендации:
- При коллизии в TRIED table не перемещать существующий адрес, а отклонить новый
- Или использовать более сложную стратегию вытеснения (например, на основе возраста адреса)
- Добавить проверку разнообразия подсетей при перемещении в TRIED
5. 🟠 ВЫСОКАЯ: Отсутствие проверки разнообразия подсетей при выборе пиров из AddrMan
Файл: Montana ACP/montana/src/net/addrman.rs
Проблема:
Метод select() выбирает адреса случайным образом без проверки разнообразия подсетей:
/// Select an address to connect to (50/50 new vs tried)
pub fn select(&mut self) -> Option<NetAddress> {
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<NetAddress> {
self.select_inner(new_only)
}
fn select_inner(&mut self, new_only: bool) -> Option<NetAddress> {
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
}
}
Эксплуатация:
- Атакующий заполняет AddrMan адресами из ограниченного набора подсетей
- При выборе пиров
select()может выбрать несколько адресов из одной подсети подряд can_connect()блокирует только после установления соединения- Атакующий может контролировать большинство исходящих соединений
Последствия:
- Неравномерное распределение соединений по подсетям
- Возможность контроля большинства исходящих соединений при заполнении AddrMan
Рекомендации:
- Добавить проверку разнообразия подсетей в
select()перед возвратом адреса - Отслеживать уже выбранные подсети в текущем цикле выбора
- Приоритизировать адреса из новых подсетей
Общий вектор атаки
Сценарий успешной Eclipse Attack:
-
Подготовка:
- Атакующий получает доступ к
addresses.datжертвы (через malware, компрометацию сервера, или заполнение через Addr сообщения) - Заполняет NEW table адресами из контролируемых подсетей (1024 buckets × 64 entries = до 65,536 адресов)
- Атакующий получает доступ к
-
Заполнение AddrMan:
- Подключается к жертве через inbound соединения
- Отправляет
Addrсообщения с адресами из контролируемых подсетей - Адреса добавляются в NEW table без проверки разнообразия
-
Продвижение в TRIED:
- Устанавливает успешные соединения с вредоносными адресами
- Адреса перемещаются в TRIED table через
mark_connected() - Легитимные адреса могут быть вытеснены через bucket collision
-
Рестарт жертвы:
- Startup verification не выполняется (уязвимость #1)
- AddrMan загружается из
addresses.datс вредоносными адресами - Все исходящие соединения идут к атакующему
-
Изоляция:
- Жертва изолирована от легитимной сети
- Атакующий контролирует всю информацию, получаемую жертвой
- Возможны атаки на консенсус, двойное расходование и т.д.
Защитные механизмы и их обход
Защита: 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 (Критический):
-
Реализовать реальную startup verification:
- Выполнять реальные сетевые запросы к hardcoded nodes
- Блокировать запуск сети при неудаче верификации
- Проверять консенсус перед загрузкой AddrMan
-
Добавить проверку разнообразия подсетей в AddrMan:
- Ограничить количество адресов из одной подсети
- Проверять разнообразие при добавлении адресов
- Приоритизировать адреса из новых подсетей при выборе
Приоритет 2 (Высокий):
-
Улучшить bucket collision handling:
- Не перемещать существующие адреса из TRIED при коллизии
- Использовать более сложную стратегию вытеснения
-
Добавить проверку разнообразия в select():
- Отслеживать уже выбранные подсети
- Приоритизировать адреса из новых подсетей
Приоритет 3 (Средний):
-
Усилить rate limiting:
- Добавить проверку разнообразия подсетей в rate limiter
- Ограничить количество адресов из одной подсети от одного источника
-
Добавить мониторинг:
- Логировать предупреждения при низком разнообразии подсетей
- Алерты при подозрительных паттернах заполнения AddrMan
Заключение
Сетевой слой Montana содержит критические уязвимости, которые позволяют злоумышленнику выполнить успешную Eclipse Attack. Основная проблема заключается в том, что startup verification не выполняется, что полностью обходит защиту от Eclipse Attack.
Дополнительные уязвимости позволяют заполнить AddrMan вредоносными адресами и контролировать все исходящие соединения жертвы.
Рекомендуется немедленное исправление всех обнаруженных уязвимостей.
Приложение: Код уязвимостей
Уязвимость #1: Startup Verification не выполняется
// Montana ACP/montana/src/net/startup.rs:165-179
async fn query_hardcoded_tips(&self, addrs: &[SocketAddr]) -> Vec<PeerChainInfo> {
// Placeholder: in production, this would make actual network requests
Vec::new() // ❌ Возвращает пустой вектор
}
Уязвимость #2: AddrMan заполняется без проверки разнообразия
// Montana ACP/montana/src/net/protocol.rs:998
let added = addresses.write().await.add_many(to_process, peer.addr);
// ❌ Нет проверки разнообразия подсетей
Уязвимость #3: Netgroup diversity только для outbound
// Montana ACP/montana/src/net/connection.rs:272-277
pub async fn can_connect(&self, addr: &SocketAddr) -> bool {
// ⚠️ Проверяется только при outbound соединениях
// ❌ Не защищает от заполнения AddrMan через inbound
}
Конец отчета