Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: high
Likelihood: high

Insecure Internal Mint Design Enables Unlimited Token Creation by Child Contracts

Author Revealed upon completion

[H-01] Insecure Internal Mint Design Enables Unlimited Token Creation by Child Contracts

Description

The core token implementation exposes a critical design weakness in the internal minting logic.
The _mint() function inside ERC20Internals.sol allows arbitrary modification of the total supply and user balances without any form of access control or hard internal constraints.

Although the function is marked as internal, it is part of a reusable token core intended for inheritance. Any external contract that inherits this module can expose the minting mechanism publicly, intentionally or accidentally, without restriction.

This makes the base design fundamentally unsafe in real-world deployments, because inheriting contracts may assume the internal logic enforces supply discipline when it does not.

// src/helpers/ERC20Internals.sol
function _mint(address account, uint256 value) internal {
assembly ("memory-safe") {
if iszero(account) {
mstore(0x00, shl(224, 0xec442f05))
mstore(add(0x00, 4), 0x00)
revert(0x00, 0x24)
}
let supplySlot := _totalSupply.slot
let balanceSlot := _balances.slot
let supply := sload(supplySlot)
sstore(supplySlot, add(supply, value))
...
}
}

Risk

Likelihood: High

  • The contract is designed for inheritance.

  • Many developers rely on internal safety guarantees.

  • Nothing prevents child contracts from exposing this function publicly.

Impact: High

  • Unlimited token creation.

  • Total supply inflation.

  • Full loss of economic integrity of the token.

Proof of Concept

  • The following PoC demonstrates how a malicious inheriting contract can expose minting publicly and allow arbitrary users to mint unlimited tokens.

contract MaliciousToken is ERC20 {
constructor() ERC20("Evil", "EVL") {}
function mintAnyone(address to, uint256 amount) public {
_mint(to, amount);
}
}
function test_PoC_UnrestrictedMint() public {
MaliciousToken evil = new MaliciousToken();
address attacker = makeAddr("attacker");
evil.mintAnyone(attacker, 1_000_000e18);
assertEq(evil.balanceOf(attacker), 1_000_000e18);
}
forge test --match-test test_PoC_UnrestrictedMint -vvv
  • The test passes, confirming that any account can mint arbitrary tokens.

Recommended Mitigation

  • Add access control directly inside _mint() so that inheriting contracts cannot expose it unsafely.

function _mint(address account, uint256 value) internal {
+ require(msg.sender == owner, "Not authorized");
let supply := sload(supplySlot)
sstore(supplySlot, add(supply, value))
}

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!