# Malicious authority can permanently kill an exodus signing session via an undecodable nonce commitment

**URL:** https://ghostforum.org/t/malicious-authority-can-permanently-kill-an-exodus-signing-session-via-an-undecodable-nonce-commitment/103
**Category:** Resolved
**Tags:** exodus-pallet
**Created:** [October 2, 2026, 11:28am UTC](https://ghostforum.org/t/malicious-authority-can-permanently-kill-an-exodus-signing-session-via-an-undecodable-nonce-commitment/103 "2026-10-02T11:28:07Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![Rusdiwireng](https://ghostforum.org/letter_avatar_proxy/v4/letter/r/9de0a6/32.png) [@Rusdiwireng](https://ghostforum.org/u/Rusdiwireng)
#### Post date: [October 2, 2026, 11:28am UTC](https://ghostforum.org/t/malicious-authority-can-permanently-kill-an-exodus-signing-session-via-an-undecodable-nonce-commitment/103/1 "2026-10-02T11:28:07Z")

</div>

**Component:** `pallets/exodus` @ `0162da46`, `src/lib.rs:3061-3115` (`validate_exodus_nonce_commitment_size_and_get_metadata`), `src/impls/frost.rs` (`generate_group_commitment`), `src/lib.rs:3405-3412, 3436-3444` (exile).

**Description:** The on-chain validation of `register_nonce_commitment` checks only byte LENGTH (`package.hiding_commitment.len() != element_bytes_len()`, lib.rs:3072-3078), not that the bytes are a valid curve point. A malicious active authority can submit 33 bytes of garbage (e.g. `[0xFF; 33]`, invalid prefix) that passes `validate_unsigned` and lands on chain. Then EVERY honest authority running `generate_group_commitment` offchain fails: the `filter_map` in `impls/frost.rs` drops the undecodable entry, then `nonce_commitments_bytes.len() != signing_commitments.len()` → `Err(InvalidParticipantId)`. The handler (`exodus_run_process`, lib.rs:3405-3412) calls `exodus_records.exclude_exodus_session(session)`: the node bans that session permanently in local storage (`release_block: Default`, `types/local_storage.rs:309-316`). Since all honest nodes fail deterministically on the same on-chain data, the exodus session dies for everyone. There is no on-chain mechanism to identify or eject the bad-nonce sender (unlike DKG which has complaint rounds 3-4). The attacker repeats one bad nonce per new session for persistent DoS of all exodus signing, defeating the ROAST resilience guarantee against unresponsive/malicious signers.

**Impact:** Liveness: all exodus requests (bridge-out, rotation, governance) stall permanently while the attacker stays an active authority. No direct fund loss.

**Reproduction:** Local PoC: faithfully replicates the `generate_group_commitment` filter (`impls/frost.rs`): 3 nonce commitments (2 valid generator points + 1 `[0xFF; 33]` that passes the on-chain length check) → only 2 decode → `nonce_commitments_bytes.len() != signing_commitments.len()` → `Err(InvalidParticipantId)`. Executed 2026-10-02: `submitted nonces: 3, successfully decoded: 2`. The downstream on-chain chain (failure → `exclude_exodus_session` at lib.rs:3405-3412) verified by code reading. Integration test `poc_garbage_nonce_breaks_group_commitment_for_everyone` added in `pallets/exodus/src/tests.rs`.

**Severity: Medium.** Reason: requires an insider (active authority / compromised validator key), so likelihood is low, but the impact is total and persistent DoS of the signing pipeline with no on-chain recovery path. Not High because validator privilege is required.

**Recommendation:** Add curve-point deserialization checks in `validate_exodus_nonce_commitment_size_and_get_metadata` (and in `validate_unsigned`): reject nonce commitments that are not valid points before they enter the mempool/chain. Alternative: identify the culprit offchain (each nonce is recorded per `authority_index` in `NonceCommitments`) and exclude them from the next session instead of banning the whole session.

**Verification:** VERIFIED (PoC executed; on-chain continuation verified by code reading).

---

<div class="post-metadata">

### Author: ![VoidDragon](https://ghostforum.org/user_avatar/ghostforum.org/voiddragon/32/16_2.png) [@VoidDragon](https://ghostforum.org/u/VoidDragon)
#### Post date: [October 3, 2026, 9:40pm UTC](https://ghostforum.org/t/malicious-authority-can-permanently-kill-an-exodus-signing-session-via-an-undecodable-nonce-commitment/103/2 "2026-10-03T21:40:03Z")

</div>

Thank you for your report!

You are absolutely right here. Which of the two options you’ve given would you consider preferable? In my view, this thread deserves to be elevated. If you’re interested in continuing the research on abuses from the validator side of the protocol, you’re welcome to do so!

---

<div class="post-metadata">

### Author: ![Rusdiwireng](https://ghostforum.org/letter_avatar_proxy/v4/letter/r/9de0a6/32.png) [@Rusdiwireng](https://ghostforum.org/u/Rusdiwireng)
#### Post date: [October 4, 2026, 3:54pm UTC](https://ghostforum.org/t/malicious-authority-can-permanently-kill-an-exodus-signing-session-via-an-undecodable-nonce-commitment/103/3 "2026-10-04T15:54:02Z")

</div>

Thanks for confirming. Of the two options, I’d pick rejecting undecodable points at validation time (option 1) as the primary fix:

- It closes the root cause: bytes that can never be used should never be admitted on chain. Decompressing a 33-byte compressed point is cheap, so the extra check inside `validate_exodus_nonce_commitment_size_and_get_metadata` costs essentially nothing on the `validate_unsigned` path.
- It is deterministic across all nodes because it lives on the consensus-critical validation path. Per-node offchain exclusion heuristics (option 2) must be reimplemented identically by every operator to avoid divergence, and they leave the poisoned bytes on chain permanently.
- Option 2 is still useful as a complement for recovery: `NonceCommitments` already maps each nonce to its `authority_index` on chain, so every node can identify a poisoned session’s culprit from the same data. For sessions poisoned before the fix lands, excluding the identified culprit from the next session beats banning the whole session in local storage.

So: option 1 to prevent, option 2 (ideally with the exclusion decision made deterministic rather than per-node local storage) to recover.

---

<div class="post-metadata">

### Author: ![VoidDragon](https://ghostforum.org/user_avatar/ghostforum.org/voiddragon/32/16_2.png) [@VoidDragon](https://ghostforum.org/u/VoidDragon)
#### Post date: [October 10, 2026, 3:29pm UTC](https://ghostforum.org/t/malicious-authority-can-permanently-kill-an-exodus-signing-session-via-an-undecodable-nonce-commitment/103/4 "2026-10-10T15:29:57Z")

</div>

Hey @Rusdiwireng — it looks like the dev team has [fixed the issue](https://git.ghostchain.io/ghostchain/ghost-node/commit/c884ec7b771abb75731a6899661b8f1e43fe7cae) you reported. Could you confirm whether it’s resolved on your end now, or continue the thread if it still persists?

It looks like deserialization happens on the stack only, that’s why we’ve decided to put this check right in the `validate_unsigned`.

---

<div class="post-metadata">

### Author: ![Rusdiwireng](https://ghostforum.org/letter_avatar_proxy/v4/letter/r/9de0a6/32.png) [@Rusdiwireng](https://ghostforum.org/u/Rusdiwireng)
#### Post date: [October 11, 2026, 8:14am UTC](https://ghostforum.org/t/malicious-authority-can-permanently-kill-an-exodus-signing-session-via-an-undecodable-nonce-commitment/103/5 "2026-10-11T08:14:50Z")

</div>

I read the diff for c884ec7. The new is\_nonce\_valid\_point() check runs in the validate\_unsigned path and rejects undecodable 33-byte points with BadProof, for both the hiding and the binding commitment. That is exactly option 1 from my earlier reply: poisoned bytes can no longer enter the pool or the chain, so generate\_group\_commitment on honest nodes will never see them. Confirmed resolved on my end.
