TSender

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

USDC and USDT transfer for blacklisted addresses block the airdrop

Summary

The TSender protocol includes functionality for airdropping ERC20 tokens to a list of recipients. The tokens expected to be integrated are USDC, USDT, LINK and WETH (as indicated in the protocol readme file).
The airdropERC20() function involves transferring the totalAmount of tokens to the contract itself before redistributing them to the intended recipients. If the airdropped token are USDC or USDT, the implementation does not handle the scenarios where individual transfers to recipients may fail, specifically when dealing with blacklisted addresses. In case of blacklisted addresses, the ERC20 transfer function reverts, consequently reverting the airdropERC20 function and blocking the airdrop.

Vulnerability Details

The vulnerability arises from the lack of proper handling of failed transfer calls within the loop that distributes specific tokens (USDC and USDT) to recipients. Specifically, if the transfer call reverts, the function reverts and the airdrop is blocked.

Impact

The vulnerability arises from the lack of proper handling of failed transfer calls within the loop that distributes tokens to recipients. In the case of transferring USDC token or USDT token to a blacklisted address, the transfer function fails (ref. USDC smart contract https://etherscan.io/address/0x0882477e7895bdC5cea7cB1552ed914aB157Fe56#code#L561) and consequently either the airdropERC20 reverts. De facto the airdrop can't happen.

Considering a scenario where the sender intends to send the USDC (or USDT) calling the airdropERC20 function, a malicious recipient can block one of the addresses in the array from receiving USDC (or USDT) by adding it to the USDC (or USDT) blacklist (e.g. by doing something malicious with that address, etc.). This leads to the block of the airdrop.

In the TSenderHuffTest.t.sol, TSenderReferenceTest.t.sol, TSenderYulTest.t.sol, Gaslitetest.t.sol, TSenderHuffNoCheckTest.t.sol files add:

import {MockFalseTransferERC20} from "test/mocks/MockFalseTransferERC20.sol";
mockFalseTransferERC20 = new MockFalseTransferERC20();

Create a MockFalseTransferERC20.sol file and copy/paste this:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MockFalseTransferERC20 is ERC20 {
uint256 public constant MINT_AMOUNT = 1e18;
constructor() ERC20("Mock Token", "MT") {}
function mint() external {
_mint(msg.sender, MINT_AMOUNT);
}
function mint(uint256 amount) external {
_mint(msg.sender, amount);
}
function transfer(address to, uint256 value) public override returns (bool) {
if (to == address(1000)) { //blacklisted address
revert("Transfer to blacklisted address not allowed");
} else {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
}
}

In the Base_Test.t.sol add these:

import {MockFalseTransferERC20} from "test/mocks/MockFalseTransferERC20.sol";
MockFalseTransferERC20 public mockFalseTransferERC20;
function test_blacklistedAddressesBlockAirdrop() public virtual hasSafetyChecks {
address sender = makeAddr("sender");
uint256 amount = 123;
uint256 expectedTotalAmount = amount * 5;
// Arrange
vm.startPrank(sender);
mockFalseTransferERC20.mint(uint256(expectedTotalAmount));
mockFalseTransferERC20.approve(address(tSender), uint256(expectedTotalAmount));
vm.stopPrank();
address[] memory recipients = new address[](5);
recipients[0] = address(5);
recipients[1] = address(1000);
recipients[2] = address(11);
recipients[3] = address(6);
recipients[4] = address(13);
uint256[] memory amounts = new uint256[](5);
amounts[0] = amount;
amounts[1] = amount;
amounts[2] = amount;
amounts[3] = amount;
amounts[4] = amount;
vm.prank(sender);
vm.expectRevert();
tSender.airdropERC20(address(mockFalseTransferERC20), recipients, amounts, expectedTotalAmount);
assertEq(mockFalseTransferERC20.balanceOf(sender), amount * 5);
vm.stopPrank();
}
}

Run: forge test --match-test test_blacklistedAddressesBlockAirdrop -vv

Logs:
Ran 1 test for test/unit/noCheck/GasliteTest.t.sol:GasliteTest
[PASS] test_blacklistedAddressesBlockAirdrop() (gas: 2312)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 13.94ms (615.96µs CPU time)
Ran 1 test for test/unit/hasChecks/TSenderReferenceTest.t.sol:TSenderReferenceTest
[PASS] test_blacklistedAddressesBlockAirdrop() (gas: 115885)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 14.34ms (2.02ms CPU time)
Ran 1 test for test/unit/hasChecks/TSenderYulTest.t.sol:TSenderYulTest
[PASS] test_blacklistedAddressesBlockAirdrop() (gas: 114316)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 14.41ms (2.15ms CPU time)
Ran 1 test for test/unit/noCheck/TSenderHuffNoCheckTest.t.sol:TSenderHuffNoCheckTest
[PASS] test_blacklistedAddressesBlockAirdrop() (gas: 2312)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.11s (33.17µs CPU time)
Ran 1 test for test/unit/hasChecks/TSenderHuffTest.t.sol:TSenderHuffTest
[PASS] test_blacklistedAddressesBlockAirdrop() (gas: 113722)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.11s (166.17µs CPU time)
Ran 5 test suites in 1.41s (2.26s CPU time): 5 tests passed, 0 failed, 0 skipped (5 total tests)

Tools Used

Manual review

Recommendations

Implement a logic that can handle blacklisted addresses.

Updates

Lead Judging Commences

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.