TSender

Cyfrin
DeFiFoundry
15,000 USDC
View results
Submission Details
Severity: high
Invalid

Reentrancy Vulnerability in Tsender.huff

Summary

The Tsender.huff contract includes two primary functions: airdropERC20 and areListsValid. The airdropERC20 function is designed to distribute a specified total amount of ERC20 tokens to multiple recipients. However, this function does not implement any safeguards against reentrancy attacks.

The vulnerability lies in the absence of a reentrancy guard in the airdropERC20 function. Here’s a simplified version of the critical section:

#define macro AIRDROP_ERC20() = takes (0) returns (0) {
// Existing function logic
// ...
// Transfer tokens to each recipient
loop_start:
// Load recipient address and amount
calldataload
// Transfer tokens
call
// Check for success and update state
jumpi
// Loop to next recipient
jump
// ...
}

Proof of concept

1: The attacker deploys a malicious contract that calls the airdropERC20 function and includes a fallback function or another function that reenters airdropERC20.

2: The attacker initiates the airdrop with their malicious contract as one of the recipients.

3: Reentrancy Execution:

  • The airdrop contract transfers tokens to the attacker’s contract.

  • The attacker’s contract’s fallback function calls airdropERC20 again before the initial execution is complete.

  • This process repeats, allowing the attacker to transfer more tokens than intended.

Impact

1: The attacker can repeatedly reenter the airdropERC20 function to transfer more tokens than intended.

2: Legitimate recipients may not receive their tokens as the attacker could exhaust the available tokens.

Tools Used

Manual Review

Recommendations

To prevent reentrancy attacks, the contract should implement a reentrancy guard. This involves using a storage variable to track the state of execution and ensuring no reentrant calls can occur during critical operations.

Recommended Code Change

// Define a storage slot for the reentrancy guard
#define constant REENTRANCY_GUARD_SLOT = 0x01
#define macro NON_REENTRANT() = takes (0) returns (0) {
// Check the reentrancy guard
sload(REENTRANCY_GUARD_SLOT)
dup1 // Duplicate the value to check and keep on stack
iszero reentrancy_not_set jumpi
// Revert if the guard is already set
0x00 0x00 revert
reentrancy_not_set:
// Set the reentrancy guard
0x01 sstore(REENTRANCY_GUARD_SLOT)
}
#define macro RESET_REENTRANCY_GUARD() = takes (0) returns (0) {
// Reset the reentrancy guard
0x00 sstore(REENTRANCY_GUARD_SLOT)
}
#define macro AIRDROP_ERC20() = takes (0) returns (0) {
NON_REENTRANT()
// Existing function logic
// ...
RESET_REENTRANCY_GUARD()
}
#define macro MAIN() = takes (0) returns (0) {
// check for msg.value and revert if so
callvalue endcall jumpi
0x00 calldataload 0xE0 shr
// We don't dup1 because we want to be as gas efficient
// with the airdrop as possible
__FUNC_SIG(airdropERC20) eq airdropERC20 jumpi
// This means, for areListsValid, we need to get the selector again
0x00 calldataload 0xE0 shr
__FUNC_SIG(areListsValid) eq areListsValid jumpi
endcall:
0x00 0x00 revert
airdropERC20:
AIRDROP_ERC20()
areListsValid:
ARE_LISTS_VALID()
}

Summary of Changes

  • Added a constant REENTRANCY_GUARD_SLOT to define a specific storage slot for tracking the reentrancy state.

  • Implemented the NON_REENTRANT macro to check and set the reentrancy guard at the beginning of the airdropERC20 function.

  • Implemented the RESET_REENTRANCY_GUARD macro to reset the reentrancy guard after the critical operations are complete.

  • Updated the AIRDROP_ERC20 macro to use the NON_REENTRANT and RESET_REENTRANCY_GUARD macros, ensuring reentrancy protection during the execution of the function.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Too generic

Support

FAQs

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