# auth only works well with external functions

AccessControl auth modifier checks msg.sig, which stays the outer function's selector on internal public calls, so nested public auth functions verify the wrong permission.

- Payout: Audit contest
- Severity: medium
- Program: Yield
- Disclosed: 2021-07-23
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-05-yield-findings/issues/4
- Library page: https://bugbountylibrary.com/r/c4%3A2021-05-yield%3AM-09
- Tags: area:web3 domain:web3 impact:privesc kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:access-control vuln:auth-bypass

**Target:** Solidity AccessControl.sol auth modifier / public governance functions
**Root cause:** The auth modifier authorizes msg.sig, but msg.sig is the selector of the outermost external call and is not updated for internal calls to public functions, so a nested public auth function is checked against the caller function's selector instead of its own.
**Steps:**
1. Call external function A (authorized for A's selector) which internally calls public function B carrying the auth modifier.
2. Inside B, msg.sig still equals A's selector, so the modifier checks authorization for A rather than B.
3. An account authorized only for A thereby passes the auth check on B, executing B's privileged logic.
4. External functions are unaffected because each external call produces a fresh msg.sig.
**How found:** Code review of the auth modifier: noticed it keys on msg.sig and reasoned about how msg.sig behaves across internal versus external calls, then enumerated public auth functions and call paths (including _moduleCall) that could reach them.
**Exploitation:** Requires a reachable path where an authorized external function internally invokes a public auth function, e.g. via Ladle.sol _moduleCall allowing module-defined functions to call public auth functions; the caller then gains the nested function's privileged action without being authorized for it.
**Insight:** In Solidity, msg.sig/msg.data are not refreshed on internal calls to public functions; any access-control modifier keyed on msg.sig is only sound for external functions, so grep for public+auth combinations and internal call paths into them.

