Weather Witness

First Flight #40
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Unauthorized minting

Root + Impact

Description

Anyone can call fulfillMintRequest once the off-chain adapter has populated s_funcReqIdToMintFunctionReqResponse[requestId] with a non-empty response

fulfillMintRequest has no access control and uses msg.sender instead of the original user stored in s_funcReqIdToUserMintReq

function fulfillMintRequest(bytes32 requestId) external {
bytes memory response = s_funcReqIdToMintFunctionReqResponse[requestId].response;
bytes memory err = s_funcReqIdToMintFunctionReqResponse[requestId].err;
require(response.length > 0 || err.length > 0, WeatherNft__Unauthorized());
uint256 tokenId = s_tokenCounter;
s_tokenCounter++;
_mint(msg.sender, tokenId);
}

Risk

Likelihood:

  • A mint fulfillment callback from Chainlink Functions arrives on‐chain with a valid requestIdany caller can invoke fulfillMintRequest at that moment

  • Once one fulfillment has succeeded the same requestId data remains in storage so repeated calls continue to succeed indefinitely

Impact:

An attacker can mint the NFT intended for someone else and because no flag is cleared, the same requestId can be replayed to mint unlimited tokens.

Proof of Concept

// 1. User A submits requestId "0xabc…" and funds the mint.
// 2. Off-chain adapter writes s_funcReqIdToMintFunctionReqResponse["0xabc…"].response
// 3. Attacker calls fulfillMintRequest("0xabc…"):
weatherNft.fulfillMintRequest{gas: 200_000}("0xabc…");
// 4. TokenId 0 minted to attacker.
// 5. Attacker repeats call in a loop to mint TokenId 1, 2, 3, … without ever being the original requester

Recommended Mitigation

- _mint(msg.sender, tokenId);
+ address user = s_funcReqIdToUserMintReq[requestId].user;
+ require(user != address(0) && msg.sender == user, "Not original requester");
+ _mint(user, tokenId);
Updates

Appeal created

bube Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Lack of ownership check in `fulfillMintRequest` function

There is no check to ensure that the caller of the `fulfillMintRequest` function is actually the owner of the `requestId`. This allows a malicious user to receive a NFT that is payed from someone else.

Support

FAQs

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