Access Control Vulnerability in DeFi | QuillAudits

Table of Contents:

1. What does Access Control mean?
2. Importance of Access controls?
3. Real-Life Exploits Case Studies
4. Openzeppelin’s access control Libraries
5. A Small Challenge for you
6. References

What does “Access Control” mean?

Basically, access control means “who is allowed to carry out a specific duty”. Access controls outline the limitations on user roles and privileges in smart contracts.
Smart contracts include a number of critical functions that should be well safeguarded from access by malicious users, such as changing ownership and upgrading contracts. A malicious user calling these critical functions can cause a great deal of harm to the project.

Importance of use of access controls:

1. It helps protect critical functions from unauthorized access.

An access control mechanism can be used to limit the access of some functions to privileged users like admin/ owners. We can limit some critical operations, such as changing owners, creating tokens, halting contracts, etc., to being called only by owner roles. It assists in preventing other users from calling these functions and safeguards against unwanted access.

2. It helps in creating different levels of authorization.

In a contract, we can create different levels of authorization based on different roles. We can define multiple roles, and each is allowed to perform different sets of actions in a contract.
For example, in a crowdfunding contract, there can be different levels of authorization, like admin, manager, contributor, etc., and each of them may have been allotted different sets of actions.

3. It helps in Whitelisting and blacklisting users.

A whitelist is useful if you want to restrict access to a certain function or grant privileges to a certain group of users. Simple whitelists can be quite powerful. When the user calls the smart contract function, it checks if the address is on the whitelist. If it is, the function executes.
The same process is used with blacklisting. Administrators or owners can easily ban certain users if they don’t want them to call specific functions.

4. Granting and Revoking Roles:

Once a specific role is granted to the user, it can also be revoked quite easily. It comes in very handy when there is an upgrade in a role or a removal of the role. Roles can be granted and revoked dynamically via the grantRole and revokeRole functions.

Let’s take an example: If an administrator discovers a rogue user who has the manager role, he can easily revoke the manager role by calling the revokeRole function.

Real-life Exploits Case Studies:

1. Rikkei Finance:

In this case, the SetOracleData() function’s visibility was set to public which means that it can be called externally. So, anyone can call SetOracleData and change Oracle data to manipulate the price. Approx $1 Million were stolen by the hackers.
https://bscscan.com/address/0xd55f01b4b51b7f48912cd8ca3cdd8070a1a9dba5#code#F1#L29

function setOracleData(address rToken, oracleChainlink _oracle) external { //vulnerable point
oracleData[rToken] = _oracle;
}

2. Ragnarok Online Invasion:

In this case, the cause of the attack was a typical access control vulnerability of the ownership transfer function. The transferOwnership function’s visibility is set to "public". The attacker transferred ownership to himself and stole around 158 BNB from the contract.

Check out this blog for an in-depth analysis:
https://medium.com/quillhash/decoding-ragnarok-online-invasion-44k-exploit-quillaudits-261b7e23b55

function transferOwnership(address newOwner) public virtual {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}

3. UERII Token:

UERII token had a mint function whose visibility was set to public. Now, any user can mint unlimited tokens. The attacker minted 100 billion tokens and gained a profit of around $2,500.
https://etherscan.io/address/0x418c24191ae947a78c99fdc0e45a1f96afb254be#code#L493

function mint() public returns (bool) { 
_mint( msg.sender, 100000000000000000 );
return true;
}

Openzeppelin’s access control Libraries:

The best way to implement access control is by using Openzeppelin’s access control libraries:

1. Ownership and Ownable:

We can use Openzeppelin’s Ownable library to implement this. OpenZeppelin’s Ownable defines modifiers like “onlyOwner” that check if the user making a function call is the owner of the contract.

By default, the owner of an Ownable contract is the account that deployed it, which is usually exactly what you want.

Ownable also lets you:
1. transferOwnership from the owner's account to a new one.
2. renounceOwnership for the owner to relinquish this administrative privilege, a typical pattern after an initial stage with centralized administration is over.

Check out here for more info on implementing it.
https://docs.openzeppelin.com/contracts/4.x/access-control#ownership-and-ownable

2. Role-Based Access Control (RBAC):

While the simplicity of ownership can be helpful for simple systems or quick prototyping, different levels of authorization are often needed.

OpenZeppelin Contracts provides Access control for implementing role-based access control. Its usage is straightforward: for each role that you want to define, you will create a new role identifier that is used to grant, revoke, and check if an account has that role.

Check out here for more information on implementing it.

https://docs.openzeppelin.com/contracts/4.x/access-control#role-based-access-control

A small challenge for you:

Objective: Become the owner of the contract and change the value of pwn to true.

Goerli link: https://goerli.etherscan.io/address/0x53bd8eafb67605c22a9e1a327aab6479583844f9#code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.1;

conract accessControlVuln {

error notWhitelisted();
bool pwn;
address owner;
mapping(address => bool) whitelistedMinters;

constructor() {
owner = msg.sender;
}
modifier whitelisted(address addr) {
if(!whitelistedMinters[addr]) revert notWhitelisted();
_;
}
function addToWhitelist(address addr) public {
require(addr != address(0), "Zero address");
whitelistedMinters[addr] = true;
}
function changeOwner(address addr) public whitelisted(addr) {
owner = msg.sender;
}
function pwnOwner() public {
require (msg.sender == owner);
pwn = true;
}
}

Web3 security- Need of the hour

Why QuillAudits for Web3 Security?
QuillAudits is well-equipped with tools and expertise to provide cybersecurity solutions saving the loss of millions in funds.

Want more Such Security Blogs & Reports?

Connect with QuillAudits on :
Linkedin | Twitter | Website | Newsletter | Discord | Telegram

Partner with QuillAudits :

- Affiliate program ( Refer and secure web3 )

- QuillAudits Partnership Programme ( Venture funds, launchpads, development companies, marketing firms, web2 cybersecurity firms, web3 products )

- Join Ambassdor program

--

--