TSender

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

Token address never validated, EOA will lead to success of the function

Description

The token address is not validated in any function. Even the areListsValid function does not perform any checks or take the assets as parameters. This could be a big issue as an EOA address or an invalid address will not cause a revert, giving the impression that the transfer function has been executed successfully. This issue affects all three files in the scope but is only valid for files with checks.

TSender.sol
function airdropERC20(
address tokenAddress,
address[] calldata recipients,
uint256[] calldata amounts,
uint256 totalAmount
) external {
assembly {
// check for equal lengths
if iszero(eq(recipients.length, amounts.length)) {
mstore(0x00, 0x50a302d6) // cast sig TSender__LengthsDontMatch()
revert(0x1c, 0x04)
}
@>
@>
// transferFrom(address from, address to, uint256 amount)
// cast sig "transferFrom(address,address,uint256)"
// This will result in memory looking like this:
// 0x00: 0x23b872dd00000000000000000000000000000000000000000000000000000000
mstore(0x00, hex"23b872dd")
// from address
mstore(0x04, caller())
// to address (this contract)
mstore(0x24, address())
//...
}
}
TSender.huff
#define macro AIRDROP_ERC20() = takes (0) returns (0) {
// ...
0x00 // [total_amount]
[TOKEN_ADDRESS_OFFSET] calldataload // [token_address, total_amount]
@>
[NUMBER_OF_AMOUNTS_OFFSET_OFFSET] calldataload // [amounts.offset, token_address, total_amount]
0x4 add // [true_amounts.offset, token_address, total_amount]
dup1 // [amounts.offset, amounts.offset, token_address, total_amount]
calldataload // [amounts.length, amounts.offset, token_address, total_amount]
[NUMBER_OF_RECIPIENTS_OFFSET] calldataload // [recipients.length, amounts.length, amounts.offset, token_address, total_amount]
eq // [amounts.length == recipients.length, amounts.offset, token_address, total_amount] // 1 is true
// Function selector is technically still on the bottom of the stack
lengths_match jumpi // [amounts.offset, token_address, total_amount]
// cast sig TSender__LengthsDontMatch()
0x50a302d6 0x00 mstore
0x04 [TWENTY_EIGHT] revert

Risk

Likelyhood: Medium

  • Any incorrect address (address(0), EOA, 256 bytes types) will pass without causing a revert.

Impact: Medium

  • Waste of gas

  • No indication that the function has failed, the transaction will still be executed on the blockchain.

Proof of Concept

This PoC will pass for the three files in scope but not TSenderReference.sol. Solidity implements internal checks to avoid this situation.

Foundry PoC to add in `Base_Test.t.sol`
function testUnexpected_returnsTrueIfAssetsIsIncorrect() public {
address sender = makeAddr("sender");
uint256 numberOfRecipients = 100;
// Arrange
uint256 uint256Amount = ONE;
uint256 expectedTotalAmount = ONE * numberOfRecipients;
address[] memory recipients = new address[](numberOfRecipients);
for (uint256 i = 0; i < numberOfRecipients; i++) {
recipients[i] = address(uint160(i + 25));
}
uint256[] memory amounts = new uint256[](numberOfRecipients);
for (uint256 i = 0; i < numberOfRecipients; i++) {
amounts[i] = uint256Amount;
}
// Act
vm.prank(sender);
uint256 startingGas = gasleft();
tSender.airdropERC20(
address(0),
recipients,
amounts,
expectedTotalAmount
);
uint256 gasUsed = startingGas - gasleft();
console2.log("Gas used", gasUsed);
}

Recommended Mitigation

TSender.sol
function airdropERC20(
address tokenAddress,
address[] calldata recipients,
uint256[] calldata amounts,
uint256 totalAmount
) external {
assembly {
// check for equal lengths
if iszero(eq(recipients.length, amounts.length)) {
mstore(0x00, 0x50a302d6) // cast sig TSender__LengthsDontMatch()
revert(0x1c, 0x04)
}
+ if iszero(extcodesize(tokenAddress)){
+ mstore(0x00, 0xa361ae82) // cast sig TSender__TokenNotValidSmartContract()
+ revert(0x1c, 0x04)
+ }
// transferFrom(address from, address to, uint256 amount)
// cast sig "transferFrom(address,address,uint256)"
// This will result in memory looking like this:
// 0x00: 0x23b872dd00000000000000000000000000000000000000000000000000000000
mstore(0x00, hex"23b872dd")
// from address
mstore(0x04, caller())
// to address (this contract)
mstore(0x24, address())
//...
}
}
TSender.huff
#define macro AIRDROP_ERC20() = takes (0) returns (0) {
// ...
0x00 // [total_amount]
[TOKEN_ADDRESS_OFFSET] calldataload // [token_address, total_amount]
+ dup1 extcodesize iszero
+ smart_contract jumpi // [amounts.offset, token_address, total_amount]
+ // cast sig TSender__TokenNotValidSmartContract()
+ 0xa361ae82 0x00 mstore
+ 0x04 [TWENTY_EIGHT] revert
+
+ smart_contract:
[NUMBER_OF_AMOUNTS_OFFSET_OFFSET] calldataload // [amounts.offset, token_address, total_amount]
0x4 add // [true_amounts.offset, token_address, total_amount]
dup1 // [amounts.offset, amounts.offset, token_address, total_amount]
calldataload // [amounts.length, amounts.offset, token_address, total_amount]
[NUMBER_OF_RECIPIENTS_OFFSET] calldataload // [recipients.length, amounts.length, amounts.offset, token_address, total_amount]
eq // [amounts.length == recipients.length, amounts.offset, token_address, total_amount] // 1 is true
// Function selector is technically still on the bottom of the stack
lengths_match jumpi // [amounts.offset, token_address, total_amount]
// cast sig TSender__LengthsDontMatch()
0x50a302d6 0x00 mstore
0x04 [TWENTY_EIGHT] revert
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue
n0kto Submitter
about 1 year ago
patrickalphac Auditor
about 1 year ago
inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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