12 KiB
ECLIPSE ATTACK НА СЕТЬ MONTANA - ДЕТАЛЬНЫЙ АНАЛИЗ
Модель: Grok 3 Компания: xAI Дата: 07.01.2026 17:00 UTC
ВВЕДЕНИЕ
Проведен детальный анализ сетевого слоя Montana на предмет возможности Eclipse Attack согласно шаблону из PROMPT_COUNSIL_TEMPLATE.md. Анализ включает проверку всех трех критических аспектов: netgroup diversity для outbound, anchor connections, и bucket collision handling.
КРИТИЧЕСКАЯ НАХОДКА: ECLIPSE ATTACK ВОЗМОЖЕН
Статус: 🔴 КРИТИЧЕСКАЯ УЯЗВИМОСТЬ - ECLIPSE ATTACK РЕАЛИЗУЕМ
АНАЛИЗ КОДА
1. ANCHOR CONNECTIONS - ОТСУТСТВУЮТ
Файл: protocol.rs, строка 206
// NOTE: Montana does NOT use anchor connections (see Network struct comment).
Анализ:
- ❌ НЕТ механизма anchor connections между рестартами
- ❌ После рестарта все адреса загружаются из
addresses.datбез приоритизации - ❌ Нет "trusted" адресов, которые гарантированно сохраняются между рестартами
- ❌ Нет механизма сохранения надежных outbound соединений
Уязвимость: После рестарта жертвы все outbound соединения выбираются из AddrMan без гарантии, что они не контролируются атакующим.
2. NETGROUP DIVERSITY - НЕДОСТАТОЧНА ДЛЯ OUTBOUND
Файлы: connection.rs, protocol.rs, addrman.rs
Анализ кода:
// Check netgroup diversity
if !connections.can_connect(&socket_addr).await {
continue;
/// 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
Проблема:
- ✅ Netgroup diversity проверяется ПОСЛЕ выбора адреса из AddrMan
- ❌
select()в AddrMan НЕ гарантирует выбор из разных netgroups - ❌
select_from_new()иselect_from_tried()используют случайный bucket выбор - ❌ Если NEW table заполнена адресами из 4-х /16 подсетей атакующего, все 8 outbound могут быть из этих подсетей
Уязвимость: MAX_PEERS_PER_NETGROUP=2 защищает только от превышения лимита, но не гарантирует разнообразие при выборе адресов.
3. BUCKET COLLISION HANDLING - УЯЗВИМОСТЬ
Файл: addrman.rs, строки 201-208
// 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;
Анализ:
- При
mark_good()если bucket занят, старый адрес перемещается обратно в NEW table - Атакующий может использовать это для:
- Вытеснения легитимных адресов из TRIED table
- Заполнения TRIED table своими адресами через повторные
mark_good()вызовы
Уязвимость: Коллизии в TRIED table могут быть использованы для вытеснения легитимных адресов.
4. ЗАПОЛНЕНИЕ NEW TABLE - БЕЗ ЗАЩИТЫ
Файл: addrman.rs, строки 408-416
/// Add multiple addresses (from addr message)
pub fn add_many(&mut self, addrs: Vec<NetAddress>, source: SocketAddr) -> usize {
let mut added = 0;
for addr in addrs {
if self.add(addr, Some(source)) {
added += 1;
}
}
added
}
Анализ:
- ❌ Нет rate limiting на
add_many() - ❌ Нет проверки разнообразия netgroups при добавлении
- ❌ Атакующий может отправить 1000 адресов за раз (MAX_ADDR_SIZE)
- ❌ NEW table: 1024 buckets × 64 slots = 65,536 возможных адресов
Уязвимость: Атакующий может заполнить NEW table вредоносными адресами через множественные Addr сообщения.
5. ВЫБОР АДРЕСОВ - СЛУЧАЙНЫЙ, БЕЗ ГАРАНТИЙ
Файл: addrman.rs, строки 230-245
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
}
}
Проблема:
select_from_new()иselect_from_tried()используют случайный выбор bucket- Нет гарантии выбора из разных netgroups
- Если NEW table заполнена адресами атакующего, вероятность выбора легитимного адреса низка
ЭКСПЛУАТАЦИЯ ECLIPSE ATTACK
ФАЗА 1: ЗАПОЛНЕНИЕ NEW TABLE
Цель: Заполнить NEW table вредоносными адресами
Шаги:
- Подключиться к жертве как inbound peer
- Отправить множественные
Addrсообщения с вредоносными адресами:- До 1000 адресов за сообщение (MAX_ADDR_SIZE)
- Использовать разные /16 подсети (минимум 4 для обхода MAX_PEERS_PER_NETGROUP=2)
- Каждый адрес должен быть routable (не private/loopback)
- Повторить с разных IP адресов для обхода rate limiting
- Цель: заполнить как можно больше buckets в NEW table
Код эксплуатации:
// Создать 65k+ вредоносных адресов в разных /16 подсетях
let mut malicious_addrs = Vec::new();
for subnet in 0..4 { // 4 разные /16 подсети
for i in 0..16384 { // 16k адресов на подсеть
let ip = Ipv4Addr::new(
subnet as u8,
(i / 256) as u8,
(i % 256) as u8,
1
);
malicious_addrs.push(NetAddress::new(
IpAddr::V4(ip),
19333,
NODE_FULL
));
}
}
// Отправить через Addr сообщения (по 1000 за раз)
for chunk in malicious_addrs.chunks(1000) {
send_addr_message(chunk.to_vec());
}
ФАЗА 2: ПРОДВИЖЕНИЕ В TRIED TABLE
Цель: Переместить вредоносные адреса из NEW в TRIED table
Шаги:
- Установить 8 outbound соединений от жертвы к атакующим адресам
- Для каждого соединения:
- Успешно завершить handshake
- Поддерживать соединение активным
- Отправлять полезные сообщения (slices, transactions)
- Жертва вызывает
mark_connected()→mark_good()для каждого соединения - Адреса перемещаются из NEW в TRIED table
Проблема с bucket collisions:
- Если bucket в TRIED table занят легитимным адресом, он перемещается обратно в NEW
- Атакующий может повторно вызывать
mark_good()для вытеснения легитимных адресов
ФАЗА 3: ОЖИДАНИЕ РЕСТАРТА
Цель: Дождаться рестарта жертвы
Шаги:
- Поддерживать вредоносные соединения активными
- Мониторить жертву на предмет рестарта
- После рестарта:
- AddrMan загружается из
addresses.dat - Все адреса в TRIED table сохраняются
- Anchor connections отсутствуют
- AddrMan загружается из
ФАЗА 4: ECLIPSE УСПЕШЕН
Результат:
- После рестарта жертва выбирает outbound адреса из AddrMan
- Все 8 outbound соединений идут к атакующему (из TRIED table)
- Жертва изолирована в контролируемой сети атакующего
- Атакующий может:
- Блокировать транзакции
- Предоставлять ложную информацию о chain state
- Проводить double-spend атаки
- Манипулировать consensus
ПРОВЕРКА ЧЕКЛИСТА
Согласно шаблону:
[ ] Eclipse: netgroup diversity работает ❌ НЕ РАБОТАЕТ для outbound выбора
[ ] Eclipse: anchor connections есть ❌ ОТСУТСТВУЮТ
[ ] Eclipse: bucket collision handling ⚠️ УЯЗВИМО
МИТИГАЦИИ И РЕКОМЕНДАЦИИ
КРИТИЧЕСКИЕ ИСПРАВЛЕНИЯ:
-
Добавить Anchor Connections:
- Сохранять 2-3 надежных outbound соединения между рестартами
- Приоритизировать эти адреса при выборе
- Использовать отдельную таблицу для anchor адресов
-
Улучшить Netgroup Diversity для Outbound:
- Гарантировать выбор из разных netgroups при
select() - Требовать минимум 4 разных /16 подсети для 8 outbound соединений
- Проверять diversity ДО выбора адреса, а не после
- Гарантировать выбор из разных netgroups при
-
Защита от Bucket Collision Abuse:
- Ограничить частоту
mark_good()вызовов - Защищать старые адреса в TRIED table от вытеснения
- Использовать более сложную логику для collision handling
- Ограничить частоту
-
Rate Limiting для Addr Messages:
- Ограничить количество адресов, принимаемых от одного peer
- Проверять разнообразие netgroups при добавлении
- Требовать подтверждение адресов перед добавлением в TRIED
ЗАКЛЮЧЕНИЕ
Eclipse Attack на сеть Montana РЕАЛИЗУЕМ из-за:
- Отсутствия anchor connections
- Недостаточной netgroup diversity при выборе outbound адресов
- Возможности заполнения NEW table вредоносными адресами
- Уязвимости bucket collision handling в TRIED table
Критичность: 🔴 КРИТИЧЕСКАЯ
Вероятность успешной атаки: ВЫСОКАЯ при наличии достаточных ресурсов (4+ /16 подсетей, множественные IP адреса)
Время до эксплуатации: Среднее (требует времени на заполнение таблиц и ожидание рестарта)