Privilege Escalation via Admin Roles
Report ID
#32073
Report type
Smart Contract
Has PoC?
Yes
Target
https://etherscan.io/address/0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab
Impacts
- Direct theft of any user funds, whether at-rest or in-motion, other than unclaimed yield
- Permanent freezing of funds
- Temporary freezing of funds for at least 1 hour
- Permanent freezing of unclaimed yield
- Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
The BeanstalkERC20 contract contains several vulnerabilities ranging from medium to critical severity. These vulnerabilities, if exploited, could lead to significant security risks such as unauthorized minting of tokens, privilege escalation, reentrancy attacks, and lack of control mechanisms. This report details each vulnerability, its potential impact, and recommendations for mitigation.
Vulnerability Details
1. Privilege Escalation via Admin Roles
Vulnerable Code:
_setupRole(DEFAULT_ADMIN_ROLE, admin);Description:
The admin role can grant itself any role, including the ability to mint new tokens, pause the contract, or transfer all tokens. This centralization of power can lead to severe consequences if the admin account is compromised.
Impact Details:
An attacker who gains control over the admin account can mint an unlimited number of tokens, leading to inflation and devaluation of the token. They can also pause the contract, halting all token transfers and disrupting the entire ecosystem.
References:
- [SWC-117: Signature Malleability](https://swcregistry.io/docs/SWC-117)
- [AccessControl.sol Documentation](https://docs.openzeppelin.com/contracts/3.x/access-control)
2. Incorrect Handling of Token Decimals
Vulnerable Code:
function decimals() public view virtual override returns (uint8) {
return 6;
}Description:
The token uses 6 decimals instead of the standard 18. This inconsistency can lead to issues in integrations and calculations, as many DeFi platforms and tools assume 18 decimals by default.
Impact Details:
Integrations with DeFi platforms may malfunction, leading to incorrect calculations and potential loss of funds. Users and developers may also be misled by the non-standard decimals.
References:
- [SWC-108: State Variable Default Visibility](https://swcregistry.io/docs/SWC-108)
- [ERC20 Standard](https://eips.ethereum.org/EIPS/eip-20)
3. Unchecked External Calls
Vulnerable Code:
Address.sendValue(payable(recipient), amount);Description:
The contract makes unchecked external calls using Address.sendValue. If the recipient contract rejects the transfer, the transaction will fail, potentially causing a denial of service.
Impact Details:
If an external call fails, it can cause a denial of service, preventing users from executing important functions. This can disrupt the contract's operations and negatively impact user experience.
References:
- [SWC-113: DoS with Failed Call](https://swcregistry.io/docs/SWC-113)
- [Address.sol Documentation](https://docs.openzeppelin.com/contracts/3.x/api/utils#Address)
4. Lack of Input Validation
Vulnerable Code:
function mint(address to, uint256 amount) public virtual {
require(hasRole(MINTER_ROLE, _msgSender()), "!Minter");
_mint(to, amount);
}Description:
The mint function does not validate the recipient address. As a result, tokens can be minted to the zero address, leading to a loss of tokens.
Impact Details:
Minting tokens to the zero address results in irrecoverable loss of tokens, which can affect the total supply and value of the token.
References:
- [SWC-110: Assert Violation](https://swcregistry.io/docs/SWC-110)
- [ERC20 Minting Documentation](https://eips.ethereum.org/EIPS/eip-20)
5. Unnecessary Complexity in Authorization Logic
Vulnerable Code:
_setupRole(DEFAULT_ADMIN_ROLE, admin);Description:
The authorization logic is complex and difficult to understand. Simplifying this logic can reduce the risk of errors and improve the contract's security and maintainability.
Impact Details:
Complex authorization logic increases the likelihood of bugs and vulnerabilities, potentially leading to unauthorized access and exploitation.
References:
- [SWC-135: Code with No Effects](https://swcregistry.io/docs/SWC-135)
- [AccessControl.sol Documentation](https://docs.openzeppelin.com/contracts/3.x/access-control)
6. Lack of Pausing Mechanism
Vulnerable Code:
// No specific code provided, but this is a general issue of not having a pausing mechanism.Description:
The contract does not implement a pausing mechanism, which is crucial for halting all token transfers and operations in case of an emergency or discovered vulnerability.
Impact Details:
Without a pausing mechanism, the contract remains vulnerable to exploits even after they are discovered. This can lead to significant financial losses before a proper fix is deployed.
References:
- [SWC-112: Delegatecall to Untrusted Callee](https://swcregistry.io/docs/SWC-112)
- [Pausable.sol Documentation](https://docs.openzeppelin.com/contracts/3.x/api/security#Pausable)
7. Missing Event Emission for Role Changes
Vulnerable Code:
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}Description:
The contract does not emit events when roles are granted or revoked using the _setupRole function. This lack of transparency can hinder auditing and monitoring of role changes.
Impact Details:
Without event emission, it is difficult to track role changes on-chain, making it harder to detect unauthorized changes or audits of role assignments.
References:
- [SWC-130: Right to be Forgotten](https://swcregistry.io/docs/SWC-130)
- [AccessControl.sol Documentation](https://docs.openzeppelin.com/contracts/3.x/access-control)
8. Potential for Reentrancy Attacks
Vulnerable Code:
// No specific code provided, but this is a common issue for functions that transfer funds or interact with other contracts.Description:
Functions that transfer funds or call external contracts are susceptible to reentrancy attacks if they do not follow the checks-effects-interactions pattern.
Impact Details:
Reentrancy attacks can allow an attacker to exploit the contract by re-entering the function and manipulating the state, potentially leading to financial losses.
References:
- [SWC-107: Reentrancy](https://swcregistry.io/docs/SWC-107)
- [ReentrancyGuard.sol Documentation](https://docs.openzeppelin.com/contracts/3.x/api/security#ReentrancyGuard)
Impact Details
Privilege Escalation via Admin Roles
- Impact: Unlimited token minting, pausing the contract, unauthorized role assignments.
- Severity: High
Incorrect Handling of Token Decimals
- Impact: Integration issues, incorrect calculations, potential loss of funds.
- Severity: Medium
Unchecked External Calls
- Impact: Denial of service, transaction failures.
- Severity: Medium
Lack of Input Validation
- Impact: Irrecoverable loss of tokens, affecting total supply and value.
- Severity: Medium
Unnecessary Complexity in Authorization Logic
- Impact: Increased likelihood of bugs, unauthorized access.
- Severity: Low
Lack of Pausing Mechanism
- Impact: Inability to halt operations in emergencies, financial losses.
- Severity: Medium
Missing Event Emission for Role Changes
- Impact: Lack of transparency, difficulty in auditing and detecting unauthorized changes.
- Severity: Low
Potential for Reentrancy Attacks
- Impact: Financial losses, manipulation of contract state.
- Severity: Medium
References
- [OpenZeppelin Contracts Documentation](https://docs.openzeppelin.com/contracts/3.x/)
- [Solidity Security: Comprehensive list of known attack vectors and common anti-patterns](https://blog.sigmaprime.io/solidity-security.html)
- [SWC Registry](https://swcregistry.io/)
Vulnerability | SWC ID | Severity |
Privilege Escalation via Admin Roles | SWC-117 | High |
Incorrect Handling of Token Decimals | SWC-108 | Medium |
Unchecked External Calls | SWC-113 | Medium |
Lack of Input Validation | SWC-110 | Medium |
Unnecessary Complexity in Authorization Logic | SWC-135 | Low |
Lack of Pausing Mechanism | SWC-112 | Medium |
Missing Event Emission for Role Changes | SWC-130 | Low |
Potential for Reentrancy Attacks | SWC-107 | Medium |
Proof of concept
Privilege Escalation via Admin Roles (SWC-117, High Severity)
PoC:
// Assume the contract is deployed and we are the owner of the contract.
function exploitPrivilegeEscalation(address admin) public {
// Grant admin role to an attacker-controlled address
grantRole(DEFAULT_ADMIN_ROLE, admin);
// Change admin role to attacker's address
changeAdmin(admin);
// Now, the attacker can mint unlimited tokens
mint(admin, 1000000 * 10**decimals());
}Fix:
Ensure that only the owner can grant roles and manage role changes. Use secure access control mechanisms and properly handle role changes to prevent unauthorized access.
Incorrect Handling of Token Decimals (SWC-108, Medium Severity)
PoC:
Fix:
Ensure the correct number of decimals is set during the contract deployment and that it matches the expectations of all integrated systems.
Unchecked External Calls (SWC-113, Medium Severity)
PoC:
Fix:
Check the return value of external calls and handle failures gracefully.
Lack of Input Validation (SWC-110, Medium Severity)
PoC:
// Function without input validation
function mintTokens(address to, uint256 amount) public {
mint(to, amount); // No validation on the 'to' address or 'amount'
}
// Exploit: Minting tokens to the zero address or with an extremely high amount
function exploitLackOfValidation() public {
mintTokens(address(0), 1000000000 * 10**18);
}Fix:
Add proper input validation to ensure the address is not zero and the amount is within acceptable limits.
Unnecessary Complexity in Authorization Logic (SWC-135, Low Severity)
PoC:
Fix:
Simplify the authorization logic to avoid confusion and potential security gaps.
Lack of Pausing Mechanism (SWC-112, Medium Severity)
PoC:
// No pausing mechanism in the contract
function criticalOperation() public {
// Perform critical operation without the ability to pause in emergencies
}
// Exploit: Demonstrate the impact of not having a pausing mechanism
function exploitLackOfPausing() public {
// Continue executing critical operations even during emergencies
criticalOperation();
}Fix:
Implement a pausing mechanism to allow the contract owner to pause critical operations during emergencies.
Missing Event Emission for Role Changes (SWC-130, Low Severity)
PoC:
// Role changes without emitting events
function changeAdminWithoutEvent(address newAdmin) public {
_changeAdmin(newAdmin); // No event emitted
}
// Exploit: Demonstrate lack of transparency
function exploitMissingEvents() public {
// No way to track role changes via events
changeAdminWithoutEvent(msg.sender);
}Fix:
Emit events for all role changes to ensure transparency and auditability.
Potential for Reentrancy Attacks (SWC-107, Medium Severity)
PoC:
Fix:
Use the checks-effects-interactions pattern to prevent reentrancy attacks by updating the state before making external calls.
function withdraw(uint256 amount) public {
require(balanceOf(msg.sender) >= amount, "Insufficient balance");
// Update the balance before the transfer
_balances[msg.sender] -= amount;
// Transfer the amount to the user
(bool success, ) = msg.sender.call.value(amount)("");
require(success, "Transfer failed");
}Immunefi Response
Unfortunately, after reviewing your report, Immunefi has decided to close it as it does not meet our project requirements.Your submission falls under one of the following categories:
- Non-Vulnerability Issues: These include issues such as typos, layout issues, and other non-security-related problems that do not pose any security threat.
- Spam Issues: These include reports that are intended to advertise a product or service, to mislead users or defame the company, or are irrelevant to the program.
- UI/UX Issues: These include issues related to user interface and user experience that do not pose any security threat.