montana/Русский/Совет/Anthropic/атака_затмения_07.01.2026_17:00.md

11 KiB
Raw Permalink Blame History

Adversarial Review: Eclipse Attack на Montana

Модель: Claude Opus 4.5 Компания: Anthropic Дата: 07.01.2026 17:00 UTC Роль: Атакующий с неограниченными ресурсами


Attack Surface

External inputs:

  • P2P addr messages (адреса пиров)
  • DNS seeds (hardcoded в бинарнике)
  • Gossip от подключённых пиров
  • AddrMan persistence file (peers.dat)

Trust boundaries:

  • Сеть → AddrMan (NEW table)
  • Успешное соединение → TRIED table
  • AddrMan file → Memory

Critical assets:

  • Outbound connections (8 слотов)
  • Inbound connections (117 слотов)
  • Chain tip consensus

Attempted Attacks

1. NEW Table Poisoning

# Attack Target Result
1.1 Отправить 1M вредоносных адресов через addr message NEW table PROTECTED — MAX_ADDR_SIZE=1000 ограничивает одно сообщение
1.2 Отправить addr от множества connection NEW table PROTECTED — bucket assignment зависит от source IP
1.3 Заполнить все 1024 bucket одним netgroup NEW table PROTECTED — bucket = hash(key || netgroup || source_netgroup)

Анализ addrman.rs:441-450:

fn get_new_bucket(&self, addr: &SocketAddr, source: Option<&SocketAddr>) -> usize {
    let mut hasher = SipHasher24::new_with_key(&self.key[..16]);
    hasher.write(&get_netgroup_bytes(addr));
    if let Some(src) = source {
        hasher.write(&get_netgroup_bytes(src)); // Source affects bucket!
    }
    (hasher.finish() as usize) % NEW_BUCKET_COUNT
}

Verdict 1: Атакующий с одного netgroup может заполнить только ограниченное количество bucket'ов. Для полного заполнения нужны адреса из многих /16 подсетей.


2. TRIED Table Manipulation

# Attack Target Result
2.1 Fake successful connections TRIED table PROTECTED — mark_good() требует реального TCP соединения
2.2 Collision в TRIED bucket Вытеснение honest peers VULNERABLE — см. анализ ниже
2.3 Массовые mark_good() TRIED overflow PROTECTED — TRIED_BUCKET_COUNT × BUCKET_SIZE = 16384 slots

Анализ addrman.rs:196-208:

fn mark_good(&mut self, addr: &SocketAddr) {
    // ...
    let bucket = self.get_tried_bucket(addr);
    let pos = self.get_bucket_position(addr, bucket, false);
    let idx = bucket * BUCKET_SIZE + pos;

    // Handle collision
    if let Some(existing_idx) = self.tried_table[idx] {
        self.move_to_new(existing_idx);  // Existing moved to NEW!
    }
    self.tried_table[idx] = Some(addr_idx);
}

FINDING [MEDIUM]: При коллизии в TRIED table, существующий адрес перемещается обратно в NEW. Атакующий контролирующий множество IP может вытеснить honest peers из TRIED.

Mitigation: Bucket assignment использует cryptographic hash с secret key, что делает предсказание коллизий вычислительно сложным.


3. Restart Attack (Classic Eclipse)

# Attack Target Result
3.1 Заполнить tables, ждать restart Все outbound PROTECTED — full bootstrap always
3.2 Poisoned peers.dat Все connections PROTECTED — full bootstrap overrides

Анализ startup.rs:79-95:

/// Montana always uses full_bootstrap regardless of chain age:
/// - 100 peers from 25+ subnets
/// - Hardcoded nodes must match network median
pub async fn verify(&self) -> Result<StartupResult, StartupError> {
    // Always use full bootstrap for decentralized verification
    self.full_bootstrap(chain_age, local_height).await
}

PROTECTED: Montana выполняет полный bootstrap при КАЖДОМ рестарте.

Требования:

  • 100 peers (20 hardcoded + 80 P2P)
  • 25+ unique /16 subnets
  • Hardcoded must match median ±1%

Стоимость атаки: контроль 51+ peers из 25+ /16 подсетей И 15+ hardcoded nodes.


4. Netgroup Diversity Check

# Attack Target Result
4.1 Все outbound из одного /16 Outbound diversity PROTECTED
4.2 Контроль 4+ разных /16 Bypass diversity PROTECTED (частично)

Анализ connection.rs:265-269:

pub async fn can_connect(&self, addr: &SocketAddr) -> bool {
    let netgroup = get_netgroup(addr);
    let counts = self.netgroup_counts.lock().await;
    let current = counts.get(&netgroup).copied().unwrap_or(0);
    current < MAX_PEERS_PER_NETGROUP  // MAX = 2
}

Verdict: MAX_PEERS_PER_NETGROUP=2 означает атакующему нужно контролировать минимум 4 различных /16 подсети для 8 outbound. Каждая /16 = 65536 IP, это дорого.


5. Eviction Gaming

# Attack Target Result
5.1 Low latency gaming Eviction protection PROTECTED — 8 lowest-ping protected
5.2 Fake tx/slice relay activity Eviction protection PROTECTED — 4 recent tx + 4 recent slice protected
5.3 Netgroup concentration Eviction target PROTECTED — evicts from most-concentrated netgroup

Анализ eviction.rs:63-126:

Eviction защищает по слоям:

  1. NoBan — trusted peers (4+)
  2. Netgroup diversity — 4 из разных /16
  3. Lowest ping — 8 peers
  4. Recent TX relay — 4 peers
  5. Recent slice relay — 4 peers
  6. Longevity — 8 oldest connections

Total protected: 4+8+4+4+8 = 28 peers minimum

С MAX_INBOUND=117 и 28 protected, атакующий может занять 89 slots, но не вытеснить protected diversity peers.


6. Bootstrap Attack

# Attack Target Result
6.1 Контроль DNS seeds New node bootstrap PROTECTED — требует 15/20 hardcoded
6.2 Sybil атака на P2P gossip Bootstrap peers PROTECTED — subnet diversity 25+ required
6.3 Clock manipulation Time verification PROTECTED — median from 100 peers

Анализ bootstrap.rs:37-50:

pub const HARDCODED_NODE_COUNT: usize = 20;
pub const MIN_HARDCODED_RESPONSES: usize = 15;
pub const MIN_DIVERSE_SUBNETS: usize = 25;
pub const BOOTSTRAP_PEER_COUNT: usize = 100;

Security model bootstrap.rs:14-25:

  • Требует 15/20 hardcoded nodes согласны
  • 25+ уникальных /16 подсетей
  • Max 5 peers per subnet
  • Hardcoded must match median (1% tolerance)

Verdict: Двойная защита (hardcoded + diversity) требует компрометации И DNS seeds, И 51+ peers из 25+ подсетей.


7. AddrMan Persistence Attack

# Attack Target Result
7.1 Malformed peers.dat Memory corruption PROTECTED — 16MB size limit
7.2 Oversized file DoS Disk/memory exhaustion PROTECTED — MAX_ADDRMAN_FILE_SIZE check

Анализ addrman.rs:107-121:

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(/* ... */);
    }
    bincode::deserialize(&data)  // Standard bincode
}

FINDING [LOW]: bincode deserialize без explicit limits на collections. При корректном MAX_ADDRMAN_FILE_SIZE это маловероятно, но теоретически возможна memory amplification при crafted data.


Findings Summary

CRITICAL: None

HIGH: None

Montana выполняет полный bootstrap при каждом рестарте (startup.rs:79-95). Атака требует контроля 51+ peers из 25+ /16 подсетей И 15+ hardcoded nodes.

MEDIUM

M-1: TRIED Collision → Move to NEW

Поле Значение
Файл addrman.rs:196-208
Описание При коллизии в TRIED table, existing peer перемещается обратно в NEW вместо отклонения нового
Impact Постепенное вытеснение honest peers из TRIED в менее надёжную NEW table
Mitigation При коллизии сравнивать качество адресов (last_success, attempts) и сохранять лучший

LOW

L-1: Bincode Deserialize без explicit collection limits

Поле Значение
Файл addrman.rs:119, connection.rs:76-77
Описание bincode::deserialize без serde(deserialize_with) limits на HashMap/Vec
Impact При MAX_FILE_SIZE=16MB атака маловероятна, но возможна memory amplification
Mitigation Добавить explicit size limits через bincode::options().with_limit()

Checklist Results

[x] Eclipse: full bootstrap on every restart — startup.rs
[x] Eclipse: netgroup diversity — MAX_PEERS_PER_NETGROUP=2
[x] Eclipse: subnet diversity — 25+ /16 subnets required
[x] Eclipse: hardcoded verification — must match median ±1%
[x] Memory: flow control до allocation — MAX_TX_SIZE early check
[x] Memory: все collections bounded — Vec<Option<usize>> fixed size
[x] Slots: eviction защищает diversity — 28 peers protected
[x] Rate: per-IP limiting — MAX_CONNECTIONS_PER_IP=2

Verdict

[x] SAFE — можно продолжать [ ] NEEDS_FIX — исправить перед продолжением


Recommendations

Priority 1: TRIED Collision Resolution

// Вместо безусловного move_to_new:
if let Some(existing_idx) = self.tried_table[idx] {
    if let Some(existing) = self.addrs.get(&existing_idx) {
        if let Some(new_info) = self.addrs.get(&addr_idx) {
            // Сохранить peer с лучшей историей
            if existing.last_success > new_info.last_success {
                return; // Keep existing, reject new
            }
        }
    }
    self.move_to_new(existing_idx);
}

Attack Cost Analysis

Eclipse attack на Montana требует:

Требование Стоимость
51+ peers из 25+ /16 подсетей Контроль IP в 25+ datacenter/ISP
15+ hardcoded DNS seeds Компрометация инфраструктуры
Subnet diversity bypass Каждая /16 = 65536 IP адресов

Защита через:

  • Full bootstrap при каждом рестарте (100 peers, 25+ subnets)
  • Hardcoded verification (must match median ±1%)
  • Runtime netgroup diversity (MAX_PEERS_PER_NETGROUP=2)
  • Eviction protection (28 peers protected)

Ничто_Nothing_无_金元Ɉ