EXAMPLE FINDING
Missing Access Control in a CosmWasm Contract
This is what an Odin Scan finding looks like, end to end: the code, the severity, how an attacker would exploit it, a working proof of concept, and the fix. It comes from our own rule corpus for CosmWasm access control, so you can verify every line yourself.
Function modifies contract state without an access control check
update_config writes the contract's CONFIG item without asserting the caller is the owner or an admin. Any account that can execute the contract can call it.
The vulnerable code
pub fn update_config(
deps: DepsMut,
new_config: Config,
) -> StdResult<Response> {
// No assert_owner / assert_admin check: anyone can call this
CONFIG.save(deps.storage, &new_config)?;
Ok(Response::default())
}Exploit sketch
- Attacker inspects the contract's exported execute messages and finds
update_configaccepts an arbitraryConfig. - They craft an execute message setting a fee wallet, proxy, or spend limit they control.
- One transaction later, every subsequent contract interaction routes value or privileges to the attacker. No keys, no bridge, no flash loan needed - just an unprotected message.
Proof of concept
With any Cosmos SDK wallet and the contract address:
# Any address can update the config - no owner signature required
wasmd tx wasm execute <CONTRACT_ADDR> \
'{"update_config":{"new_config":{"fee_wallet":"attacker_addr","fee_bps":10000}}}' \
--from attacker --chain-id <CHAIN_ID>The fix
Assert ownership before mutating state. With cw-ownable:
pub fn update_config(
deps: DepsMut,
info: MessageInfo,
new_config: Config,
) -> StdResult<Response> {
cw_ownable::assert_owner(deps.storage, &info.sender)?;
CONFIG.save(deps.storage, &new_config)?;
Ok(Response::default())
}How Odin Scan catches it
The deterministic rule missing_access_control flags any function that writes storage items while its body lacks a recognized authorization call (cw_ownable::assert_owner, cw_controllers admin checks, or a custom sender check). On the OdinBench suite this rule's category scored 100% F1 on synthetic missing-access-control contracts after the verification pass. Run it yourself on the live demo - paste the vulnerable snippet above and you will get this finding in about three minutes.
Every finding ships like this: severity, exploit path, PoC, fix.
Try the no-account demo, or run the full multi-model scan on your whole repository with a 7-day Pro trial.