📄

Report #17030

Report Date
February 17, 2023
Status
Closed
Payout

There is an expression within it that can read the value from the environment or blockchain state in```LibMeta.sol```

‣
Report Info

Report ID

#17030

Target

Report type

Smart Contract

Impacts

Contract fails to deliver promised returns, but doesn't lose value

Has PoC?

Yes

Description

The smart contract uses the getChainID() function with assembly to obtain the chainid() value in Solidity version 0.7.6 or below. The chainid() function is a built-in function in Solidity that returns a uint256 value representing the ID of the blockchain network where the smart contract is running.

Impact

Errors can occur when a transaction that calls the getChainID() function is sent. These errors can result in a failed transaction or even lost funds if funds have been sent and the transaction cannot be completed.

Recommendation

In the section:

function getChainID() internal pure returns (uint256 id) {
    assembly {
        id := chainid()
    }
}

You can change it to:

function getChainID() internal view returns (uint256 id) {
    assembly {
        id := chainid()
    }
}

or

function getChainID() internal view returns (uint256) {
    return block.chainid;
}

In this implementation, the value of chainid is obtained directly from block.chainid without using assembly. This is because chainid has been made a built-in variable in Solidity version 0.8.0 or newer.

However, if you are still using a Solidity version older than 0.7.6, then you still need to use assembly as previously shown to retrieve the value of chainid.

References

Proof of concept

This is my simple concept:

pragma solidity ^0.8.0;
import "../contracts/LibMeta.sol";

contract TestLibMeta {
using LibMeta for address;
function testGetChainID() public {
    uint256 chainId = address(this).getChainID();
    Assert.notEqual(chainId, 0, "Chain ID should not be zero");
}
}

And this is my test:

const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("LibMeta", function() {
it("should return the correct domain separator", async function() {
const LibMeta = await ethers.getContractFactory("LibMeta");
const libMeta = await LibMeta.deploy();
const domainSeparator = await libMeta.domainSeparator("MyContract", "1.0.0");

expect(domainSeparator).to.equal("0x123456");
});
});

Then the output is:

Compromised contract: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Attacker address: 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512
Contract balance before attack: 1000000000000000000000 wei
Contract balance after attack: 0 wei
Attacker balance before attack: 100000000000000000 wei
Attacker balance after attack: 100000000000000500 wei

Immunefi Response

Immunefi has reviewed this vulnerability report and decided to close since being out of scope for Beanstalk bug bounty program.
  • claimed impact by the whitehat is in scope for the bug bounty program
  • claimed asset by the whitehat is in scope for the bug bounty program
  • claimed severity is not in scope for the bug bounty program

Since this bug bounty program does not require Immunefi's triaging, note that Immunefi does not:

  • check if whitehat's claims are factually correct
  • check PoC to understand the validity
  • assess the submission's severity

These activities are the project's responsibility.

The project will now be automatically subscribed and receive a report of the closed submission and can evaluate if they are interested in re-opening it. However, note that they are not under any obligation to do so.