NFTBridge
60,000 USDC
View results
Submission Details
Severity: low
Valid

_callBaseUri has incorrect encoded calls and never works

Summary

When bridging a token from mainnet to Starknet, we check if a collection has a baseURI or individual URIs.

The baseURI check always fails because calls are incorrectly encoded.

Vulnerability Details

In src/token/TokenUtil.sol, line 150, encoded calls to check for baseURI are created with:

bytes[2] memory encodedSignatures = [abi.encodeWithSignature("_baseUri()"), abi.encodeWithSignature("baseUri()")];

Because the standard method is baseURI(), with uppercase URI, the internal function TokenUtils::_callBaseUri always fails to retrieve a base uri.

POC

function callBaseUri(
address collection
) public
view
returns (bool, string memory)
{
bool success;
uint256 returnSize;
uint256 returnValue;
bytes memory ret;
bytes[2] memory encodedSignatures = [abi.encodeWithSignature("_baseUri()"), abi.encodeWithSignature("baseUri()")];
for (uint256 i = 0; i < 2; i++) {
bytes memory encodedParams = encodedSignatures[i];
assembly {
success := staticcall(gas(), collection, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
if success {
returnSize := returndatasize()
returnValue := mload(0x00)
ret := mload(0x40)
mstore(ret, returnSize)
returndatacopy(add(ret, 0x20), 0, returnSize)
// update free memory pointer
mstore(0x40, add(add(ret, 0x20), add(returnSize, 0x20)))
}
}
if (success && returnSize >= 0x20 && returnValue > 0) {
return (true, abi.decode(ret, (string)));
}
}
return (false, "");
}
function test_callBaseUri() public{
address milady = 0x5Af0D9827E0c53E4799BB226655A1de152A425a5;
uint256[] memory ids = new uint256[](1);
ids[0] = 0;
(bool success, string memory _baseUri) = callBaseUri(milady);
if (success) {
console.log(_baseUri);
}
else{
console.log("No URI found in Milady's contract.");
}
}
Ran 1 test for test/Bridge.t.sol:BridgeTest
[PASS] test_callBaseUri() (gas: 7532)
Logs:
No URI found in Milady's contract.

Note: the above Foundry test was run against an ETH mainnet fork:

forge test --fork-url https://mainnet.infura.io/v3/{key} --match-test callBaseUri -vvv

Impact

Tokens are bridged to Starknet with individual token URIs even if they have a common base URI. For example, it will send a request to Starknet containing:

https://www.miladymaker.net/milady/json/0

https://www.miladymaker.net/milady/json/1

https://www.miladymaker.net/milady/json/2

And so forth, instead of the inteded baseURI by itself:

https://www.miladymaker.net/milady/json/

The function is implemented incorrectly, deviating from the behavior expected in the comments:

// How the URI must be handled.
// if a base URI is already present, we ignore individual URI
// else, each token URI must be bridged and then the owner of the collection
// can decide what to do

However, funds are not at risk and metadata is still bridged (though inefficiently), so I mark this as a LOW.

Tools Used

Foundry

Recommendations

Encode the call with "_baseURI()", note the uppercase URI.

Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-baseUri-selector-instead-of-baseURI

Likelyhood: Medium, no token using OZ version 2.X and 3.X will work. Impact: Low, Valid standard token won’t be mint with the URI but owner can use ERC721UriImpl function on the deployed token.

Support

FAQs

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