# Reentrancy in LiquidStakingManager.sol#withdrawETHForKnow leads to loss of fund from smart wallet

withdrawETHForKnot sends 4 ETH before marking the BLS key banned, letting a contract recipient reenter and drain 8 ETH from the smart wallet.

- Payout: Contest reward
- Severity: high
- Program: Stakehouse Protocol
- Credited: ladboy233
- Disclosed: 2023-01-20
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-11-stakehouse-findings/issues/110
- Library page: https://bugbountylibrary.com/r/c4%3A2022-11-stakehouse%3AH-05
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:reentrancy

**Target:** LiquidStakingManager.withdrawETHForKnot smart contract function
**Root cause:** State update banning the BLS public key occurs after the external ETH transfer via rawExecute, violating checks-effects-interactions and lacking a reentrancy guard.
**Steps:**
1. Deploy attacker contract that calls `registerBLSPublicKeys{value: 8 ether}(publicKeys, signatures, address(this))` to become node runner representative for two keys
2. Set lifecycle status of key to INITIALS_REGISTERED
3. Call `withdrawETHForKnot(address(this), "publicKeys1")`
4. In `receive()`, reenter `withdrawETHForKnot(address(this), "publicKeys1")` before bannedBLSPublicKeys is set
5. Observe 8 ETH withdrawn instead of 4
**How found:** Code review of withdrawETHForKnot noting the external rawExecute call precedes the bannedBLSPublicKeys mapping update, violating checks-effects-interactions; recipient being attacker-controlled contract enables reentry.
**Exploitation:** Attacker contract registers as EOA representative, triggers withdraw; the 4 ETH rawExecute to itself fires receive() which reenters withdrawETHForKnot for the same key before the ban mapping is written, extracting 8 ETH total from the smart wallet.
**Insight:** When a withdrawal function bans/marks a key as spent after an external transfer, check whether the recipient can be a contract and reenter before the ban write; the msg.sender==recipient constraint is satisfiable by making the attacker contract both.
