452 lines
27 KiB
Markdown
452 lines
27 KiB
Markdown
|
|
# Security Audit: Сетевой слой Montana
|
|||
|
|
|
|||
|
|
**Модель:** Composer 1
|
|||
|
|
**Компания:** Cursor
|
|||
|
|
**Дата:** 15.01.2025 20:00 UTC
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Понимание архитектуры
|
|||
|
|
|
|||
|
|
Montana использует принципиально иную модель консенсуса — Atemporal Coordinate Presence (ACP), основанную на физических ограничениях времени и вычислений, а не на традиционных механизмах блокчейна.
|
|||
|
|
|
|||
|
|
### Ключевые отличия от традиционных систем:
|
|||
|
|
|
|||
|
|
1. **Время как основа консенсуса**: Montana использует физические ограничения Слоя -1 (упорядоченность событий) для создания временных координат (τ₂), которые физически невозможно подделать или ускорить. Это означает, что атаки типа "long-range" или "nothing-at-stake" не применимы, так как нельзя создать валидные координаты для прошлого времени.
|
|||
|
|
|
|||
|
|
2. **Отсутствие майнеров/валидаторов**: Вместо этого используется детерминистическая лотерея на основе хешей для выбора "победителей" слайсов. Без майнинга или стейкинга нет экономических стимулов для атак типа 51% или selfish mining.
|
|||
|
|
|
|||
|
|
3. **Fork choice через время**: Выбор форка основан на временной цепочке (timechain), где каждый слайс ссылается на предыдущий через хеш. Это делает реорганизации предсказуемыми и ограниченными временем.
|
|||
|
|
|
|||
|
|
4. **Криптографическая безопасность**: Все подписи используют ML-DSA-65 (пост-квантовая криптография), что защищает от будущих квантовых атак. Присутствие (presence) требует реального времени для создания подписей.
|
|||
|
|
|
|||
|
|
5. **Sybil модель**: В Montana Sybil-атаки влияют в основном на распределение наград, а не на безопасность сети, так как безопасность обеспечивается криптографически, а накопление присутствия требует реального времени.
|
|||
|
|
|
|||
|
|
### Следствия для безопасности сетевого слоя:
|
|||
|
|
|
|||
|
|
- Защита от Eclipse критична, так как изоляция узла может привести к получению фейкового chain state
|
|||
|
|
- DoS-атаки могут нарушить синхронизацию и доступность сети
|
|||
|
|
- Memory exhaustion может привести к отказу в обслуживании
|
|||
|
|
- Неправильная обработка сообщений может привести к некорректному состоянию узла
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Изученные файлы
|
|||
|
|
|
|||
|
|
| Файл | LOC | Ключевые компоненты |
|
|||
|
|
|------|-----|---------------------|
|
|||
|
|
| `types.rs` | ~400 | Константы сети, ограничения памяти, типы сообщений, state machines |
|
|||
|
|
| `protocol.rs` | ~1500 | Основной протокол P2P, обработка сообщений, handshake, connection loops |
|
|||
|
|
| `addrman.rs` | ~600 | Менеджер адресов с криптографическими бакетами, защита от отравления |
|
|||
|
|
| `connection.rs` | ~500 | Управление подключениями, ban list, retry logic, netgroup limits |
|
|||
|
|
| `inventory.rs` | ~600 | Управление инвентарем (slices, tx, presence), LRU eviction, relay cache |
|
|||
|
|
| `peer.rs` | ~400 | Состояние пира, rate limits, flow control, known inventory tracking |
|
|||
|
|
| `message.rs` | ~300 | Определение всех типов P2P сообщений, размеры, сериализация |
|
|||
|
|
| `rate_limit.rs` | ~400 | Token bucket rate limiting, flow control, adaptive subnet limiting |
|
|||
|
|
| `bootstrap.rs` | ~400 | Верификация bootstrap, Trusted Core модель, консенсус проверок |
|
|||
|
|
| `eviction.rs` | ~300 | Логика вытеснения пиров, многоуровневая защита |
|
|||
|
|
| `sync.rs` | ~700 | Headers-first sync, параллельная загрузка слайсов, orphan pool |
|
|||
|
|
| `startup.rs` | ~200 | Оркестрация полной верификации при старте |
|
|||
|
|
| `subnet.rs` | ~300 | Отслеживание репутации подсетей, защита от Eclipse |
|
|||
|
|
| `verification.rs` | ~700 | Клиент для верификации, аутентификация hardcoded нод |
|
|||
|
|
| `hardcoded_identity.rs` | ~200 | Определение hardcoded нод, ML-DSA-65 аутентификация |
|
|||
|
|
| `discouraged.rs` | ~300 | Rolling bloom filter для discouraged пиров |
|
|||
|
|
| `feeler.rs` | ~250 | Короткоживущие подключения для проверки адресов |
|
|||
|
|
| `dns.rs` | ~270 | DNS seeds и fallback IPs для bootstrap |
|
|||
|
|
| `noise.rs` | ~900 | Noise XX + ML-KEM-768 гибридное шифрование |
|
|||
|
|
| `encrypted.rs` | ~435 | Обертка для зашифрованных потоков, управление ключами |
|
|||
|
|
| `mod.rs` | ~75 | Объявления модулей и re-exports |
|
|||
|
|
|
|||
|
|
**Всего:** ~20 файлов, ~9000+ строк кода
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Attack Surface
|
|||
|
|
|
|||
|
|
### Точки входа для атакующего:
|
|||
|
|
|
|||
|
|
1. **P2P подключения**: Атакующий может инициировать входящие и исходящие TCP подключения, выполнять Noise handshake и отправлять P2P сообщения.
|
|||
|
|
|
|||
|
|
2. **Bootstrap процесс**: При старте узла атакующий может контролировать DNS seeds, fallback IPs (через BGP hijacking), или предоставлять фейковый chain state через P2P пиры.
|
|||
|
|
|
|||
|
|
3. **Сообщения протокола**: Атакующий может отправлять любые типы сообщений (Version, Inv, Addr, GetData, Slice, Tx, Presence и т.д.) в пределах rate limits.
|
|||
|
|
|
|||
|
|
4. **Address management**: Атакующий может отправлять адреса через `Addr` сообщения для отравления адресной таблицы.
|
|||
|
|
|
|||
|
|
5. **Inventory flooding**: Атакующий может отправлять множество уникальных inventory items для исчерпания памяти.
|
|||
|
|
|
|||
|
|
6. **Handshake exhaustion**: Атакующий может инициировать множество handshake без их завершения для исчерпания ресурсов.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Найденные уязвимости
|
|||
|
|
|
|||
|
|
### [HIGH] Memory Exhaustion через неограниченный `have_slice` HashSet
|
|||
|
|
|
|||
|
|
**Файл:** `inventory.rs:118`
|
|||
|
|
|
|||
|
|
**Уязвимый код:**
|
|||
|
|
```rust
|
|||
|
|
pub struct Inventory {
|
|||
|
|
// Items we have locally
|
|||
|
|
// Slices use HashSet (bounded by chain length, not attacker-controlled)
|
|||
|
|
have_slice: HashSet<Hash>,
|
|||
|
|
// Tx and Presence use LruHashSet (attacker can flood with unique items)
|
|||
|
|
have_tx: LruHashSet,
|
|||
|
|
have_presence: LruHashSet,
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Вектор атаки:**
|
|||
|
|
1. Атакующий подключается к узлу и отправляет множество `Inv` сообщений с фейковыми slice hashes
|
|||
|
|
2. Узел обрабатывает эти `Inv` и добавляет каждый hash в `have_slice` через `add_have()` или `add_slice()`
|
|||
|
|
3. Так как `have_slice` использует обычный `HashSet` без ограничений размера, память растет неограниченно
|
|||
|
|
4. Комментарий предполагает, что slice hashes ограничены длиной цепи, но атакующий может отправлять фейковые hashes, которые не соответствуют реальным слайсам
|
|||
|
|
|
|||
|
|
**Импакт:**
|
|||
|
|
- Memory exhaustion узла
|
|||
|
|
- Возможный отказ в обслуживании (DoS)
|
|||
|
|
- Узел может быть вынужден перезапуститься или упасть
|
|||
|
|
|
|||
|
|
**Сложность:**
|
|||
|
|
- Низкая: требуется только одно подключение и отправка множества `Inv` сообщений
|
|||
|
|
- Ресурсы: минимальные (один бот или скрипт)
|
|||
|
|
|
|||
|
|
**PoC сценарий:**
|
|||
|
|
```python
|
|||
|
|
# Псевдокод атаки
|
|||
|
|
1. Подключиться к целевому узлу Montana
|
|||
|
|
2. Выполнить Noise handshake
|
|||
|
|
3. Отправить Version/Verack
|
|||
|
|
4. Для i в range(1, 1000000):
|
|||
|
|
5. Сгенерировать случайный hash h_i
|
|||
|
|
6. Отправить Inv([InvItem(InvType::Slice, h_i)])
|
|||
|
|
7. Узел добавит h_i в have_slice HashSet
|
|||
|
|
8. Память узла исчерпается
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Рекомендация:** Использовать `LruHashSet` для `have_slice` с ограничением размера (например, `MAX_SLICE_HAVE_ENTRIES = 1_000_000` для учета реальной длины цепи с запасом).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### [MEDIUM] Resource Exhaustion через неограниченные handshake подключения
|
|||
|
|
|
|||
|
|
**Файл:** `protocol.rs:589-613`, `connection.rs:186`
|
|||
|
|
|
|||
|
|
**Уязвимый код:**
|
|||
|
|
```rust
|
|||
|
|
// protocol.rs:589
|
|||
|
|
tokio::spawn(async move {
|
|||
|
|
if let Err(e) = Self::handle_connection(
|
|||
|
|
stream,
|
|||
|
|
socket_addr,
|
|||
|
|
false,
|
|||
|
|
// ...
|
|||
|
|
)
|
|||
|
|
.await
|
|||
|
|
{
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// connection.rs:186
|
|||
|
|
connecting: Mutex<HashSet<SocketAddr>>,
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Вектор атаки:**
|
|||
|
|
1. Атакующий инициирует множество TCP подключений к узлу одновременно (используя разные IP адреса)
|
|||
|
|
2. Каждое подключение добавляется в `connecting` HashSet и создает новую `tokio::spawn` задачу
|
|||
|
|
3. Атакующий начинает Noise handshake, но не завершает его (например, отправляет только часть сообщений или медленно отвечает)
|
|||
|
|
4. Каждое незавершенное handshake потребляет память (задача, буферы, криптографические операции) и CPU
|
|||
|
|
5. `connecting` HashSet не имеет явного ограничения размера, только `MAX_CONNECTIONS_PER_IP` ограничивает одно IP
|
|||
|
|
|
|||
|
|
**Импакт:**
|
|||
|
|
- Исчерпание памяти через множество незавершенных handshake
|
|||
|
|
- Исчерпание CPU через криптографические операции (ML-KEM-768, X25519)
|
|||
|
|
- Возможный отказ в обслуживании
|
|||
|
|
|
|||
|
|
**Сложность:**
|
|||
|
|
- Средняя: требуется множество IP адресов (например, ботнет или распределенная атака)
|
|||
|
|
- Ресурсы: ботнет с ~1000+ узлов или распределенная атака
|
|||
|
|
|
|||
|
|
**PoC сценарий:**
|
|||
|
|
```python
|
|||
|
|
# Псевдокод атаки
|
|||
|
|
1. Получить доступ к ботнету с 1000+ IP адресов
|
|||
|
|
2. Для каждого IP:
|
|||
|
|
3. Инициировать TCP подключение к целевому узлу
|
|||
|
|
4. Начать Noise handshake (отправить Message 0)
|
|||
|
|
5. НЕ отвечать на Message 1 от узла (или отвечать очень медленно)
|
|||
|
|
6. Поддержать подключение открытым до таймаута (30 секунд)
|
|||
|
|
7. Повторить для всех IP
|
|||
|
|
8. Узел будет иметь 1000+ незавершенных handshake, потребляющих ресурсы
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Рекомендация:**
|
|||
|
|
1. Добавить глобальное ограничение на количество одновременных подключений в процессе handshake (например, `MAX_CONCURRENT_HANDSHAKES = 100`)
|
|||
|
|
2. Добавить ограничение размера `connecting` HashSet
|
|||
|
|
3. Более агрессивные таймауты для handshake
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### [MEDIUM] CPU Exhaustion через дубликаты в Inv сообщениях
|
|||
|
|
|
|||
|
|
**Файл:** `protocol.rs:1001-1022`
|
|||
|
|
|
|||
|
|
**Уязвимый код:**
|
|||
|
|
```rust
|
|||
|
|
Message::Inv(items) => {
|
|||
|
|
if items.len() > MAX_INV_SIZE {
|
|||
|
|
return Err(NetError::Protocol("Too many inv items".into()));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if !peer.rate_limits.inv.try_consume(items.len()) {
|
|||
|
|
debug!("Rate limited inv from {} ({} items)", peer.addr, items.len());
|
|||
|
|
return Ok(false);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
for item in &items {
|
|||
|
|
peer.add_known_inv(item.hash);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let needed = inventory.read().await.filter_needed(&items);
|
|||
|
|
if !needed.is_empty() {
|
|||
|
|
for item in &needed {
|
|||
|
|
inventory.write().await.request(item, peer.addr);
|
|||
|
|
}
|
|||
|
|
peer_tx.send(Message::GetData(needed)).await.ok();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Вектор атаки:**
|
|||
|
|
1. Атакующий отправляет `Inv` сообщение с `MAX_INV_SIZE` (50,000) дубликатов одного и того же hash
|
|||
|
|
2. Код не дедуплицирует элементы внутри одного сообщения перед обработкой
|
|||
|
|
3. Каждый элемент обрабатывается отдельно:
|
|||
|
|
- `peer.add_known_inv(item.hash)` вызывается 50,000 раз для одного hash
|
|||
|
|
- `inventory.read().await.filter_needed(&items)` обрабатывает все 50,000 элементов
|
|||
|
|
- Хотя `BoundedInvSet` в `peer.add_known_inv` использует HashSet и не добавит дубликаты, проверка выполняется 50,000 раз
|
|||
|
|
4. Это приводит к избыточному использованию CPU
|
|||
|
|
|
|||
|
|
**Импакт:**
|
|||
|
|
- CPU exhaustion через избыточную обработку дубликатов
|
|||
|
|
- Возможное замедление обработки легитимных сообщений
|
|||
|
|
- DoS через ресурсное исчерпание
|
|||
|
|
|
|||
|
|
**Сложность:**
|
|||
|
|
- Низкая: требуется только одно подключение
|
|||
|
|
- Ресурсы: минимальные (один скрипт)
|
|||
|
|
|
|||
|
|
**PoC сценарий:**
|
|||
|
|
```python
|
|||
|
|
# Псевдокод атаки
|
|||
|
|
1. Подключиться к целевому узлу
|
|||
|
|
2. Выполнить handshake
|
|||
|
|
3. Сгенерировать один валидный hash h
|
|||
|
|
4. Создать Inv сообщение с 50,000 копий InvItem(InvType::Slice, h)
|
|||
|
|
5. Отправить это сообщение
|
|||
|
|
6. Повторить шаги 3-5 с разными hashes
|
|||
|
|
7. Узел будет обрабатывать миллионы дубликатов, потребляя CPU
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Рекомендация:** Дедуплицировать элементы внутри `Inv` сообщения перед обработкой:
|
|||
|
|
```rust
|
|||
|
|
let unique_items: Vec<InvItem> = items.iter()
|
|||
|
|
.unique_by(|item| item.hash)
|
|||
|
|
.cloned()
|
|||
|
|
.collect();
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### [LOW] Потенциальная утечка памяти в `ip_votes` при частых переподключениях
|
|||
|
|
|
|||
|
|
**Файл:** `protocol.rs:897`, `protocol.rs:834-839`
|
|||
|
|
|
|||
|
|
**Уязвимый код:**
|
|||
|
|
```rust
|
|||
|
|
// При получении Version от outbound пира
|
|||
|
|
ip_votes.write().await.insert(peer.addr, their_view_of_us);
|
|||
|
|
|
|||
|
|
// При отключении пира
|
|||
|
|
peers.write().await.remove(&addr);
|
|||
|
|
let _ = event_tx.send(NetEvent::PeerDisconnected(addr)).await;
|
|||
|
|
|
|||
|
|
if let Some(nonce) = our_sent_nonce {
|
|||
|
|
sent_nonces.write().await.remove(&nonce);
|
|||
|
|
}
|
|||
|
|
// НО: ip_votes НЕ очищается при отключении пира
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Вектор атаки:**
|
|||
|
|
1. Атакующий создает множество outbound подключений к узлу
|
|||
|
|
2. Каждое подключение отправляет Version с "голосом" за внешний IP, добавляя запись в `ip_votes`
|
|||
|
|
3. Атакующий быстро отключается и переподключается с новыми адресами
|
|||
|
|
4. Старые записи в `ip_votes` не удаляются при отключении
|
|||
|
|
5. При частых переподключениях `ip_votes` может расти неограниченно
|
|||
|
|
|
|||
|
|
**Импакт:**
|
|||
|
|
- Потенциальная утечка памяти (хотя комментарий говорит, что ограничено `MAX_OUTBOUND`)
|
|||
|
|
- Если пиры часто переподключаются, старые записи накапливаются
|
|||
|
|
|
|||
|
|
**Сложность:**
|
|||
|
|
- Средняя: требуется множество подключений и частые переподключения
|
|||
|
|
- Ресурсы: ботнет или распределенная атака
|
|||
|
|
|
|||
|
|
**PoC сценарий:**
|
|||
|
|
```python
|
|||
|
|
# Псевдокод атаки
|
|||
|
|
1. Получить доступ к множеству IP адресов
|
|||
|
|
2. Для каждого IP:
|
|||
|
|
3. Подключиться к целевому узлу (outbound)
|
|||
|
|
4. Отправить Version с "голосом" за фейковый IP
|
|||
|
|
5. Немедленно отключиться
|
|||
|
|
6. Повторить с новым IP
|
|||
|
|
7. После 10,000 переподключений ip_votes будет содержать 10,000 записей
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Рекомендация:** Очищать `ip_votes` при отключении outbound пира:
|
|||
|
|
```rust
|
|||
|
|
if !peer.inbound {
|
|||
|
|
ip_votes.write().await.remove(&addr);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### [LOW] Потенциальная атака через false positives в DiscouragedFilter
|
|||
|
|
|
|||
|
|
**Файл:** `discouraged.rs:13-105`
|
|||
|
|
|
|||
|
|
**Уязвимый код:**
|
|||
|
|
```rust
|
|||
|
|
pub struct DiscouragedFilter {
|
|||
|
|
data: Vec<u64>,
|
|||
|
|
n_hash: u32,
|
|||
|
|
n_elements: u32,
|
|||
|
|
max_elements: u32,
|
|||
|
|
generation: u32,
|
|||
|
|
tweak: u64,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pub fn contains(&self, addr: &SocketAddr) -> bool {
|
|||
|
|
// Bloom filter может иметь false positives
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Вектор атаки:**
|
|||
|
|
1. Атакующий изучает структуру bloom filter (хотя `tweak` случайный)
|
|||
|
|
2. Атакующий создает множество адресов, которые могут вызвать false positives с легитимными адресами
|
|||
|
|
3. Легитимные пиры случайно попадают в discouraged filter
|
|||
|
|
4. Легитимные пиры деприоритизируются, что может помочь атакующему вытеснить их
|
|||
|
|
|
|||
|
|
**Импакт:**
|
|||
|
|
- Деприоритизация легитимных пиров
|
|||
|
|
- Помощь в Eclipse атаке (вытеснение хороших пиров)
|
|||
|
|
|
|||
|
|
**Сложность:**
|
|||
|
|
- Высокая: требуется глубокое понимание структуры bloom filter и криптографических хешей
|
|||
|
|
- Ресурсы: значительные вычислительные ресурсы для анализа
|
|||
|
|
|
|||
|
|
**PoC сценарий:**
|
|||
|
|
```python
|
|||
|
|
# Псевдокод атаки (теоретический)
|
|||
|
|
1. Изучить структуру DiscouragedFilter (SipHasher24, tweak)
|
|||
|
|
2. Найти адреса, которые вызывают коллизии хешей с легитимными адресами
|
|||
|
|
3. Добавить эти адреса в discouraged filter (через misbehavior)
|
|||
|
|
4. Легитимные адреса могут попасть в false positive
|
|||
|
|
5. Легитимные пиры деприоритизируются
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Рекомендация:** False positives в bloom filter приемлемы для "мягкого" наказания, но стоит мониторить частоту false positives и при необходимости увеличить размер фильтра или уменьшить false positive rate.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Атаки, которые НЕ работают
|
|||
|
|
|
|||
|
|
### Классические блокчейн атаки:
|
|||
|
|
|
|||
|
|
1. **51% атака**: Не применима, так как нет майнинга или стейкинга. Безопасность обеспечивается криптографически и физическими ограничениями времени.
|
|||
|
|
|
|||
|
|
2. **Selfish mining**: Не применима, так как нет майнеров, которые могут удерживать блоки. Слайсы создаются детерминистически на основе хешей.
|
|||
|
|
|
|||
|
|
3. **Long-range атака**: Не применима, так как нельзя создать валидные координаты для прошлого времени (накопление присутствия требует реального времени).
|
|||
|
|
|
|||
|
|
4. **Nothing-at-stake**: Не применима, так как нет стейкинга и нет экономических стимулов для создания множества форков.
|
|||
|
|
|
|||
|
|
5. **Eclipse через адресное отравление**: Частично защищено криптографическими бакетами в AddrMan, hardcoded нодами, и netgroup diversity. Однако, если атакующий контролирует DNS/BGP, он может отравить bootstrap процесс.
|
|||
|
|
|
|||
|
|
6. **Sybil атака на консенсус**: Не применима в традиционном смысле, так как консенсус не зависит от количества нод. Однако Sybil может влиять на распределение наград и сетевую топологию.
|
|||
|
|
|
|||
|
|
### Защитные механизмы, которые работают хорошо:
|
|||
|
|
|
|||
|
|
1. **Rate limiting**: Token bucket для различных типов сообщений эффективно ограничивает DoS через сообщения.
|
|||
|
|
|
|||
|
|
2. **Flow control**: Управление очередями предотвращает переполнение буферов.
|
|||
|
|
|
|||
|
|
3. **Bounded collections**: Большинство коллекций имеют явные ограничения размера (LruHashSet, MAX_RELAY_CACHE_ENTRIES, и т.д.).
|
|||
|
|
|
|||
|
|
4. **Netgroup diversity**: Ограничение подключений из одной /16 подсети защищает от Erebus-стиль атак.
|
|||
|
|
|
|||
|
|
5. **Hardcoded nodes**: ML-DSA-65 аутентификация hardcoded нод обеспечивает надежный bootstrap.
|
|||
|
|
|
|||
|
|
6. **Adaptive subnet limiting**: Двухуровневое адаптивное лимитирование подсетей защищает от распределенных атак.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Рекомендации
|
|||
|
|
|
|||
|
|
### Критические исправления:
|
|||
|
|
|
|||
|
|
1. **Исправить `have_slice` HashSet** (`inventory.rs:118`):
|
|||
|
|
- Заменить на `LruHashSet` с ограничением размера
|
|||
|
|
- Рекомендуемый размер: `MAX_SLICE_HAVE_ENTRIES = 1_000_000` (для учета реальной длины цепи с запасом)
|
|||
|
|
|
|||
|
|
2. **Ограничить одновременные handshake** (`protocol.rs:589`, `connection.rs:186`):
|
|||
|
|
- Добавить глобальное ограничение `MAX_CONCURRENT_HANDSHAKES = 100`
|
|||
|
|
- Добавить ограничение размера `connecting` HashSet
|
|||
|
|
- Более агрессивные таймауты для handshake
|
|||
|
|
|
|||
|
|
3. **Дедуплицировать Inv сообщения** (`protocol.rs:1001-1022`):
|
|||
|
|
- Дедуплицировать элементы внутри `Inv` сообщения перед обработкой
|
|||
|
|
- Использовать `HashSet` или `BTreeSet` для уникальности
|
|||
|
|
|
|||
|
|
### Улучшения:
|
|||
|
|
|
|||
|
|
4. **Очищать `ip_votes` при отключении** (`protocol.rs:834-839`):
|
|||
|
|
- Добавить очистку `ip_votes` при отключении outbound пира
|
|||
|
|
|
|||
|
|
5. **Мониторинг false positives** (`discouraged.rs`):
|
|||
|
|
- Добавить метрики для отслеживания частоты false positives в DiscouragedFilter
|
|||
|
|
- При необходимости увеличить размер фильтра или уменьшить false positive rate
|
|||
|
|
|
|||
|
|
6. **Валидация содержимого сообщений**:
|
|||
|
|
- Добавить более строгую валидацию содержимого сообщений (например, проверка типов, диапазонов значений)
|
|||
|
|
- Добавить метрики для отслеживания аномальных паттернов в сообщениях
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Вердикт
|
|||
|
|
|
|||
|
|
- [ ] CRITICAL — есть уязвимости, позволяющие уничтожить сеть
|
|||
|
|
- [x] HIGH — есть серьёзные уязвимости
|
|||
|
|
- [ ] MEDIUM — есть уязвимости среднего риска
|
|||
|
|
- [ ] LOW — только minor issues
|
|||
|
|
- [ ] SECURE — уязвимостей не найдено
|
|||
|
|
|
|||
|
|
**Обоснование:**
|
|||
|
|
|
|||
|
|
Найдены **4 уязвимости** различной степени серьезности:
|
|||
|
|
|
|||
|
|
1. **HIGH**: Memory exhaustion через неограниченный `have_slice` HashSet — может привести к DoS узла
|
|||
|
|
2. **MEDIUM**: Resource exhaustion через неограниченные handshake — требует больше ресурсов, но все еще серьезная
|
|||
|
|
3. **MEDIUM**: CPU exhaustion через дубликаты в Inv — может замедлить обработку сообщений
|
|||
|
|
4. **LOW**: Потенциальная утечка памяти в `ip_votes` — менее критична, но стоит исправить
|
|||
|
|
|
|||
|
|
Сетевой слой Montana в целом хорошо защищен с множеством защитных механизмов (rate limiting, bounded collections, netgroup diversity, hardcoded nodes). Однако найденные уязвимости показывают, что есть места для улучшения, особенно в области ограничения памяти и обработки дубликатов.
|
|||
|
|
|
|||
|
|
**Общая оценка:** Сетевой слой имеет хорошую архитектуру безопасности, но требует исправления найденных уязвимостей перед production deployment.
|
|||
|
|
|