155 lines
11 KiB
Markdown
155 lines
11 KiB
Markdown
|
|
# Security Audit: Lottery Grinding & Bootstrap DoS
|
|||
|
|
|
|||
|
|
**Модель:** Gemini 3 Pro
|
|||
|
|
**Компания:** Google
|
|||
|
|
**Дата:** 08.01.2026 16:30 UTC
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Понимание архитектуры
|
|||
|
|
|
|||
|
|
Montana ACP (Atemporal Coordinate Presence) — это сеть, где консенсус достигается не через PoW/PoS, а через "накопление присутствия" (время). Узлы подписывают временные слоты (таймслайсы), доказывая своё существование. Победитель раунда (каждые 10 минут, τ₂) выбирается через детерминированную лотерею.
|
|||
|
|
|
|||
|
|
Ключевые механизмы защиты:
|
|||
|
|
1. **Детерминированная лотерея**: Должна зависеть только от *предыдущего* состояния сети (хеш прошлого слайса), чтобы исключить манипуляции (grinding) в текущем раунде.
|
|||
|
|
2. **Adaptive Cooldown**: Защита от Sybil через временнУю стоимость входа (1-180 дней).
|
|||
|
|
3. **Adaptive Subnet Limiter**: Защита от сетевых атак (Erebus/DoS) через двухуровневые лимиты подсетей.
|
|||
|
|
|
|||
|
|
Я изучил архитектуру и обнаружил критическую уязвимость в реализации консенсуса.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Изученные файлы
|
|||
|
|
|
|||
|
|
| Файл | LOC | Ключевые компоненты |
|
|||
|
|
|------|-----|---------------------|
|
|||
|
|
| `montana/src/consensus.rs` | 271 | Логика лотереи, расчет seed, проверка победителя |
|
|||
|
|
| `montana/src/net/rate_limit.rs` | 1183 | Реализация лимитеров, AdaptiveSubnetLimiter |
|
|||
|
|
| `montana/src/net/protocol.rs` | 1653 | Основной сетевой протокол, обработка сообщений |
|
|||
|
|
| `montana/src/net/bootstrap.rs` | 1254 | Логика начальной загрузки и верификации |
|
|||
|
|
| `montana/src/cooldown.rs` | 262 | Реализация адаптивного кулдауна |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Attack Surface
|
|||
|
|
|
|||
|
|
Я определил следующие векторы атак:
|
|||
|
|
1. **Манипуляция лотереей (Grinding)**: Возможность влиять на выбор победителя через изменение содержимого блока.
|
|||
|
|
2. **Блокировка запуска (Bootstrap DoS)**: Манипуляция медианой сети для принудительного отказа в обслуживании новым узлам.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Найденные уязвимости
|
|||
|
|
|
|||
|
|
### [CRITICAL] Lottery Grinding (Манипуляция лотереей)
|
|||
|
|
|
|||
|
|
**Файл:** `montana/src/consensus.rs:23`
|
|||
|
|
|
|||
|
|
**Уязвимый код:**
|
|||
|
|
```rust
|
|||
|
|
pub fn compute_lottery_seed(prev_hash: &Hash, tau2_index: u64, presence_root: &Hash) -> Hash {
|
|||
|
|
let mut data = Vec::with_capacity(72);
|
|||
|
|
data.extend_from_slice(prev_hash);
|
|||
|
|
data.extend_from_slice(&tau2_index.to_le_bytes());
|
|||
|
|
data.extend_from_slice(presence_root); // <--- КРИТИЧЕСКАЯ ОШИБКА
|
|||
|
|
sha3(&data)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Описание:**
|
|||
|
|
В документации (`layer_1.md`) явно указано, что `seed = SHA3-256(prev_slice_hash ‖ τ₂_index)`. Это гарантирует, что победитель определен *до* формирования блока.
|
|||
|
|
Однако в коде в расчет `seed` включен `presence_root` *текущего* блока.
|
|||
|
|
Поскольку `presence_root` зависит от набора подписей, включенных в блок, а блок формирует сам победитель, это создает циклическую зависимость, которую атакующий может эксплуатировать.
|
|||
|
|
|
|||
|
|
**Вектор атаки:**
|
|||
|
|
1. Атакующий (даже с минимальным весом) собирает подписи присутствия (presence signatures) от других узлов.
|
|||
|
|
2. Вместо публикации всех подписей, атакующий перебирает различные подмножества подписей.
|
|||
|
|
3. Для каждого подмножества вычисляется новый `presence_root`.
|
|||
|
|
4. Новый `presence_root` дает новый `seed` лотереи.
|
|||
|
|
5. Новый `seed` меняет победителя (функция `find_winner`).
|
|||
|
|
6. Атакующий повторяет перебор (Grinding), пока не найдет такой `presence_root`, при котором `seed` делает **его** победителем.
|
|||
|
|
7. Атакующий публикует валидный блок, где он — законный победитель.
|
|||
|
|
|
|||
|
|
**Импакт:**
|
|||
|
|
Атакующий с минимальным весом может выигрывать лотерею практически в каждом раунде, просто перебирая комбинации подписей. Это полностью ломает модель консенсуса, основанную на времени/весе, и превращает её в PoW-гонку (где "работа" — это хеширование комбинаций подписей). Захват сети возможен с одним слабым узлом.
|
|||
|
|
|
|||
|
|
**Сложность:** Низкая. Требуется только CPU для хеширования.
|
|||
|
|
|
|||
|
|
**PoC сценарий:**
|
|||
|
|
```rust
|
|||
|
|
// Псевдокод атаки
|
|||
|
|
let signatures = collect_mempool_signatures();
|
|||
|
|
let my_pubkey = my_keypair.public;
|
|||
|
|
|
|||
|
|
for subset in signatures.powerset() {
|
|||
|
|
let root = compute_merkle_root(subset);
|
|||
|
|
let seed = compute_lottery_seed(prev_hash, index, root);
|
|||
|
|
let (winner, _) = find_winner(seed, all_participants);
|
|||
|
|
|
|||
|
|
if winner == my_pubkey {
|
|||
|
|
publish_block(subset); // PROFIT!
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### [MEDIUM] Bootstrap DoS via Consensus Manipulation
|
|||
|
|
|
|||
|
|
**Файл:** `montana/src/net/bootstrap.rs:326` (verify_hardcoded_consensus)
|
|||
|
|
|
|||
|
|
**Описание:**
|
|||
|
|
При запуске узел опрашивает 100 пиров (из которых ~20 hardcoded). Он вычисляет медиану высоты цепи по всем ответам. Затем он требует, чтобы *каждый* hardcoded узел соответствовал этой медиане (с допуском 1%). Если hardcoded узлы отклоняются (потому что медиана смещена атакой), загрузка прерывается с ошибкой CRITICAL.
|
|||
|
|
|
|||
|
|
**Вектор атаки:**
|
|||
|
|
Атакующий окружает новый узел своими пирами (Eclipse) и сообщает ложную высоту (например, сильно большую или малую). Медиана по всем 100 пирам смещается в сторону атакующего. Честные hardcoded узлы (которые сообщают реальную высоту) оказываются "несогласными" с медианой. Узел считает, что hardcoded узлы скомпрометированы, и аварийно останавливается.
|
|||
|
|
|
|||
|
|
**Импакт:**
|
|||
|
|
Полный отказ в обслуживании (DoS) для новых узлов. Они не могут присоединиться к сети.
|
|||
|
|
|
|||
|
|
**Сложность:** Средняя (требуется контроль входящих соединений жертвы).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Атаки, которые НЕ работают
|
|||
|
|
|
|||
|
|
1. **Erebus / Subnet Flood**: Код `GlobalSubnetLimiter` в `rate_limit.rs` корректно реализует двухуровневую защиту (Fast/Slow tiers) и правильно интегрирован в `protocol.rs` (вызовы `subnet_limiter.check()`). Это защищает от атак с использованием множества IP из одной подсети.
|
|||
|
|
2. **Sybil (регистрация)**: `AdaptiveCooldown` реализован корректно в `cooldown.rs`. Спам регистрациями действительно приведет к увеличению кулдауна до 180 дней.
|
|||
|
|
3. **Fake Hardcoded Nodes**: `HardcodedNode` аутентификация через ML-DSA-65 реализована и работает. Подделать hardcoded узел без приватного ключа нельзя.
|
|||
|
|
4. **Inventory Exhaustion**: `BoundedInvSet` использует FIFO удаление старых элементов, что защищает память даже при флуде уникальными инвентарными хешами.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Рекомендации
|
|||
|
|
|
|||
|
|
1. **Исправить Lottery Grinding (СРОЧНО)**:
|
|||
|
|
Исключить `presence_root` из расчета `seed` в `consensus.rs`. Seed должен зависеть *только* от `prev_hash` и `index`.
|
|||
|
|
```rust
|
|||
|
|
// Было:
|
|||
|
|
// data.extend_from_slice(presence_root);
|
|||
|
|
// Стало:
|
|||
|
|
// (удалить эту строку)
|
|||
|
|
```
|
|||
|
|
Это сделает лотерею честной: победитель определяется до того, как он сможет манипулировать содержимым блока.
|
|||
|
|
|
|||
|
|
2. **Улучшить логику Bootstrap**:
|
|||
|
|
Вместо аварийной остановки при несогласии hardcoded узлов с медианой сети, следует использовать "Trusted Core" подход:
|
|||
|
|
1. Собрать ответы от всех hardcoded узлов.
|
|||
|
|
2. Вычислить медиану ТОЛЬКО среди hardcoded узлов (`hardcoded_median`).
|
|||
|
|
3. Проверить консенсус внутри hardcoded группы (сколько узлов в пределах ±1% от `hardcoded_median`).
|
|||
|
|
4. Если большинство (>50%) hardcoded узлов согласны → считать `hardcoded_median` истинной.
|
|||
|
|
5. P2P узлы проверять относительно этой доверенной медианы (warning при расхождении, но не блокировка).
|
|||
|
|
6. Hardcoded узлы, которые не согласны с `hardcoded_median` (меньшинство), игнорируются, но не вызывают падения.
|
|||
|
|
Это сделает bootstrap устойчивым к Eclipse-атакам, так как атакующий не может подделать подписи hardcoded узлов.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Вердикт
|
|||
|
|
|
|||
|
|
[x] CRITICAL — есть уязвимости, позволяющие уничтожить сеть (Lottery Grinding позволяет захватить контроль над блокчейном).
|
|||
|
|
[ ] HIGH
|
|||
|
|
[ ] MEDIUM
|
|||
|
|
[ ] LOW
|
|||
|
|
[ ] SECURE
|