Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refs #474 -- Added AccessControlExploit to general/build/smart-contracts/contract-security #505

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: Access Control Vulnerabilities
displayed_sidebar: generalSidebar
---

Access control vulnerabilities in smart contracts were one of the primary factors leading to the Poly Network cross-chain bridge hack with losses of $611 million and contributed to a $300,000 hack of the ShadowFi DeFi project on Binance Smart Chain (BSC).


More insights on the detailed incidents of these attacks can be gleaned from the following two articles:

- [Abusing Smart Contracts to Steal $600 million: How the Poly Network Hack Actually Happened](https://blog.kraken.com/product/security/abusing-smart-contracts-to-steal-600-million-how-the-poly-network-hack-actually-happened)
- [ShadowFi $301K Burn function Exploit Analysis](https://medium.com/quillhash/shadowfi-301k-burn-function-exploit-analysis-quillaudits-45a17ce04193)

Access control in smart contracts defines the permissions of different roles within the application. Typically, actions such as minting tokens, withdrawing funds, or pausing transactions require users with higher privileges. Incorrect configuration of these permissions can lead to unforeseen losses.

Below, we discuss two common types of access control vulnerabilities.

**1. Misconfigured Permissions**

Lack of proper access control on critical functions allows anyone to mint an excessive number of tokens or withdraw all funds from the contract. For instance, the Poly Network bridge contract failed to restrict the function for transferring guardianship, allowing a hacker to redirect $611 million to their own address.

In the following code, the `flawedCreateToken()` function lacks access restrictions, permitting unrestricted token minting by any user.

```solidity
// Flawed createToken function without access control
function flawedCreateToken(address recipient, uint256 quantity) public {
_mint(recipient, quantity);
}
```

**2. Flawed Authorization Checks**

Another common vulnerability arises when functions do not verify whether the caller has sufficient permissions. For example, ShadowFi's token contract on BSC omitted a crucial check in its `burn` function, allowing attackers to arbitrarily burn tokens owned by other addresses. After burning tokens in the liquidity pool, the hacker could extract all `BNB` from the pool by selling a minimal amount of tokens, netting $300,000.


In the provided code snippet, the `flawedDestroyToken()` function does not implement necessary authorization checks, thereby allowing any user to burn tokens without restrictions.

```solidity
// Flawed destroyToken function without proper authorization checks
function flawedDestroyToken(address holder, uint256 quantity) public {
_burn(holder, quantity);
}
```

## Prevention Strategies

There are two main strategies to prevent access control vulnerabilities:

1. Utilize access control libraries like [OpenZeppelin](https://docs.openzeppelin.com/contracts/5.x/access-control) to assign appropriate permissions to special functions, for instance using the `OnlyOwner` modifier to restrict function calls to the contract owner.

```solidity
// Correct createToken function using the onlyOwner modifier for access control
function correctCreateToken(address recipient, uint256 quantity) public onlyOwner {
_mint(recipient, quantity);
}
```

2. Ensure that function logic verifies the caller has the necessary permissions.

```solidity
// Correct destroyToken function with authorization check
function correctDestroyToken(address holder, uint256 quantity) public {
if(msg.sender != holder){
_spendAllowance(holder, msg.sender, quantity);
}
_burn(holder, quantity);
}
```