693 lines
33 KiB
Markdown
693 lines
33 KiB
Markdown
|
|
# 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-атаки.
|
|||
|
|
|
|||
|
|
```715:720:Montana ACP/montana/src/net/verification.rs
|
|||
|
|
// 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`), но нет проверки количества десериализованных адресов. Злоумышленник может создать файл с большим количеством адресов, что приведет к исчерпанию памяти.
|
|||
|
|
|
|||
|
|
```117:123:Montana ACP/montana/src/net/addrman.rs
|
|||
|
|
/// 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. Добавить проверку максимального количества адресов после десериализации:
|
|||
|
|
```rust
|
|||
|
|
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. Между чтением и записью другой поток может изменить состояние.
|
|||
|
|
|
|||
|
|
```925:947:Montana ACP/montana/src/net/protocol.rs
|
|||
|
|
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. Использовать одну блокировку для всей операции:
|
|||
|
|
```rust
|
|||
|
|
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ов, что приведет к росту памяти.
|
|||
|
|
|
|||
|
|
```68:77:Montana ACP/montana/src/net/subnet.rs
|
|||
|
|
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ов.
|
|||
|
|
|
|||
|
|
```46:48:Montana ACP/montana/src/net/subnet.rs
|
|||
|
|
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 может остаться в памяти навсегда.
|
|||
|
|
|
|||
|
|
```866:868:Montana ACP/montana/src/net/protocol.rs
|
|||
|
|
// 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, что близко к лимиту.
|
|||
|
|
|
|||
|
|
```198:198:Montana ACP/montana/src/net/message.rs
|
|||
|
|
"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`.
|
|||
|
|
|
|||
|
|
```1036:1049:Montana ACP/montana/src/net/protocol.rs
|
|||
|
|
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 имеют одинаковое время получения, может быть удален не самый старый.
|
|||
|
|
|
|||
|
|
```330:351:Montana ACP/montana/src/net/sync.rs
|
|||
|
|
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`. Это несоответствие может ввести в заблуждение разработчиков.
|
|||
|
|
|
|||
|
|
```17:18:Montana ACP/montana/src/net/sync.rs
|
|||
|
|
/// 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
|
|||
|
|
|
|||
|
|
|