Skip to content

Repository files navigation

Vyper Access Control, Demonstration Security Review

tests

A self-contained demonstration of a smart-contract security review, written in Vyper: a deliberately vulnerable vault whose set_owner ships without the owner guard its sibling admin functions carry, the ownership takeover and drain it allows (proven with a passing Foundry proof-of-concept), and a fixed branch where the same scenario is neutralised.

This is a demonstration on intentionally vulnerable code. Vault.vy was written to showcase audit methodology end to end. It is not production code, not a real client engagement, and must never be deployed. The finding is a real vulnerability in this demo contract, not an invented severity.

Why this repo exists

Anyone can write "I audit smart contracts" in a bio. This repo shows the work instead: a target, a concrete finding, an executable proof, and a verified fix. If it isn't reproducible, it isn't done.

Repository layout

The review lives across two branches:

Branch Contents What a green forge test means
master The vulnerable vault and the PoC that exploits it any account seizes ownership and drains the balance
fixed The remediated vault and the same scenario the takeover reverts and the balance stays put
  • src/Vault.vy, the contract under review (Vyper 0.4.3)
  • test/Vault.poc.t.sol, the proof-of-concept (Foundry, Solidity harness over the Vyper contract)
  • Vyper_Access_Control_Review.pdf, the full written report

Finding

ID Severity Summary
H-01 High Unguarded ownership transfer. set_owner is missing the assert msg.sender == self.owner check that pause and withdraw_all both enforce. An attacker calls set_owner(attacker), becomes owner, then calls the legitimately guarded withdraw_all to take the funds. The asymmetry between the sibling functions is the bug.

PoC numbers on master: a victim deposits 10 ether. The attacker calls set_owner to seize ownership, then withdraw_all, and walks away with the full 10 ether. On fixed the takeover reverts, ownership holds, and the vault keeps its 10 ether.

Reproduce it

Requires Foundry and the Vyper 0.4.x compiler on PATH (pip install vyper), which Foundry invokes to compile the .vy contract.

git clone https://github.com/Musyg/vyper-access-control-audit.git
cd vyper-access-control-audit
forge install

# master: the takeover and drain succeed
forge test -vv

# fixed: the same attack reverts
git checkout fixed
forge test -vv

The fix

Add the owner guard to set_owner so it matches its siblings:

@external
def set_owner(new_owner: address):
    assert msg.sender == self.owner, "not owner"
    self.owner = new_owner

The rule is mechanical: every state-changing admin entry point must enforce the same access check. When four of five sibling functions carry a guard, the unguarded outlier is the finding.

How severity is rated

High: an unprivileged actor takes full control of the contract and drains 100% of the balance. The only precondition is the vault holding funds, which is its normal state.

About

Demonstration security review: unguarded ownership transfer in a Vyper vault (access control), with a runnable Foundry PoC and a remediated branch.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages