# Transactions can be replayed when a connectedChain is removed and then reconnected

Removing then reconnecting a SKALE connected chain resets its message counter to zero, letting old signed interchain messages be replayed.

- Payout: Audit contest
- Severity: medium
- Program: SKALE
- Found by: WatchPug
- Disclosed: 2022-11-11
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-02-skale-findings/issues/57
- Library page: https://bugbountylibrary.com/r/c4%3A2022-02-skale%3AM-01
- Tags: domain:web3 impact:integrity kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:business-logic vuln:signature-replay

**Target:** MessageProxy / MessageProxyForSchain interchain message contracts
**Root cause:** removeConnectedChain deletes the ConnectedChainInfo struct, and _addConnectedChain reinitializes incomingMessageCounter/outgoingMessageCounter to 0; replay protection relies on that counter plus signature, so resetting it re-validates old signed messages.
**Steps:**
1. Chain connector calls `removeConnectedChain(schainName)` — struct deleted, counters gone.
2. Chain connector calls `_addConnectedChain(schainHash)` — counters reset to 0, inited=true.
3. Attacker resubmits a previously processed signed message batch with `startingCounter == 0`.
4. `_verifyMessages(_hashedArray(messages, startingCounter, fromChainName), signature)` passes since signature is still valid.
5. `startingCounter == connectedChains[fromChainHash].incomingMessageCounter` (0==0) passes, message re-executed.
**How found:** Code review of removeConnectedChain/_addConnectedChain noticed counters are deleted and reset to 0, then traced the replay-protection check in MessageProxyForSchain that compares startingCounter to incomingMessageCounter.
**Exploitation:** Requires the chain to be removed and reconnected while validators/signature set unchanged; old signed message batches are then replayed against the reset counter to re-execute past transactions.
**Insight:** When replay protection keys on a monotonic counter, check every lifecycle path (remove/re-add, upgrade, migration) that could reset it; deletion of state structs silently re-arms old signatures.
