sherlock-DODO Cross-Chain DEX
sherlock-DODO Cross-Chain DEX
参数没有校验,用户可以伪造
ZEVM
合约中的 onCall()
函数接收来自Gateway
的 token
和 amount
,但没有验证解码后的 MixSwapParams
是否与这些值一致。
具体来说:
params.fromToken
完全由用户传入消息决定params.fromTokenAmount
也是用户可控的
因此攻击者可以:
通过桥接发送一小笔任意代币(如 1 DAI
)
在消息中伪造一个大额的 swap
请求,比如用合约中持有的大额WZETA
来兑换 USDC
实际 swap 时使用的是合约自己持有的 WZETA
攻击者获得兑换后的 USDC
,而合约的 WZETA
被盗
这会导致攻击者可以完全清空合约中持有的高价值代币。
参数没有校验
如果发生转移的是原生代币,实际上是没有校验数量是不是准确的
function withdrawToNativeChain( |
攻击者可以假装提取原生币(ETH),从而绕过代币的 transferFrom
校验,但实际指向真实的 ZRC20
合约(如 USDC.ZRC20
)来偷偷提走这些代币!
地址不要设置为零
比如代币转回的时候,接受地址不能设置为零,在看到地址为零的地方,需要查看它接受的代币是什么,如果被转到零地址,那么就会出现代币进入黑底洞
攻击者获得退款信息后,可以提前构造一个相同的退款交易覆盖用户的退款
攻击者发现,某合法用户(称为“受害者”)的跨链交易失败了,退款信息 refundInfo
[VICTIM_EXTERNAL_ID] 已经存在,里面有受害者的钱,比如 1000 个 zUSDC。
攻击者自己发起一笔跨链交易,故意设计让它失败(比如调用一个总是会 revert
的合约),以便触发 onRevert
。
这笔攻击交易构造了一个恶意的 RevertOptions
,里面的 revertMessage
包含了受害者的 externalId
和攻击者自己的钱包地址。
当这笔攻击交易触发 onRevert
时:
合约拿到
revertMessage
,用它去写 refundInfo[externalId]。由于没有检查,直接用攻击者的地址覆盖了受害者对应的退款信息。
这样,原本属于受害者的钱被“绑架”到了攻击者的钱包地址。
受害者尝试退款时,发现退款的钱已经不在自己的地址了,资金被攻击者劫持。
验证逻辑错误
function claimRefund(bytes32 externalId) external { |
只要if条件不满足,就会直接进入require语句,那么就会直接变为ture,任何人都可以调用
USDT不允许从非零额度授权到非零额度
USDT
的 approve
函数设计有别于标准 ERC20
:
如果当前授权额度(
allowance
)大于 0,且调用方试图设置一个非零的新授权额度,approve
会直接失败(revert
)。这是
USDT
特殊的安全设计,避免某些风险,但与大多数ERC20
标准实现不同。
区分原生代币和ERC20代币
如果一个合约中出现这实现了原生代币和erc20代币,那么就要考虑到不要混淆实现逻辑,或者忘记实现原生代币的
无法兼容 USDT 等非标准 ERC20 代币
协议本应支持 ZetaChain 所支持的资产代币,但在实际运行中,它无法兼容如 USDT 这类非标准 ERC20 代币,即使它们已被加入支持列表中。
// 第 238-241 行 |
在 depositAndCall 函数中,协议调用 IERC20.transferFrom 试图将用户的代币转入合约中。但部分非标准 ERC20 代币(比如 USDT)在实现 transfer 或 transferFrom 方法时,没有返回布尔值(bool),违反了 ERC20 的标准。
由于 require(…) 表达式期望该调用返回 true,而 USDT 的调用会直接返回空值(无返回),因此 require 判断为 false,从而导致整个交易失败。
池子创造没有进行检查条件,
这个漏洞我发现了,但是由于影响写错了,不符合它们给的规定,下次一定要写规整点,这个漏洞错过太可惜了