I consider myself a versatile cybersecurity professional, passionate about finding vulnerabilities, securing smart contracts, and investigating digital threats. In this article, I'll share a real example of a Web3 exploit and show you how to identify and prevent such risks.
π Introduction
Access control defines who is allowed to call which function in a smart contract. When access control is implemented incorrectly β or not at all β any attacker can execute privileged functions, leading to stolen funds, destroyed contracts, or complete loss of ownership.
Access control bugs are among the most common and most dangerous vulnerabilities in Ethereum smart contracts, often resulting in irreversible financial losses.
π What Is an Access Control Vulnerability?
An access control vulnerability occurs when:
- Critical functions are publicly callable
- Ownership is not verified
msg.senderis not properly checked- Admin roles are misconfigured
- π In simple words:
The contract forgets to ask: "Are you allowed to do this?"
β Vulnerable Smart Contract Example
Below is a contract that looks fine, but contains a serious access control flaw.

π© What's the Problem?
withdrawAll()is marked public- No check for
msg.sender == owner - Anyone can drain the entire contract balance
βοΈ Attack Scenario (Step-by-Step)
- Contract is deployed by the owner
- Owner deposits ETH into the contract
- Attacker notices
withdrawAll()has no access restriction - Attacker calls
withdrawAll() - π₯ Entire balance is transferred to the attacker
No hacking tools. No exploits. Just one function call.
Secure Smart Contract (Fixed Version)
Let's fix the vulnerability using proper access control.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SecureVault {
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Not authorized");
_;
}
function withdrawAll() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
receive() external payable {}
}β Why This Is Secure
Uses an onlyOwner modifier
Verifies msg.sender
Prevents unauthorized withdrawals
Follows best practices
π§ͺ Medium-Level Demo Using Remix IDE
You can demonstrate this vulnerability live using Remix.
π§ Step 1: Open Remix
Visit: https://remix.ethereum.org
- Create two files:
VulnerableVault.solSecureVault.so
π§ Step 2: Deploy Vulnerable Contract
- Compile
VulnerableVault.sol - Deploy using Account 1
- Send some ETH to the contract
π§ Step 3: Attack the Contract
- Switch to Account 2
- Call
withdrawAll() - β ETH is transferred to attacker account
- π₯ Attack successful
π§ Step 4: Deploy Secure Contract
- Compile & deploy
SecureVault.sol - Fund the contract
- Switch to Account 2
- Try calling
withdrawAll()
β Transaction reverts: "Not authorized"
β Attack prevented
π§ Real-World Impact
Access control bugs have caused:
- Millions of dollars in losses
- DAO takeovers
- Permanent contract destruction
Even experienced developers make this mistake when:
- Rushing deployments
- Copy-pasting code
- Forgetting modifiers
π οΈ Best Practices to Prevent Access Control Bugs
β Always restrict critical functions
β Use onlyOwner or role-based access
β Prefer OpenZeppelin's Ownable / AccessControl
β Write unit tests for authorization
β Perform security audits
π Conclusion
Access control vulnerabilities are simple but catastrophic.
One missing require() can destroy an entire protocol.
π If a function changes money, ownership, or state β protect it.