271 lines
12 KiB
Markdown
271 lines
12 KiB
Markdown
# 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
|
||
|
||
```rust
|
||
// 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`
|
||
|
||
**Анализ кода:**
|
||
|
||
```517:519:Montana ACP/montana/src/net/protocol.rs
|
||
// Check netgroup diversity
|
||
if !connections.can_connect(&socket_addr).await {
|
||
continue;
|
||
```
|
||
|
||
```264:270: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
|
||
```
|
||
|
||
**Проблема:**
|
||
- ✅ 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
|
||
|
||
```201:208:Montana ACP/montana/src/net/addrman.rs
|
||
// 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
|
||
- Атакующий может использовать это для:
|
||
1. Вытеснения легитимных адресов из TRIED table
|
||
2. Заполнения TRIED table своими адресами через повторные `mark_good()` вызовы
|
||
|
||
**Уязвимость:**
|
||
Коллизии в TRIED table могут быть использованы для вытеснения легитимных адресов.
|
||
|
||
### 4. ЗАПОЛНЕНИЕ NEW TABLE - БЕЗ ЗАЩИТЫ
|
||
|
||
**Файл:** `addrman.rs`, строки 408-416
|
||
|
||
```408:416:Montana ACP/montana/src/net/addrman.rs
|
||
/// 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
|
||
|
||
```230:245:Montana ACP/montana/src/net/addrman.rs
|
||
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 вредоносными адресами
|
||
|
||
**Шаги:**
|
||
1. Подключиться к жертве как inbound peer
|
||
2. Отправить множественные `Addr` сообщения с вредоносными адресами:
|
||
- До 1000 адресов за сообщение (MAX_ADDR_SIZE)
|
||
- Использовать разные /16 подсети (минимум 4 для обхода MAX_PEERS_PER_NETGROUP=2)
|
||
- Каждый адрес должен быть routable (не private/loopback)
|
||
3. Повторить с разных IP адресов для обхода rate limiting
|
||
4. Цель: заполнить как можно больше buckets в NEW table
|
||
|
||
**Код эксплуатации:**
|
||
```rust
|
||
// Создать 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
|
||
|
||
**Шаги:**
|
||
1. Установить 8 outbound соединений от жертвы к атакующим адресам
|
||
2. Для каждого соединения:
|
||
- Успешно завершить handshake
|
||
- Поддерживать соединение активным
|
||
- Отправлять полезные сообщения (slices, transactions)
|
||
3. Жертва вызывает `mark_connected()` → `mark_good()` для каждого соединения
|
||
4. Адреса перемещаются из NEW в TRIED table
|
||
|
||
**Проблема с bucket collisions:**
|
||
- Если bucket в TRIED table занят легитимным адресом, он перемещается обратно в NEW
|
||
- Атакующий может повторно вызывать `mark_good()` для вытеснения легитимных адресов
|
||
|
||
### ФАЗА 3: ОЖИДАНИЕ РЕСТАРТА
|
||
|
||
**Цель:** Дождаться рестарта жертвы
|
||
|
||
**Шаги:**
|
||
1. Поддерживать вредоносные соединения активными
|
||
2. Мониторить жертву на предмет рестарта
|
||
3. После рестарта:
|
||
- AddrMan загружается из `addresses.dat`
|
||
- Все адреса в TRIED table сохраняются
|
||
- Anchor connections отсутствуют
|
||
|
||
### ФАЗА 4: ECLIPSE УСПЕШЕН
|
||
|
||
**Результат:**
|
||
1. После рестарта жертва выбирает outbound адреса из AddrMan
|
||
2. Все 8 outbound соединений идут к атакующему (из TRIED table)
|
||
3. Жертва изолирована в контролируемой сети атакующего
|
||
4. Атакующий может:
|
||
- Блокировать транзакции
|
||
- Предоставлять ложную информацию о chain state
|
||
- Проводить double-spend атаки
|
||
- Манипулировать consensus
|
||
|
||
## ПРОВЕРКА ЧЕКЛИСТА
|
||
|
||
Согласно шаблону:
|
||
|
||
```
|
||
[ ] Eclipse: netgroup diversity работает ❌ НЕ РАБОТАЕТ для outbound выбора
|
||
[ ] Eclipse: anchor connections есть ❌ ОТСУТСТВУЮТ
|
||
[ ] Eclipse: bucket collision handling ⚠️ УЯЗВИМО
|
||
```
|
||
|
||
## МИТИГАЦИИ И РЕКОМЕНДАЦИИ
|
||
|
||
### КРИТИЧЕСКИЕ ИСПРАВЛЕНИЯ:
|
||
|
||
1. **Добавить Anchor Connections:**
|
||
- Сохранять 2-3 надежных outbound соединения между рестартами
|
||
- Приоритизировать эти адреса при выборе
|
||
- Использовать отдельную таблицу для anchor адресов
|
||
|
||
2. **Улучшить Netgroup Diversity для Outbound:**
|
||
- Гарантировать выбор из разных netgroups при `select()`
|
||
- Требовать минимум 4 разных /16 подсети для 8 outbound соединений
|
||
- Проверять diversity ДО выбора адреса, а не после
|
||
|
||
3. **Защита от Bucket Collision Abuse:**
|
||
- Ограничить частоту `mark_good()` вызовов
|
||
- Защищать старые адреса в TRIED table от вытеснения
|
||
- Использовать более сложную логику для collision handling
|
||
|
||
4. **Rate Limiting для Addr Messages:**
|
||
- Ограничить количество адресов, принимаемых от одного peer
|
||
- Проверять разнообразие netgroups при добавлении
|
||
- Требовать подтверждение адресов перед добавлением в TRIED
|
||
|
||
## ЗАКЛЮЧЕНИЕ
|
||
|
||
**Eclipse Attack на сеть Montana РЕАЛИЗУЕМ** из-за:
|
||
1. Отсутствия anchor connections
|
||
2. Недостаточной netgroup diversity при выборе outbound адресов
|
||
3. Возможности заполнения NEW table вредоносными адресами
|
||
4. Уязвимости bucket collision handling в TRIED table
|
||
|
||
**Критичность:** 🔴 **КРИТИЧЕСКАЯ**
|
||
|
||
**Вероятность успешной атаки:** ВЫСОКАЯ при наличии достаточных ресурсов (4+ /16 подсетей, множественные IP адреса)
|
||
|
||
**Время до эксплуатации:** Среднее (требует времени на заполнение таблиц и ожидание рестарта)
|