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

33 KiB
Raw Blame 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-атаки.

    // 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), но нет проверки количества десериализованных адресов. Злоумышленник может создать файл с большим количеством адресов, что приведет к исчерпанию памяти.

    /// 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. Добавить проверку максимального количества адресов после десериализации:
    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. Между чтением и записью другой поток может изменить состояние.

                    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. Использовать одну блокировку для всей операции:
    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ов, что приведет к росту памяти.

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ов.

    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 может остаться в памяти навсегда.

        // 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, что близко к лимиту.

            "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.

                    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 имеют одинаковое время получения, может быть удален не самый старый.

    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. Это несоответствие может ввести в заблуждение разработчиков.

    /// 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