montana/Русский/Совет/OpenAI/аудит_сетевого_уровня_08.01.2026_14:54.md

14 KiB
Raw Blame History

Security Audit: Montana Network Layer (net/)

Модель: GPT-5.2 Компания: OpenAI Дата: 08.01.2026 14:54 UTC


1. Понимание архитектуры

Montana — это ACP: сеть фиксирует «присутствие во времени» через регулярные подписи (τ₁/τ₂), которые распространяются в реальном времени по P2P. Безопасность консенсуса опирается на (a) криптографическую неподделываемость, (b) привязку к текущему τ₂ (prev_hash / сетевое окно), (c) eventual consistency через fork-choice.

Следствие для сетевого слоя: ключевая цель сети — обеспечить доставку и валидацию внешних данных (подписей/слайсов/tx) в bounded-режиме и не дать атакующему:

  • изолировать узел на старте (bootstrap) или во время работы (eclipse/addrman poisoning)
  • исчерпать ресурсы (mem/cpu/bandwidth) через сообщения, очереди, инвентори
  • подменить доверенные источники на старте (hardcoded bootstrap anchors)

2. Изученные файлы

Сетевой слой montana/src/net/ (12,330 LOC):

  • net/message.rs (168)
  • net/encrypted.rs (570)
  • net/types.rs (658)
  • net/serde_safe.rs (266)
  • net/protocol.rs (1506)
  • net/verification.rs (848)
  • net/dns.rs (266)
  • net/feeler.rs (256)
  • net/discouraged.rs (286)
  • net/sync.rs (665)
  • net/bootstrap.rs (1413)
  • net/startup.rs (184)
  • net/subnet.rs (444)
  • net/eviction.rs (347)
  • net/mod.rs (82)
  • net/inventory.rs (705)
  • net/hardcoded_identity.rs (203)
  • net/noise.rs (898)
  • net/rate_limit.rs (879)
  • net/connection.rs (509)
  • net/peer.rs (438)
  • net/addrman.rs (739)

Связанные файлы:

  • src/types.rs, src/crypto.rs, src/db.rs, src/nmi.rs, src/nts.rs

Инструменты/утилиты:

  • src/bin/attacker.rs (stress-тест)

3. Attack Surface

  • Bootstrap / Startup verification: net/startup.rsnet/verification.rsnet/bootstrap.rs.

    • hardcoded challenge-response
    • сбор P2P адресов через GetAddr / Addr
    • принятие network time (median) и median height
  • Долгоживущая P2P-сеть: net/protocol.rs.

    • inbound/outbound TCP
    • Noise XX + ML-KEM (шифрование/аутентификация транспорта)
    • обработка сообщений (Version/Verack/Addr/Inv/GetData/Slice/Tx/Presence)
  • Механизмы устойчивости:

    • connection limits / netgroup / per-IP (net/connection.rs)
    • eviction защищённых слотов (net/eviction.rs)
    • AddrMan bucket system (net/addrman.rs) + feeler (net/feeler.rs)
    • rate limit per peer + subnet-level (fast/slow) (net/rate_limit.rs)
    • bounded collections (net/serde_safe.rs, net/message.rs)

4. Найденные уязвимости

[CRITICAL] Bootstrap MITM: подпись hardcoded узла не привязана к VersionPayload

Суть: протокол AuthChallenge/AuthResponse предполагает, что hardcoded узел доказывает идентичность подписью, но в коде проверяется подпись только над challenge, а поля version (высота, timestamp, best_slice, etc.) не входят в подписанное сообщение.

Уязвимый код (проверка подписи по одному challenge):

  • montana/src/net/verification.rs:493-515 — подпись проверяется функцией verify_hardcoded_response(&challenge, &signature) и затем используется version.
  • montana/src/net/hardcoded_identity.rs:85-112verify_hardcoded_response() проверяет verify_mldsa65(pubkey, challenge, signature).

Уязвимый код (hardcoded узел подписывает только challenge):

  • montana/src/net/protocol.rs:1175-1204sign_mldsa65(secret_key, &challenge) и отправка AuthResponse { version, signature }.

Почему это важно: на старте именно bootstrap-верификация определяет, чему узел доверяет как «правде» о сети (высота/время/seed peers). Если подпись не связывает version с challenge, атакующий «на проводе» может сохранить валидную подпись на challenge, но подменить VersionPayload (с высотой/временем/параметрами) в транзите.

Импакт: высокий риск некорректного bootstrap (неверная высота/время/peer-discovery), что может привести к:

  • изоляции узла (eclipse-like) на этапе запуска
  • выбору неправильного chain-tip/сегмента сети
  • отказу в запуске из‑за «clock divergence» или «height divergence» (liveness)

Сложность: средняя для сетевого противника на пути (ISP/BGP/локальная сеть) или при наличии вредоносного прокси; не требует компрометации приватного ключа hardcoded узла.

Проверка председателя: ПОДТВЕРЖДЕНО по коду: подпись проверяется только над challenge и никак не включает version.

Рекомендация исправления:

  • Подписывать и проверять контекст + challenge + сериализованный VersionPayload (и/или transcript):
    • sig_msg = "Montana.HardcodedAuth.v1" || challenge || postcard(version)
    • проверять verify_mldsa65(pubkey, sig_msg, signature)
  • Дополнительно: рассмотреть перенос bootstrap-канала поверх EncryptedStream (Noise), чтобы исключить MITM в принципе.

[HIGH] Централизация bootstrap: фактически 1 hardcoded IP и пустые DNS seeds

Суть: документы декларируют набор seed/hardcoded узлов и их разнообразие. В текущем коде:

  • DNS seeds пусты (комментарии-заглушки)
  • fallback IP содержит 1 адрес
  • hardcoded идентичности mainnet содержат 1 узел

Доказательства:

  • montana/src/net/dns.rs:8-32DNS_SEEDS пустой, FALLBACK_IPS содержит один адрес.
  • montana/src/net/hardcoded_identity.rs:40-66MAINNET_HARDCODED содержит один узел.

Импакт: при таком наборе «trusted core» становится фактически единственной точкой отказа:

  • DoS/недоступность узла → bootstrap деградирует/ломается
  • регуляторные/инфраструктурные события вокруг одного провайдера/географии → системный риск

Сложность: низкая (противнику достаточно бить в доступность одного узла).

Проверка председателя: ПОДТВЕРЖДЕНО по коду.

Рекомендация: расширить список hardcoded узлов (минимум 510), реально включить DNS seeds, обеспечить юрисдикционное/AS/гео разнообразие.


[MEDIUM] Несогласованность лимитов размера сообщений: в protocol.rs глобальный лимит = MAX_TX_SIZE (1MB), а в net/types.rs заявлен 2MB и inv до ~1.8MB

Суть: net/types.rs декларирует MESSAGE_SIZE_LIMIT = 2MB и MAX_INV_MSG_SIZE ≈ 1.8MB, но protocol.rs делает ранний reject любых payload > MAX_TX_SIZE (1MB) независимо от команды.

Доказательства:

  • montana/src/net/types.rs:111-140 — 2MB лимит и MAX_INV_MSG_SIZE.
  • montana/src/net/protocol.rs:1249-1263 и 1329-1334 — ранняя проверка if len > MAX_TX_SIZE.

Импакт: функциональная несовместимость со «спецификацией» внутри самого репо:

  • крупные inv сообщения по верхней границе фактически не проходят
  • возможна деградация синхронизации/relay при больших батчах

Сложность: низкая (это детерминированное поведение кода).

Проверка председателя: ПОДТВЕРЖДЕНО по коду.

Рекомендация:

  • заменить ранний лимит на MESSAGE_SIZE_LIMIT, либо
  • привести MAX_* константы к тому, что реально разрешено ранней проверкой.

[MEDIUM] ip_votes заявлен как bounded, но не очищается на disconnect → потенциальный рост памяти

Суть: комментарий говорит «max size = MAX_OUTBOUND», но ключи SocketAddr добавляются при каждом (outbound) Version и нигде не удаляются при разрыве соединения.

Доказательства:

  • Определение + заявление bounded: montana/src/net/protocol.rs:210-216.
  • Вставка голоса: montana/src/net/protocol.rs:884-914.
  • Cleanup на disconnect удаляет только sent_nonces, но не ip_votes: montana/src/net/protocol.rs:837-844.

Импакт: потенциальный медленный рост HashMap при длительной работе и смене outbound адресов.

Сложность: низкая (естественная динамика сети + перезапросы).

Проверка председателя: ПОДТВЕРЖДЕНО по коду.

Рекомендация:

  • удалять ip_votes[peer.addr] при PeerDisconnected/disconnect,
  • либо хранить bounded LRU на MAX_OUTBOUND последних адресов.

[LOW] Хранение Noise static secret key не соответствует комментарию («encrypted»), хранится как raw 32 bytes

Суть: в комментарии указано «encrypted with a derived key», но фактически ключ пишется как std::fs::write(noise_key.bin, keypair.secret).

Доказательства:

  • montana/src/net/encrypted.rs:406-435.

Импакт: риск при локальной компрометации/backup leaks; смягчается правами 0600.

Сложность: зависит от локального противника.

Проверка председателя: ПОДТВЕРЖДЕНО по коду.

Рекомендация: либо зашифровать ключ на диске (например, OS keychain), либо поправить комментарий, чтобы не вводил в заблуждение.


5. Атаки, которые НЕ работают (или работают хуже ожидаемого)

  • «Просто спамить большие inv до 1.8MB»: верхняя граница MAX_INV_MSG_SIZE теоретически есть, но protocol.rs режет всё >1MB ранней проверкой (MAX_TX_SIZE).

  • src/bin/attacker.rs как готовый эксплойт: этот бинарь сериализует сообщения через bincode и работает без Noise, тогда как текущая сеть в protocol.rs использует postcard поверх EncryptedStream (Noise). В текущем виде это скорее исторический stress-тест/заготовка и не отражает реальный on-wire протокол.


6. Рекомендации

  • Закрыть CRITICAL bootstrap MITM:

    • изменить AuthResponse так, чтобы подпись покрывала challenge || VersionPayload с domain separation.
    • (опционально, сильнее) проводить VerificationClient поверх EncryptedStream.
  • Убрать single point of failure в bootstrap:

    • заполнить DNS_SEEDS, расширить FALLBACK_IPS, добавить 510+ hardcoded identities.
  • Согласовать лимиты сообщений:

    • использовать MESSAGE_SIZE_LIMIT как ранний лимит в protocol.rs/verification.rs или уменьшить max-команды.
  • Сделать ip_votes реально bounded:

    • удаление на disconnect или bounded LRU.
  • Уточнить/исправить хранение Noise secret:

    • либо реализовать шифрование на диске, либо исправить комментарий.

7. Вердикт

[x] CRITICAL — есть уязвимость, позволяющая сломать доверие bootstrap (MITM при старте) [ ] HIGH — есть серьёзные уязвимости [ ] MEDIUM — есть уязвимости среднего риска [ ] LOW — только minor issues [ ] SECURE — уязвимостей не найдено