漏洞解析:
代码解析:
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
interface USDT {
function transfer(address to, uint256 value) external;
function balanceOf(address account) external view returns (uint256);
function approve(address spender, uint256 value) external;
}
contract ContractTest is Test {
using SafeERC20 for IERC20;
//引入USDT
IERC20 constant usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
//fork主网,高度为16138254
function setUp() public {
vm.createSelectFork("mainnet", 16138254);
}
//给该合约转账123个USDT
function testTransfer() public {
vm.startPrank(0xef0DCc839c1490cEbC7209BAa11f46cfe83805ab);
//普通的转账
usdt.transfer(address(this), 123); //revert
vm.stopPrank();
}
//给该合约转账123个USDT
function testSafeTransfer() public {
vm.startPrank(0xef0DCc839c1490cEbC7209BAa11f46cfe83805ab);
//使用SafeERC20的safeTransfer转账,会revert回滚
usdt.safeTransfer(address(this), 123);
vm.stopPrank();
}
receive() external payable {}
}
上面这个合约代码的意思就是展示了了USDT两种方式的转账,如果按照标准的ERC20格式来转账,就会失败回滚。
使用safeTransfer,也是一样的回滚,但是它更标准化。
对于使用Foundry和remix可能小伙伴们使用已经忘记了,可以查看我的第一篇文章:
也可查看该文章:
https://mp.weixin.qq.com/s/IuLD--prbWJYgrrsWRZr_A
USDT 合约地址:
0xdAC17F958D2ee523a2206206994597C13D831ec7
function transferFrom(address _from, address _to, uint _value) publiconlyPayloadSize(3 * 32) {
var _allowance = allowed[_from][msg.sender];
// Check is not needed because sub(_allowance, _value) will already throw if this condition is not met
// if (_value > _allowance) throw;
uint fee = (_value.mul(basisPointsRate)).div(10000);
if (fee > maximumFee) {
fee = maximumFee;
}
if (_allowance < MAX_UINT) {
allowed[_from][msg.sender] = _allowance.sub(_value);
}
uint sendAmount = _value.sub(fee);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(sendAmount);
if (fee > 0) {
balances[owner] = balances[owner].add(fee);
Transfer(_from, owner, fee);
}
Transfer(_from, _to, sendAmount);
}
可以看到transferFrom没有返回任何值,这也就意味着其他交易智能合约调用USDT进行交易的时候需要小心。
如果用户交易失败了,但是没有正确处理返回值的话,可能会造成用户体验上的问题。且较难定位,所以这个点严格意义上算是一种写法规范的问题,并不是实际的安全问题。
解决方法就是所有的ERC20标准化代币交易,都引入SafeERC20的safeTransfer即可。
// 添加导入声明
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
// 使用 SafeERC20 和 SafeMath
using SafeERC20 for IERC20;
using SafeMath for uint256;
...
// 使用 SafeERC20 进行转账
IERC20(address(this)).safeTransferFrom(_from, _to, sendAmount);
// 手续费处理
if (fee > 0) {
IERC20(address(this)).safeTransferFrom(_from, owner, fee);
emit Transfer(_from, owner, fee);
}
原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(二十五)未经检查的返回值(不符合ERC20标准)
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论