Skip to main content

WTF Solidity S04. Access Control Exploit

Recently, I have been revisiting Solidity, consolidating the finer details, and writing "WTF Solidity" tutorials for newbies.

Twitter: @0xAA_Science | @WTFAcademy_

Community: DiscordWechatWebsite wtf.academy

Codes and tutorials are open source on GitHub: github.com/AmazingAng/WTF-Solidity

English translations by: @to_22X


In this lesson, we will discuss the access control vulnerabilities in smart contracts. These vulnerabilities led to the Poly Network being hacked for $611 million and the ShadowFi DeFi project on BSC being hacked for $300,000.

Access Control Vulnerabilities

Access control in smart contracts defines the roles and accesses of different users in the application. Typically, functions such as token minting, fund withdrawal, and pausing require higher-level accesses to be called. If the access configuration is incorrect, it can lead to unexpected losses. Below, we will discuss two common access control vulnerabilities.

1. Incorrect Access Configuration

If special functions in a contract are not properly managed with accesses, anyone can mint a large number of tokens or drain the funds from the contract. In the case of the Poly Network cross-chain bridge, the function for transferring guardianship was not configured with the appropriate accesses, allowing hackers to change it to their own address and withdraw $611 million from the contract.

In the code below, the mint() function does not have access control, allowing anyone to call it and mint tokens.

// Bad mint function without access control
function badMint(address to, uint amount) public {
_mint(to, amount);
}

2. Authorization Check Error

Another common type of access control vulnerability is failing to check if the caller has sufficient authorization in a function. The token contract of the DeFi project ShadowFi on BSC forgot to check the caller's allowance in the burn() function, allowing attackers to arbitrarily burn tokens from other addresses. After the hacker burned tokens in the liquidity pool, they only needed to sell a small amount of tokens to withdraw all the BNB from the pool, resulting in a profit of $300,000.

// Bad burn function without access control
function badBurn(address account, uint amount) public {
_burn(account, amount);
}

How to Prevent

There are two main methods for preventing access control vulnerabilities:

  1. Use OpenZeppelin's access control library to configure appropriate accesses for special functions in the contract. For example, use the OnlyOwner modifier to restrict access to only the contract owner.
// Good mint function with onlyOwner modifier for access control
function goodMint(address to, uint amount) public onlyOwner {
_mint(to, amount);
}
  1. Ensure that the caller of the function has sufficient authorization within the function's logic.
// Good burn function that checks authorization if burning tokens owned by another account
function goodBurn(address account, uint amount) public {
if(msg.sender != account){
_spendAllowance(account, msg.sender, amount);
}
_burn(account, amount);
}

Summary

In this lesson, we discussed access control vulnerabilities in smart contracts. There are two main forms: incorrect access configuration and authorization check errors. To avoid these vulnerabilities, we should use a access control library to configure appropriate accesses for special functions and ensure that the caller of the function has sufficient authorization within the function's logic.