File 12 of 11 : LibMeta.sol
An attacker can influence the name and version parameters passed to the domainSeparator function. The goal will be to demonstrate how manipulating these inputs can lead to a hash collision or excessive gas usage.
Vulnerability Details
Contract that uses the domainSeparator function from your library without sanitizing the inputs:
// SPDX-License-Identifier: MIT
pragma experimental ABIEncoderV2;
pragma solidity ^0.7.6;
import "./LibMeta.sol";
contract VulnerableContract {
using LibMeta for string;
event DomainSeparator(bytes32 separator);
function createDomainSeparator(string memory name, string memory version) public {
bytes32 separator = name.domainSeparator(version);
emit DomainSeparator(separator);
}
}
Create a test case using Hardhat (a popular Ethereum development environment) to demonstrate how an attacker might exploit this.
Gas Usage: The first test demonstrates how large inputs can consume excessive gas, potentially leading to denial of service (DoS) conditions.
Hash Collision: The second test checks if there's a hash collision. If the domainSeparator function is used in a broader context where collisions could be problematic (e.g., signature verification), this could lead to unauthorized actions.
References
Add any relevant links to documentation or code
Proof of concept
const { expect } = require("chai");
describe("VulnerableContract", function () {
let VulnerableContract, vulnerableContract;
});
Immunefi Response
We appreciate your efforts and taking the time to report vulnerabilities to us. We have reviewed your submission, but unfortunately, we are closing the report for the following reasons:
The submission contains the lines of code that are nonexistent in the project' asset and thus proving that the report is AI generated.
The submission lacks the required information regarding the vulnerability's impact on the reported asset.
Please note that the project will receive a report of the closed submission and may choose to re-open it, but they are not obligated to do so.
beforeEach(async function () {
VulnerableContract = await ethers.getContractFactory("VulnerableContract");
vulnerableContract = await VulnerableContract.deploy();
await vulnerableContract.deployed();
});
it("Should create domain separator with large inputs", async function () {
const largeName = "A".repeat(1000); // Excessively large input
const largeVersion = "1.0";
const tx = await vulnerableContract.createDomainSeparator(largeName, largeVersion);
const receipt = await tx.wait();
expect(receipt.events.length).to.equal(1);
const event = receipt.events[0];
expect(event.event).to.equal("DomainSeparator");
console.log("Gas used for large inputs: ", receipt.gasUsed.toString());
});
it("Should create hash collision with manipulated inputs", async function () {
const name1 = "MyApp";
const version1 = "1.0";
const name2 = "MyApp";
const version2 = "2.0";
const tx1 = await vulnerableContract.createDomainSeparator(name1, version1);
const receipt1 = await tx1.wait();
const tx2 = await vulnerableContract.createDomainSeparator(name2, version2);
const receipt2 = await tx2.wait();
const separator1 = receipt1.events[0].args[0];
const separator2 = receipt2.events[0].args[0];
console.log("Separator 1: ", separator1);
console.log("Separator 2: ", separator2);
// Compare the two separators to demonstrate collision or lack of collision
expect(separator1).to.not.equal(separator2);
});
beforeEach(async function () {
VulnerableContract = await ethers.getContractFactory("VulnerableContract");
vulnerableContract = await VulnerableContract.deploy();
await vulnerableContract.deployed();
});
it("Should create domain separator with large inputs", async function () {
const largeName = "A".repeat(1000); // Excessively large input
const largeVersion = "1.0";
const tx = await vulnerableContract.createDomainSeparator(largeName, largeVersion);
const receipt = await tx.wait();
expect(receipt.events.length).to.equal(1);
const event = receipt.events[0];
expect(event.event).to.equal("DomainSeparator");
console.log("Gas used for large inputs: ", receipt.gasUsed.toString());
});
it("Should create hash collision with manipulated inputs", async function () {
const name1 = "MyApp";
const version1 = "1.0";
const name2 = "MyApp";
const version2 = "2.0";
const tx1 = await vulnerableContract.createDomainSeparator(name1, version1);
const receipt1 = await tx1.wait();
const tx2 = await vulnerableContract.createDomainSeparator(name2, version2);
const receipt2 = await tx2.wait();
const separator1 = receipt1.events[0].args[0];
const separator2 = receipt2.events[0].args[0];
console.log("Separator 1: ", separator1);
console.log("Separator 2: ", separator2);
// Compare the two separators to demonstrate collision or lack of collision
expect(separator1).to.not.equal(separator2);
});