# 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>(path: P) -> Result { 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` - неограниченная структура #### Сценарий эксплуатации 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 = 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, /// Known signers per subnet (pubkey hash -> subnet) signer_subnets: HashMap, /// Last snapshot τ₂ last_snapshot_tau2: u64, /// Snapshot reputations (frozen every τ₃) snapshot: HashMap, } ``` #### Сценарий эксплуатации 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