montana/Русский/Совет/Cursor/аудит_сетевого_уровня_15.01.2025_20:00.md

27 KiB
Raw Permalink Blame History

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

Уязвимый код:

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 сценарий:

# Псевдокод атаки
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

Уязвимый код:

// 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 сценарий:

# Псевдокод атаки
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

Уязвимый код:

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 сценарий:

# Псевдокод атаки
1. Подключиться к целевому узлу
2. Выполнить handshake
3. Сгенерировать один валидный hash h
4. Создать Inv сообщение с 50,000 копий InvItem(InvType::Slice, h)
5. Отправить это сообщение
6. Повторить шаги 3-5 с разными hashes
7. Узел будет обрабатывать миллионы дубликатов, потребляя CPU

Рекомендация: Дедуплицировать элементы внутри Inv сообщения перед обработкой:

let unique_items: Vec<InvItem> = items.iter()
    .unique_by(|item| item.hash)
    .cloned()
    .collect();

[LOW] Потенциальная утечка памяти в ip_votes при частых переподключениях

Файл: protocol.rs:897, protocol.rs:834-839

Уязвимый код:

// При получении 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 сценарий:

# Псевдокод атаки
1. Получить доступ к множеству IP адресов
2. Для каждого IP:
    3. Подключиться к целевому узлу (outbound)
    4. Отправить Version с "голосом" за фейковый IP
    5. Немедленно отключиться
    6. Повторить с новым IP
7. После 10,000 переподключений ip_votes будет содержать 10,000 записей

Рекомендация: Очищать ip_votes при отключении outbound пира:

if !peer.inbound {
    ip_votes.write().await.remove(&addr);
}

[LOW] Потенциальная атака через false positives в DiscouragedFilter

Файл: discouraged.rs:13-105

Уязвимый код:

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 сценарий:

# Псевдокод атаки (теоретический)
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 для уникальности

Улучшения:

  1. Очищать ip_votes при отключении (protocol.rs:834-839):

    • Добавить очистку ip_votes при отключении outbound пира
  2. Мониторинг false positives (discouraged.rs):

    • Добавить метрики для отслеживания частоты false positives в DiscouragedFilter
    • При необходимости увеличить размер фильтра или уменьшить false positive rate
  3. Валидация содержимого сообщений:

    • Добавить более строгую валидацию содержимого сообщений (например, проверка типов, диапазонов значений)
    • Добавить метрики для отслеживания аномальных паттернов в сообщениях

7. Вердикт

  • CRITICAL — есть уязвимости, позволяющие уничтожить сеть
  • 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.