33 KiB
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-атаки.
// 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
Сценарий эксплуатации
- Атакующий подключается к жертве как hardcoded node (или подделывает ответ)
- Отправляет сообщение размером 1.5MB (больше основного лимита, но меньше лимита verification)
- Это может привести к:
- Несогласованности в обработке сообщений
- Потенциальному обходу защиты от DoS
- Проблемам с памятью при десериализации
Влияние
- DoS: Возможность отправки больших сообщений во время bootstrap
- Memory Exhaustion: Риск исчерпания памяти на узлах
- Inconsistency: Разные лимиты в разных частях кода
Сложность эксплуатации
Низкая - Требуется только подключение к узлу во время bootstrap
Рекомендации
- Использовать
MAX_TX_SIZE(1MB) вместо жестко закодированного2MB - Унифицировать все лимиты размеров сообщений через константы из
types.rs - Добавить проверку размера до десериализации
VULN-002: Отсутствие проверки границ при десериализации AddrMan
Файл: src/net/addrman.rs:117-123
Строка: 117-123
Категория: DoS / Memory Exhaustion
Приоритет: КРИТИЧЕСКИЙ
Описание
При загрузке AddrMan из файла проверяется только размер файла (MAX_ADDRMAN_FILE_SIZE = 16MB), но нет проверки количества десериализованных адресов. Злоумышленник может создать файл с большим количеством адресов, что приведет к исчерпанию памяти.
/// 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>- неограниченная структура
Сценарий эксплуатации
- Атакующий создает поддельный
addresses.datс миллионами адресов - Файл укладывается в лимит 16MB (можно использовать сжатие или оптимизированную сериализацию)
- При загрузке узла происходит десериализация всех адресов
- Это приводит к:
- Исчерпанию памяти (каждый адрес ~100 байт, миллион адресов = ~100MB)
- Замедлению работы узла
- Потенциальному DoS
Влияние
- Memory Exhaustion: Возможность исчерпания памяти при загрузке
- DoS: Замедление работы узла
- Persistence: Атака сохраняется в файле
Сложность эксплуатации
Средняя - Требуется доступ к файловой системе узла или компрометация источника загрузки
Рекомендации
- Добавить проверку максимального количества адресов после десериализации:
const MAX_ADDRS: usize = 100_000; // Разумный лимит if addrman.addrs.len() > MAX_ADDRS { return Err(...); } - Проверять
new_count + tried_countпротив лимита - Добавить валидацию структуры данных после десериализации
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. Между чтением и записью другой поток может изменить состояние.
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));
}
}
Уязвимый код
- Запись в
ip_votes(строка 925) - Освобождение блокировки
- Чтение из
ip_votes(строка 928) - другая блокировка - Между шагами 2 и 3 другой поток может изменить данные
Сценарий эксплуатации
- Атакующий контролирует несколько исходящих соединений (Sybil)
- Отправляет разные IP-адреса в
Versionсообщениях - Из-за race condition может произойти:
- Неправильный подсчет голосов
- Установка неправильного внешнего IP
- Потенциальная атака на маршрутизацию
Влияние
- Logic Error: Неправильное определение внешнего IP
- Security: Потенциальная атака на маршрутизацию
- Race Condition: Непредсказуемое поведение в многопоточной среде
Сложность эксплуатации
Средняя - Требуется контроль над несколькими исходящими соединениями
Рекомендации
- Использовать одну блокировку для всей операции:
let mut votes = ip_votes.write().await; votes.insert(peer.addr, their_view_of_us); // Проверка консенсуса внутри той же блокировки - Или использовать атомарные операции для подсчета
- Добавить проверку на изменение состояния между операциями
ВЫСОКИЙ ПРИОРИТЕТ
VULN-004: Неограниченный рост HashMap в SubnetTracker
Файл: src/net/subnet.rs:68-77
Строка: 68-77
Категория: DoS / Memory Exhaustion
Приоритет: ВЫСОКИЙ
Описание
SubnetTracker хранит репутацию подnetов в HashMap, но нет лимита на количество отслеживаемых подnetов. Атакующий может создать множество подnetов, что приведет к росту памяти.
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>,
}
Сценарий эксплуатации
- Атакующий создает множество узлов из разных /16 подnetов
- Каждый узел отправляет presence proofs
SubnetTrackerнакапливает репутацию для каждого поднета- Память растет неограниченно
Влияние
- Memory Exhaustion: Неограниченный рост памяти
- DoS: Замедление работы узла
Рекомендации
- Добавить лимит на количество отслеживаемых подnetов (например, 10,000)
- Использовать LRU eviction для старых подnetов
- Периодически очищать неактивные поднеты
VULN-005: Отсутствие проверки на переполнение в saturating_add
Файл: src/net/subnet.rs:46
Строка: 46
Категория: Logic Error / Integer Overflow
Приоритет: ВЫСОКИЙ
Описание
Использование saturating_add скрывает переполнение, что может привести к неправильным расчетам весов подnetов.
pub fn add_weight(&mut self, weight: u64, tau2: u64) {
self.total_weight = self.total_weight.saturating_add(weight);
self.last_seen_tau2 = tau2;
}
Сценарий эксплуатации
- Атакующий отправляет множество presence proofs с большими весами
total_weightдостигаетu64::MAX- Дальнейшие добавления игнорируются (saturating_add)
- Это может привести к неправильным расчетам репутации
Влияние
- Logic Error: Неправильные расчеты весов
- Security: Потенциальная манипуляция репутацией
Рекомендации
- Добавить проверку на переполнение и логирование предупреждений
- Использовать
checked_addи обрабатывать переполнение явно - Добавить лимит на максимальный вес поднета
VULN-006: Потенциальная утечка памяти в sent_nonces
Файл: src/net/protocol.rs:211, 866-868
Строка: 211, 866-868
Категория: Memory Leak
Приоритет: ВЫСОКИЙ
Описание
sent_nonces хранит nonces для обнаружения self-connections, но очистка происходит только при отключении пира. Если соединение не установлено полностью, nonce может остаться в памяти навсегда.
// Remove our nonce from sent_nonces to prevent memory leak
if let Some(nonce) = our_sent_nonce {
sent_nonces.write().await.remove(&nonce);
}
Сценарий эксплуатации
- Атакующий инициирует множество неполных соединений
- Каждое соединение добавляет nonce в
sent_nonces - Если соединение не завершается нормально, nonce не удаляется
- Память растет неограниченно
Влияние
- Memory Leak: Неограниченный рост
sent_nonces - DoS: Исчерпание памяти
Рекомендации
- Добавить TTL для nonces (например, 1 час)
- Периодически очищать старые nonces
- Ограничить максимальный размер
sent_nonces
VULN-007: Отсутствие проверки на дубликаты в add_many для AddrMan
Файл: src/net/addrman.rs:170-200
Строка: 170-200
Категория: Logic Error
Приоритет: ВЫСОКИЙ
Описание
Метод add_many вызывает add для каждого адреса, но нет проверки на дубликаты в самом списке addrs. Это может привести к избыточной обработке.
Сценарий эксплуатации
- Атакующий отправляет
Addrсообщение с тысячами дубликатов одного адреса - Каждый дубликат проходит проверку в
add(хотя большинство будут отклонены) - Это создает избыточную нагрузку на CPU
Влияние
- DoS: Избыточная обработка дубликатов
- Performance: Замедление работы узла
Рекомендации
- Дедуплицировать адреса перед вызовом
add_many - Использовать
HashSetдля быстрой проверки дубликатов
VULN-008: Недостаточная валидация в PresenceProofs сообщении
Файл: src/net/message.rs:198
Строка: 198
Категория: DoS
Приоритет: ВЫСОКИЙ
Описание
PresenceProofs может содержать до 100 proofs (MAX_PRESENCE_SIZE * 100), но нет проверки на разумность этого количества. 100 proofs × 8KB = 800KB, что близко к лимиту.
"presenceproofs" => MAX_PRESENCE_SIZE * 100,
Сценарий эксплуатации
- Атакующий отправляет
PresenceProofsс максимальным количеством proofs - Каждый proof требует валидации (проверка подписи, проверка структуры)
- Это создает высокую нагрузку на CPU
Влияние
- DoS: Высокая нагрузка на CPU при валидации
- Performance: Замедление обработки сообщений
Рекомендации
- Добавить rate limiting для
PresenceProofs - Ограничить количество proofs в одном сообщении (например, 10-20)
- Добавить проверку на разумность размера перед валидацией
СРЕДНИЙ ПРИОРИТЕТ
VULN-009: Использование unwrap() в production коде
Файл: Множественные файлы
Категория: Reliability
Приоритет: СРЕДНИЙ
Описание
В коде используется множество unwrap(), которые могут привести к панике при неожиданных условиях.
Примеры
addrman.rs:436, 448, 458:unwrap()при создании SipHasherprotocol.rs:749, 961, 1273:unwrap()при парсинге IP адресовverification.rs:426:Arc::try_unwrap().unwrap()
Влияние
- Reliability: Возможность паники узла
- DoS: Атакующий может вызвать панику
Рекомендации
- Заменить все
unwrap()на обработку ошибок - Использовать
expect()с понятными сообщениями только в тестах - Добавить graceful degradation при ошибках
VULN-010: Отсутствие проверки на валидность timestamp в PresenceProof
Файл: src/net/protocol.rs (обработка Presence)
Категория: Time-warp Attack
Приоритет: СРЕДНИЙ
Описание
При обработке Presence сообщений нет явной проверки на валидность timestamp относительно текущего времени сети.
Сценарий эксплуатации
- Атакующий отправляет
Presenceс будущим timestamp - Если проверка времени недостаточна, это может повлиять на консенсус времени
Влияние
- Time-warp: Потенциальная атака на синхронизацию времени
- Consensus: Влияние на консенсус времени сети
Рекомендации
- Добавить явную проверку timestamp в
PresenceProof - Отклонять proofs с timestamp слишком далеко в будущем/прошлом
- Использовать сетевой консенсус времени для проверки
VULN-011: Потенциальная атака на eviction через манипуляцию last_tx_time
Файл: src/net/eviction.rs
Категория: Eclipse Attack
Приоритет: СРЕДНИЙ
Описание
Eviction использует last_tx_time и last_slice_time для защиты пиров. Атакующий может манипулировать этими значениями, отправляя фиктивные транзакции.
Сценарий эксплуатации
- Атакующий создает множество входящих соединений
- Отправляет фиктивные транзакции через эти соединения
- Это обновляет
last_tx_time, защищая атакующие соединения от eviction
Влияние
- Eclipse: Потенциальная атака на eviction
- Security: Обход защиты от eclipse атак
Рекомендации
- Добавить проверку на валидность транзакций перед обновлением
last_tx_time - Использовать весовую систему для eviction (не только время)
- Добавить проверку на качество транзакций
VULN-012: Отсутствие проверки на циклы в GetHeaders locator
Файл: src/net/protocol.rs (обработка GetHeaders)
Категория: DoS
Приоритет: СРЕДНИЙ
Описание
GetHeaders содержит locator (список хешей), но нет проверки на циклы или чрезмерную длину.
Сценарий эксплуатации
- Атакующий отправляет
GetHeadersс очень длинным locator - Это создает избыточную нагрузку на обработку
Влияние
- DoS: Избыточная обработка длинных locators
- Performance: Замедление работы узла
Рекомендации
- Ограничить длину locator (например, 100 хешей)
- Добавить проверку на циклы в locator
- Добавить rate limiting для GetHeaders
VULN-013: Потенциальная утечка информации через error messages
Файл: Множественные файлы
Категория: Information Disclosure
Приоритет: СРЕДНИЙ
Описание
Error messages могут содержать чувствительную информацию о внутреннем состоянии узла.
Примеры
- Детали о внутренних структурах данных
- Информация о количестве пиров
- Детали о синхронизации
Влияние
- Information Disclosure: Утечка информации о состоянии узла
- Reconnaissance: Помощь в разведке для атакующего
Рекомендации
- Ограничить детализацию error messages для внешних пиров
- Использовать общие сообщения об ошибках
- Логировать детали только локально
VULN-014: Отсутствие проверки на валидность подписи перед обработкой SignedAddr
Файл: src/net/protocol.rs:1036-1049
Строка: 1036-1049
Категория: Logic Error
Приоритет: СРЕДНИЙ
Описание
При отправке SignedAddr от hardcoded node нет проверки на валидность подписи перед отправкой. Если подпись не удалась, отправляется unsigned Addr.
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();
}
Сценарий эксплуатации
- Hardcoded node не может подписать адреса (проблема с ключом)
- Отправляется unsigned
AddrвместоSignedAddr - Получатель может не заметить отсутствие подписи
Влияние
- Logic Error: Отправка unsigned адресов от hardcoded node
- Security: Потенциальный обход защиты от gossip poisoning
Рекомендации
- Не отправлять
Addrесли подпись не удалась - Логировать критическую ошибку
- Возможно, отключить узел если подпись не работает
VULN-015: Потенциальная проблема с nonce в Version сообщении
Файл: src/net/protocol.rs:912-915
Строка: 912-915
Категория: Logic Error
Приоритет: СРЕДНИЙ
Описание
Проверка на self-connection использует nonce из Version, но если узел перезапускается, старые nonces могут остаться в памяти.
Сценарий эксплуатации
- Узел перезапускается
- Старые nonces могут остаться в
sent_nonces(если не очищены) - Новое соединение с тем же nonce может быть неправильно определено как self-connection
Влияние
- Logic Error: Неправильное определение self-connections
- Reliability: Проблемы с подключением после перезапуска
Рекомендации
- Очищать
sent_noncesпри перезапуске - Использовать TTL для nonces
- Добавить проверку на время создания nonce
VULN-016: Отсутствие проверки на валидность subnet в rate limiting
Файл: src/net/rate_limit.rs
Категория: Logic Error
Приоритет: СРЕДНИЙ
Описание
Rate limiting использует subnet для группировки, но нет проверки на валидность IP адреса перед извлечением subnet.
Сценарий эксплуатации
- Атакующий отправляет сообщения с невалидными IP адресами
- Извлечение subnet может привести к неожиданным результатам
- Это может обойти rate limiting
Влияние
- Logic Error: Проблемы с rate limiting
- DoS: Потенциальный обход защиты
Рекомендации
- Валидировать IP адрес перед извлечением subnet
- Обрабатывать edge cases (loopback, multicast, etc.)
- Добавить проверку на валидность subnet
VULN-017: Потенциальная проблема с expire_oldest в OrphanPool
Файл: src/net/sync.rs:330-351
Строка: 330-351
Категория: Logic Error
Приоритет: СРЕДНИЙ
Описание
expire_oldest() удаляет только один hash со всеми его orphans, но если у одного hash много orphans, это может привести к неэффективному использованию памяти. Также есть потенциальная проблема: если все orphans имеют одинаковое время получения, может быть удален не самый старый.
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);
}
}
}
Сценарий эксплуатации
- Атакующий отправляет множество orphans с одинаковым
prev_hash - Все они имеют одинаковое время получения
- При достижении лимита удаляется только один hash, но может быть много orphans под одним hash
- Это может привести к неэффективному использованию памяти
Влияние
- Logic Error: Неэффективное удаление orphans
- Memory: Потенциальное неоптимальное использование памяти
Рекомендации
- Удалять самый старый orphan, а не весь hash
- Или удалять несколько старых orphans до достижения лимита
- Улучшить алгоритм выбора для удаления
VULN-018: Несоответствие комментария и реального размера slice в OrphanPool
Файл: src/net/sync.rs:17
Строка: 17
Категория: Documentation Error
Приоритет: НИЗКИЙ
Описание
Комментарий говорит, что каждый orphan может быть до 4MB, но реальный лимит MAX_SLICE_SIZE = 8KB. Это несоответствие может ввести в заблуждение разработчиков.
/// flooding us with orphan slices. Each orphan is up to 4MB,
/// so worst case is 400MB memory usage.
Влияние
- Documentation: Неправильная информация о размере
- Planning: Неправильная оценка использования памяти
Рекомендации
- Исправить комментарий: "Each orphan is up to 8KB, so worst case is 800KB memory usage"
- Обновить документацию для соответствия реальным лимитам
ЗАКЛЮЧЕНИЕ
Анализ выявил несколько критических и высокоприоритетных уязвимостей в сетевом слое Montana ACP. Наиболее серьезные проблемы связаны с:
- Несогласованностью лимитов размеров сообщений - может привести к DoS
- Отсутствием проверки границ при десериализации - риск memory exhaustion
- 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