# lending-market/Note.sol Wrong implementation of access control

Note.sol's _mint_to_Accountant sets accountant to msg.sender when unset, bypassing admin check, letting anyone mint max supply.

- Payout: Audit contest
- Severity: high
- Program: Canto
- Found by: WatchPug
- Disclosed: 2022-10-18
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-06-newblockchain-findings/issues/173
- Library page: https://bugbountylibrary.com/r/c4%3A2022-06-canto%3AH-11
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:auth-bypass vuln:business-logic

**Target:** Solidity lending-market Note token contract
**Root cause:** _setAccountantAddress only enforces msg.sender==admin when accountant!=0, but _mint_to_Accountant calls it precisely when accountant==0, so the first caller self-assigns as accountant and admin.
**Steps:**
1. Call `_mint_to_Accountant(address)` on a fresh Note contract where `accountant == address(0)`.
2. Inside, `_setAccountantAddress(msg.sender)` runs with accountant==0, skipping the admin require, setting `accountant = msg.sender` and `admin = msg.sender`.
3. Back in `_mint_to_Accountant`, `require(msg.sender == accountant)` now passes.
4. `_mint(msg.sender, type(uint).max)` mints max supply to attacker.
**How found:** Code reading of the access-control logic: noticed the admin check is gated on accountant!=0 while the mint path only triggers when accountant==0, making the guard unreachable on first call.
**Exploitation:** Front-run the legitimate initialization call to _mint_to_Accountant; the first caller becomes accountant/admin and mints type(uint).max to themselves.
**Insight:** When an access-control check is conditioned on a state variable being non-zero, verify whether the guarded function is itself the initializer that sets that variable from zero — the guard becomes dead code on first invocation.

