montana/Русский/Совет/Cursor/комплексный_аудит_сети_08.01.2026_10:55.md

693 lines
33 KiB
Markdown
Raw Permalink Normal View History

# Security Audit: Сетевой слой Montana ACP
**Модель:** Composer 1
**Компания:** Cursor
**Дата:** 08.01.2026 10:55 UTC
---
## EXECUTIVE SUMMARY
Проведен глубокий анализ сетевого слоя Montana ACP с фокусом на поиск уязвимостей, которые могут быть использованы для атак на сеть. Анализ включал:
- Изучение архитектуры ACP (MONTANA.md, layer_*.md)
- Детальный код-ревью всех файлов в `src/net/`
- Поиск уязвимостей в категориях: DoS, Eclipse, Time-warp, Sybil, Race Conditions
**Критические находки:** 3 критические уязвимости, 5 высокого приоритета, 10 среднего приоритета, 1 низкого приоритета.
---
## КРИТИЧЕСКИЕ УЯЗВИМОСТИ
### VULN-001: Несогласованность лимитов размера сообщений в VerificationClient
**Файл:** `src/net/verification.rs:715`
**Строка:** 715
**Категория:** DoS / Memory Exhaustion
**Приоритет:** КРИТИЧЕСКИЙ
#### Описание
`VerificationClient` использует лимит `2MB` для сообщений при bootstrap, в то время как основной протокол использует `MAX_TX_SIZE = 1MB`. Это создает несогласованность и потенциальную возможность для DoS-атаки.
```715:720:Montana ACP/montana/src/net/verification.rs
// Security: Limit message size
if length > 2 * 1024 * 1024 {
return Err(VerificationError::Protocol(format!(
"message too large: {}",
length
)));
}
```
#### Уязвимый код
- `verification.rs:715`: Лимит `2MB` вместо `MAX_TX_SIZE` (1MB)
- Основной протокол (`protocol.rs:1368`) использует `MAX_TX_SIZE = 1MB`
#### Сценарий эксплуатации
1. Атакующий подключается к жертве как hardcoded node (или подделывает ответ)
2. Отправляет сообщение размером 1.5MB (больше основного лимита, но меньше лимита verification)
3. Это может привести к:
- Несогласованности в обработке сообщений
- Потенциальному обходу защиты от DoS
- Проблемам с памятью при десериализации
#### Влияние
- **DoS**: Возможность отправки больших сообщений во время bootstrap
- **Memory Exhaustion**: Риск исчерпания памяти на узлах
- **Inconsistency**: Разные лимиты в разных частях кода
#### Сложность эксплуатации
**Низкая** - Требуется только подключение к узлу во время bootstrap
#### Рекомендации
1. Использовать `MAX_TX_SIZE` (1MB) вместо жестко закодированного `2MB`
2. Унифицировать все лимиты размеров сообщений через константы из `types.rs`
3. Добавить проверку размера до десериализации
---
### VULN-002: Отсутствие проверки границ при десериализации AddrMan
**Файл:** `src/net/addrman.rs:117-123`
**Строка:** 117-123
**Категория:** DoS / Memory Exhaustion
**Приоритет:** КРИТИЧЕСКИЙ
#### Описание
При загрузке `AddrMan` из файла проверяется только размер файла (`MAX_ADDRMAN_FILE_SIZE = 16MB`), но нет проверки количества десериализованных адресов. Злоумышленник может создать файл с большим количеством адресов, что приведет к исчерпанию памяти.
```117:123:Montana ACP/montana/src/net/addrman.rs
/// Load from file
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, std::io::Error> {
let data = std::fs::read(path)?;
if data.len() as u64 > MAX_ADDRMAN_FILE_SIZE {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"AddrMan file too large"
));
}
let addrman: AddrMan = bincode::deserialize(&data).map_err(|e| {
```
#### Уязвимый код
- `addrman.rs:123`: Десериализация без проверки количества адресов
- `addrman.rs:39`: `addrs: HashMap<usize, AddressInfo>` - неограниченная структура
#### Сценарий эксплуатации
1. Атакующий создает поддельный `addresses.dat` с миллионами адресов
2. Файл укладывается в лимит 16MB (можно использовать сжатие или оптимизированную сериализацию)
3. При загрузке узла происходит десериализация всех адресов
4. Это приводит к:
- Исчерпанию памяти (каждый адрес ~100 байт, миллион адресов = ~100MB)
- Замедлению работы узла
- Потенциальному DoS
#### Влияние
- **Memory Exhaustion**: Возможность исчерпания памяти при загрузке
- **DoS**: Замедление работы узла
- **Persistence**: Атака сохраняется в файле
#### Сложность эксплуатации
**Средняя** - Требуется доступ к файловой системе узла или компрометация источника загрузки
#### Рекомендации
1. Добавить проверку максимального количества адресов после десериализации:
```rust
const MAX_ADDRS: usize = 100_000; // Разумный лимит
if addrman.addrs.len() > MAX_ADDRS {
return Err(...);
}
```
2. Проверять `new_count + tried_count` против лимита
3. Добавить валидацию структуры данных после десериализации
---
### VULN-003: Race Condition в обновлении IP votes для внешнего IP
**Файл:** `src/net/protocol.rs:925-947`
**Строка:** 925-947
**Категория:** Race Condition / Logic Error
**Приоритет:** КРИТИЧЕСКИЙ
#### Описание
Обновление `ip_votes` и проверка консенсуса происходят в разных блоках `read().await` и `write().await`, что создает race condition. Между чтением и записью другой поток может изменить состояние.
```925:947:Montana ACP/montana/src/net/protocol.rs
ip_votes.write().await.insert(peer.addr, their_view_of_us);
// Check for consensus
let votes = ip_votes.read().await;
let mut ip_counts: HashMap<IpAddr, usize> = HashMap::new();
for ip in votes.values() {
*ip_counts.entry(*ip).or_insert(0) += 1;
}
// Find IP with most votes (need MIN_IP_VOTES agreement)
if let Some((consensus_ip, count)) = ip_counts.iter().max_by_key(|(_, c)| *c)
&& *count >= MIN_IP_VOTES
{
let mut local = local_addr.write().await;
let should_update = match &*local {
None => true,
Some(addr) => addr.ip != *consensus_ip,
};
if should_update {
info!("External IP discovered: {} ({} peers agree)", consensus_ip, count);
*local = Some(NetAddress::new(*consensus_ip, config.listen_port, config.services));
}
}
```
#### Уязвимый код
1. Запись в `ip_votes` (строка 925)
2. Освобождение блокировки
3. Чтение из `ip_votes` (строка 928) - другая блокировка
4. Между шагами 2 и 3 другой поток может изменить данные
#### Сценарий эксплуатации
1. Атакующий контролирует несколько исходящих соединений (Sybil)
2. Отправляет разные IP-адреса в `Version` сообщениях
3. Из-за race condition может произойти:
- Неправильный подсчет голосов
- Установка неправильного внешнего IP
- Потенциальная атака на маршрутизацию
#### Влияние
- **Logic Error**: Неправильное определение внешнего IP
- **Security**: Потенциальная атака на маршрутизацию
- **Race Condition**: Непредсказуемое поведение в многопоточной среде
#### Сложность эксплуатации
**Средняя** - Требуется контроль над несколькими исходящими соединениями
#### Рекомендации
1. Использовать одну блокировку для всей операции:
```rust
let mut votes = ip_votes.write().await;
votes.insert(peer.addr, their_view_of_us);
// Проверка консенсуса внутри той же блокировки
```
2. Или использовать атомарные операции для подсчета
3. Добавить проверку на изменение состояния между операциями
---
## ВЫСОКИЙ ПРИОРИТЕТ
### VULN-004: Неограниченный рост HashMap в SubnetTracker
**Файл:** `src/net/subnet.rs:68-77`
**Строка:** 68-77
**Категория:** DoS / Memory Exhaustion
**Приоритет:** ВЫСОКИЙ
#### Описание
`SubnetTracker` хранит репутацию подnetов в `HashMap`, но нет лимита на количество отслеживаемых подnetов. Атакующий может создать множество подnetов, что приведет к росту памяти.
```68:77:Montana ACP/montana/src/net/subnet.rs
pub struct SubnetTracker {
/// Reputation by subnet
reputations: HashMap<Subnet16, SubnetReputation>,
/// Known signers per subnet (pubkey hash -> subnet)
signer_subnets: HashMap<Hash, Subnet16>,
/// Last snapshot τ₂
last_snapshot_tau2: u64,
/// Snapshot reputations (frozen every τ₃)
snapshot: HashMap<Subnet16, SubnetReputation>,
}
```
#### Сценарий эксплуатации
1. Атакующий создает множество узлов из разных /16 подnetов
2. Каждый узел отправляет presence proofs
3. `SubnetTracker` накапливает репутацию для каждого поднета
4. Память растет неограниченно
#### Влияние
- **Memory Exhaustion**: Неограниченный рост памяти
- **DoS**: Замедление работы узла
#### Рекомендации
1. Добавить лимит на количество отслеживаемых подnetов (например, 10,000)
2. Использовать LRU eviction для старых подnetов
3. Периодически очищать неактивные поднеты
---
### VULN-005: Отсутствие проверки на переполнение в saturating_add
**Файл:** `src/net/subnet.rs:46`
**Строка:** 46
**Категория:** Logic Error / Integer Overflow
**Приоритет:** ВЫСОКИЙ
#### Описание
Использование `saturating_add` скрывает переполнение, что может привести к неправильным расчетам весов подnetов.
```46:48:Montana ACP/montana/src/net/subnet.rs
pub fn add_weight(&mut self, weight: u64, tau2: u64) {
self.total_weight = self.total_weight.saturating_add(weight);
self.last_seen_tau2 = tau2;
}
```
#### Сценарий эксплуатации
1. Атакующий отправляет множество presence proofs с большими весами
2. `total_weight` достигает `u64::MAX`
3. Дальнейшие добавления игнорируются (saturating_add)
4. Это может привести к неправильным расчетам репутации
#### Влияние
- **Logic Error**: Неправильные расчеты весов
- **Security**: Потенциальная манипуляция репутацией
#### Рекомендации
1. Добавить проверку на переполнение и логирование предупреждений
2. Использовать `checked_add` и обрабатывать переполнение явно
3. Добавить лимит на максимальный вес поднета
---
### VULN-006: Потенциальная утечка памяти в sent_nonces
**Файл:** `src/net/protocol.rs:211, 866-868`
**Строка:** 211, 866-868
**Категория:** Memory Leak
**Приоритет:** ВЫСОКИЙ
#### Описание
`sent_nonces` хранит nonces для обнаружения self-connections, но очистка происходит только при отключении пира. Если соединение не установлено полностью, nonce может остаться в памяти навсегда.
```866:868:Montana ACP/montana/src/net/protocol.rs
// Remove our nonce from sent_nonces to prevent memory leak
if let Some(nonce) = our_sent_nonce {
sent_nonces.write().await.remove(&nonce);
}
```
#### Сценарий эксплуатации
1. Атакующий инициирует множество неполных соединений
2. Каждое соединение добавляет nonce в `sent_nonces`
3. Если соединение не завершается нормально, nonce не удаляется
4. Память растет неограниченно
#### Влияние
- **Memory Leak**: Неограниченный рост `sent_nonces`
- **DoS**: Исчерпание памяти
#### Рекомендации
1. Добавить TTL для nonces (например, 1 час)
2. Периодически очищать старые nonces
3. Ограничить максимальный размер `sent_nonces`
---
### VULN-007: Отсутствие проверки на дубликаты в add_many для AddrMan
**Файл:** `src/net/addrman.rs:170-200`
**Строка:** 170-200
**Категория:** Logic Error
**Приоритет:** ВЫСОКИЙ
#### Описание
Метод `add_many` вызывает `add` для каждого адреса, но нет проверки на дубликаты в самом списке `addrs`. Это может привести к избыточной обработке.
#### Сценарий эксплуатации
1. Атакующий отправляет `Addr` сообщение с тысячами дубликатов одного адреса
2. Каждый дубликат проходит проверку в `add` (хотя большинство будут отклонены)
3. Это создает избыточную нагрузку на CPU
#### Влияние
- **DoS**: Избыточная обработка дубликатов
- **Performance**: Замедление работы узла
#### Рекомендации
1. Дедуплицировать адреса перед вызовом `add_many`
2. Использовать `HashSet` для быстрой проверки дубликатов
---
### VULN-008: Недостаточная валидация в PresenceProofs сообщении
**Файл:** `src/net/message.rs:198`
**Строка:** 198
**Категория:** DoS
**Приоритет:** ВЫСОКИЙ
#### Описание
`PresenceProofs` может содержать до 100 proofs (`MAX_PRESENCE_SIZE * 100`), но нет проверки на разумность этого количества. 100 proofs × 8KB = 800KB, что близко к лимиту.
```198:198:Montana ACP/montana/src/net/message.rs
"presenceproofs" => MAX_PRESENCE_SIZE * 100,
```
#### Сценарий эксплуатации
1. Атакующий отправляет `PresenceProofs` с максимальным количеством proofs
2. Каждый proof требует валидации (проверка подписи, проверка структуры)
3. Это создает высокую нагрузку на CPU
#### Влияние
- **DoS**: Высокая нагрузка на CPU при валидации
- **Performance**: Замедление обработки сообщений
#### Рекомендации
1. Добавить rate limiting для `PresenceProofs`
2. Ограничить количество proofs в одном сообщении (например, 10-20)
3. Добавить проверку на разумность размера перед валидацией
---
## СРЕДНИЙ ПРИОРИТЕТ
### VULN-009: Использование unwrap() в production коде
**Файл:** Множественные файлы
**Категория:** Reliability
**Приоритет:** СРЕДНИЙ
#### Описание
В коде используется множество `unwrap()`, которые могут привести к панике при неожиданных условиях.
#### Примеры
- `addrman.rs:436, 448, 458`: `unwrap()` при создании SipHasher
- `protocol.rs:749, 961, 1273`: `unwrap()` при парсинге IP адресов
- `verification.rs:426`: `Arc::try_unwrap().unwrap()`
#### Влияние
- **Reliability**: Возможность паники узла
- **DoS**: Атакующий может вызвать панику
#### Рекомендации
1. Заменить все `unwrap()` на обработку ошибок
2. Использовать `expect()` с понятными сообщениями только в тестах
3. Добавить graceful degradation при ошибках
---
### VULN-010: Отсутствие проверки на валидность timestamp в PresenceProof
**Файл:** `src/net/protocol.rs` (обработка Presence)
**Категория:** Time-warp Attack
**Приоритет:** СРЕДНИЙ
#### Описание
При обработке `Presence` сообщений нет явной проверки на валидность timestamp относительно текущего времени сети.
#### Сценарий эксплуатации
1. Атакующий отправляет `Presence` с будущим timestamp
2. Если проверка времени недостаточна, это может повлиять на консенсус времени
#### Влияние
- **Time-warp**: Потенциальная атака на синхронизацию времени
- **Consensus**: Влияние на консенсус времени сети
#### Рекомендации
1. Добавить явную проверку timestamp в `PresenceProof`
2. Отклонять proofs с timestamp слишком далеко в будущем/прошлом
3. Использовать сетевой консенсус времени для проверки
---
### VULN-011: Потенциальная атака на eviction через манипуляцию last_tx_time
**Файл:** `src/net/eviction.rs`
**Категория:** Eclipse Attack
**Приоритет:** СРЕДНИЙ
#### Описание
Eviction использует `last_tx_time` и `last_slice_time` для защиты пиров. Атакующий может манипулировать этими значениями, отправляя фиктивные транзакции.
#### Сценарий эксплуатации
1. Атакующий создает множество входящих соединений
2. Отправляет фиктивные транзакции через эти соединения
3. Это обновляет `last_tx_time`, защищая атакующие соединения от eviction
#### Влияние
- **Eclipse**: Потенциальная атака на eviction
- **Security**: Обход защиты от eclipse атак
#### Рекомендации
1. Добавить проверку на валидность транзакций перед обновлением `last_tx_time`
2. Использовать весовую систему для eviction (не только время)
3. Добавить проверку на качество транзакций
---
### VULN-012: Отсутствие проверки на циклы в GetHeaders locator
**Файл:** `src/net/protocol.rs` (обработка GetHeaders)
**Категория:** DoS
**Приоритет:** СРЕДНИЙ
#### Описание
`GetHeaders` содержит `locator` (список хешей), но нет проверки на циклы или чрезмерную длину.
#### Сценарий эксплуатации
1. Атакующий отправляет `GetHeaders` с очень длинным locator
2. Это создает избыточную нагрузку на обработку
#### Влияние
- **DoS**: Избыточная обработка длинных locators
- **Performance**: Замедление работы узла
#### Рекомендации
1. Ограничить длину locator (например, 100 хешей)
2. Добавить проверку на циклы в locator
3. Добавить rate limiting для GetHeaders
---
### VULN-013: Потенциальная утечка информации через error messages
**Файл:** Множественные файлы
**Категория:** Information Disclosure
**Приоритет:** СРЕДНИЙ
#### Описание
Error messages могут содержать чувствительную информацию о внутреннем состоянии узла.
#### Примеры
- Детали о внутренних структурах данных
- Информация о количестве пиров
- Детали о синхронизации
#### Влияние
- **Information Disclosure**: Утечка информации о состоянии узла
- **Reconnaissance**: Помощь в разведке для атакующего
#### Рекомендации
1. Ограничить детализацию error messages для внешних пиров
2. Использовать общие сообщения об ошибках
3. Логировать детали только локально
---
### VULN-014: Отсутствие проверки на валидность подписи перед обработкой SignedAddr
**Файл:** `src/net/protocol.rs:1036-1049`
**Строка:** 1036-1049
**Категория:** Logic Error
**Приоритет:** СРЕДНИЙ
#### Описание
При отправке `SignedAddr` от hardcoded node нет проверки на валидность подписи перед отправкой. Если подпись не удалась, отправляется unsigned `Addr`.
```1036:1049:Montana ACP/montana/src/net/protocol.rs
if let Some(ref secret_key) = config.hardcoded_secret_key {
// Serialize addresses for signing
let addrs_bytes = bincode::serialize(&addrs).unwrap_or_default();
if let Some(signature) = crate::crypto::sign_mldsa65(secret_key, &addrs_bytes) {
peer_tx.send(Message::SignedAddr { addrs, signature }).await.ok();
} else {
// Fallback to unsigned if signing fails
warn!("Failed to sign Addr message, sending unsigned");
peer_tx.send(Message::Addr(addrs)).await.ok();
}
```
#### Сценарий эксплуатации
1. Hardcoded node не может подписать адреса (проблема с ключом)
2. Отправляется unsigned `Addr` вместо `SignedAddr`
3. Получатель может не заметить отсутствие подписи
#### Влияние
- **Logic Error**: Отправка unsigned адресов от hardcoded node
- **Security**: Потенциальный обход защиты от gossip poisoning
#### Рекомендации
1. Не отправлять `Addr` если подпись не удалась
2. Логировать критическую ошибку
3. Возможно, отключить узел если подпись не работает
---
### VULN-015: Потенциальная проблема с nonce в Version сообщении
**Файл:** `src/net/protocol.rs:912-915`
**Строка:** 912-915
**Категория:** Logic Error
**Приоритет:** СРЕДНИЙ
#### Описание
Проверка на self-connection использует nonce из `Version`, но если узел перезапускается, старые nonces могут остаться в памяти.
#### Сценарий эксплуатации
1. Узел перезапускается
2. Старые nonces могут остаться в `sent_nonces` (если не очищены)
3. Новое соединение с тем же nonce может быть неправильно определено как self-connection
#### Влияние
- **Logic Error**: Неправильное определение self-connections
- **Reliability**: Проблемы с подключением после перезапуска
#### Рекомендации
1. Очищать `sent_nonces` при перезапуске
2. Использовать TTL для nonces
3. Добавить проверку на время создания nonce
---
### VULN-016: Отсутствие проверки на валидность subnet в rate limiting
**Файл:** `src/net/rate_limit.rs`
**Категория:** Logic Error
**Приоритет:** СРЕДНИЙ
#### Описание
Rate limiting использует subnet для группировки, но нет проверки на валидность IP адреса перед извлечением subnet.
#### Сценарий эксплуатации
1. Атакующий отправляет сообщения с невалидными IP адресами
2. Извлечение subnet может привести к неожиданным результатам
3. Это может обойти rate limiting
#### Влияние
- **Logic Error**: Проблемы с rate limiting
- **DoS**: Потенциальный обход защиты
#### Рекомендации
1. Валидировать IP адрес перед извлечением subnet
2. Обрабатывать edge cases (loopback, multicast, etc.)
3. Добавить проверку на валидность subnet
---
### VULN-017: Потенциальная проблема с expire_oldest в OrphanPool
**Файл:** `src/net/sync.rs:330-351`
**Строка:** 330-351
**Категория:** Logic Error
**Приоритет:** СРЕДНИЙ
#### Описание
`expire_oldest()` удаляет только один hash со всеми его orphans, но если у одного hash много orphans, это может привести к неэффективному использованию памяти. Также есть потенциальная проблема: если все orphans имеют одинаковое время получения, может быть удален не самый старый.
```330:351:Montana ACP/montana/src/net/sync.rs
fn expire_oldest(&mut self) {
let mut oldest_time = Instant::now();
let mut oldest_hash = None;
for (hash, orphans) in &self.by_prev_hash {
if let Some(orphan) = orphans.first()
&& orphan.received_at < oldest_time
{
oldest_time = orphan.received_at;
oldest_hash = Some(*hash);
}
}
if let Some(hash) = oldest_hash
&& let Some(orphans) = self.by_prev_hash.remove(&hash)
{
for orphan in orphans {
self.indices.remove(&orphan.slice.header.slice_index);
self.count = self.count.saturating_sub(1);
}
}
}
```
#### Сценарий эксплуатации
1. Атакующий отправляет множество orphans с одинаковым `prev_hash`
2. Все они имеют одинаковое время получения
3. При достижении лимита удаляется только один hash, но может быть много orphans под одним hash
4. Это может привести к неэффективному использованию памяти
#### Влияние
- **Logic Error**: Неэффективное удаление orphans
- **Memory**: Потенциальное неоптимальное использование памяти
#### Рекомендации
1. Удалять самый старый orphan, а не весь hash
2. Или удалять несколько старых orphans до достижения лимита
3. Улучшить алгоритм выбора для удаления
---
### VULN-018: Несоответствие комментария и реального размера slice в OrphanPool
**Файл:** `src/net/sync.rs:17`
**Строка:** 17
**Категория:** Documentation Error
**Приоритет:** НИЗКИЙ
#### Описание
Комментарий говорит, что каждый orphan может быть до 4MB, но реальный лимит `MAX_SLICE_SIZE = 8KB`. Это несоответствие может ввести в заблуждение разработчиков.
```17:18:Montana ACP/montana/src/net/sync.rs
/// flooding us with orphan slices. Each orphan is up to 4MB,
/// so worst case is 400MB memory usage.
```
#### Влияние
- **Documentation**: Неправильная информация о размере
- **Planning**: Неправильная оценка использования памяти
#### Рекомендации
1. Исправить комментарий: "Each orphan is up to 8KB, so worst case is 800KB memory usage"
2. Обновить документацию для соответствия реальным лимитам
---
## ЗАКЛЮЧЕНИЕ
Анализ выявил несколько критических и высокоприоритетных уязвимостей в сетевом слое Montana ACP. Наиболее серьезные проблемы связаны с:
1. **Несогласованностью лимитов размеров сообщений** - может привести к DoS
2. **Отсутствием проверки границ при десериализации** - риск memory exhaustion
3. **Race conditions** - непредсказуемое поведение в многопоточной среде
Рекомендуется немедленно исправить критические уязвимости и провести дополнительный аудит безопасности с фокусом на:
- Синхронизацию доступа к shared state
- Валидацию всех внешних данных
- Ограничение роста всех коллекций данных
- Обработку ошибок вместо паники
---
## ПРИЛОЖЕНИЕ: КАТЕГОРИИ АТАК
### DoS (Denial of Service)
- VULN-001, VULN-002, VULN-004, VULN-006, VULN-008, VULN-012
### Memory Exhaustion
- VULN-001, VULN-002, VULN-004, VULN-006
### Race Conditions
- VULN-003
### Logic Errors
- VULN-003, VULN-005, VULN-007, VULN-010, VULN-011, VULN-014, VULN-015, VULN-016, VULN-017
### Information Disclosure
- VULN-013
### Eclipse Attacks
- VULN-011
### Time-warp Attacks
- VULN-010