Analyse des Prinzips des EGD-Preismanipulationsangriffsvorfalls – phalcon+etherscan

Weitere verwandte Inhalte finden Sie auf: Persönliche Homepage

Informationen zum EGD-Angriff

Passiert auf BSC

EGD-Finance-Codeanalyse und Erklärung des Angriffsprozesses

Alle Aufrufe des EGD_Finance-Vertrags erfolgen über den Proxy-Vertrag.

Klicken Sie im Blockchain-Browser auf „Als Proxy lesen“, um den endgültigen Vertrag anzuzeigen.

EGD FinanceDie im Vertrag implementierte Hauptfunktion besteht darin, USDT für einen bestimmten Zeitraum zu verpfänden, und Sie können den Belohnungs-EGD-Token abheben , der einer Bankeinzahlung entspricht. Nach der Einzahlung für einen bestimmten Zeitraum können Sie die Zinsen abheben.

Die folgenden Schritte zum Abstecken und Einlösen von Belohnungen sind alle Transaktionsschritte, die tatsächlich vom Angreifer initiiert wurden.

Versprechensschritte

Adresse 0xbc5e8602c4fba28d0efdbf3c6a52be455d9558f5 | BscScan ruft stake()die Funktion des Angriffsvertrags auf, um die entsprechende Hypothekenoperation durchzuführen. Diese Adresse sollte auch die Adresse des Angreifers sein, der den Angriffsvertrag erstellt hat.

Die spezifischen Transaktionen sind wie folgt: BNB Smart Chain Transaction Hash (Txhash) Details | BscScan

Wir können die spezifischen Anrufinformationen dieser Transaktion auf Phalcon sehen:

Bild-20231219160115062

Hier ist eine weitere Analyse:

EGD_Finance | Adresse 0x93c175439726797dcee24d08e4ac9164e88e7aee | Die Funktion in BscScan bond()sollte einfach den folgenden Einladenden ausfüllen. Er sollte mit web2 identisch sein. Der Einladende jeder Adresse hängt mit dem Pfandeinkommen zusammen.

function bond(address invitor) external {        
        require(userInfo[msg.sender].invitor == address(0), 'have invitor');
        require(userInfo[invitor].invitor != address(0) || invitor == fund, 'wrong invitor');
        userInfo[msg.sender].invitor = invitor;
        userInfo[invitor].refer ++;

    }

Der nächste swapETHForExactTokens()Aufruf ist eine sehr häufige Token-Austauschoperation in Defi. Betrachtet man den Quellcode anhand der Adresse, stimmt er mit der entsprechenden Funktion von uniswap_v2 überein

Aus dem Namen ist ersichtlich, dass eine unbestimmte Menge ETH gegen eine bestimmte Anzahl an Token getauscht wird. Es lässt sich feststellen, dass es sich bei dem Tausch um USDT handelt.

Uniswap-Parameterliste

function swapETHForExactTokens(
    uint amountOut, // 交易获得的代币数量
    address[] calldata path, // 交易路径列表
    address to, // 交易获得的 token 发送到的地址
    uint deadline // 过期时间
) external virtual override payable ensure(deadline) returns (
    uint[] memory amounts // 交易期望数量列表
){
    ...
}

PancakeSwap: Router v2 | Adresse 0x10ed43c718714eb63d5aa57b78b54704e256024e | BscScan

    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        virtual
        override
        payable
        ensure(deadline)
        returns (uint[] memory amounts)
    {
    	//检查是否为WETH进行交换
        require(path[0] == WETH, 'PancakeRouter: INVALID_PATH');
        // 从library中获知得到amountOut数量的USDT,需要多少ETH
        amounts = PancakeLibrary.getAmountsIn(factory, amountOut, path);
        //发给pancake的ETH必须大于所需数量
        require(amounts[0] <= msg.value, 'PancakeRouter: EXCESSIVE_INPUT_AMOUNT');
        // 将 WETH 换成 ETH(对应phalcon的操作)
        IWETH(WETH).deposit{value: amounts[0]}();
        // 将 amounts[0] 数量的 path[0] 代币从用户账户中转移到 path[0], path[1] 的流动池
        assert(IWETH(WETH).transfer(PancakeLibrary.pairFor(factory, path[0], path[1]), amounts[0]));
        // 按 path 列表执行交易集合,不细究了,之后再详细看uniswap-qwq
        _swap(amounts, path, to);
        // 返回多余的ETH
        if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
    }

Schließlich stake()wird die Funktion des Agentenvertrags aufgerufen und 100 USDT verpfändet. Aus dem Anrufdatensatz in Phalcon geht hervor, dass der spezifische Funktionscode nicht analysiert wird. Er zeichnet hauptsächlich viele relevante Informationen für nachfolgende Belohnungsberechnungen auf Es ist ersichtlich, dass EGD_Finance | Adresse 0x93c175439726797dcee24d08e4ac9164e88e7aee | BscScan

Bild-20231219162437653

Schritte zum Einlösen von Prämien

Adresse 0xee0221d76504aec40f63ad7e36855eebf5ea5edd | BscScan Der Angreifer ruft harvest()die Funktion des Angriffsvertrags auf, um die entsprechende Belohnung einzulösen. Die Transaktionsanalyse von Phalcon ist in der folgenden Abbildung dargestellt:

Bild-20231219162808682

Zunächst wird der Vertrag aufgerufen calculateAll()函数, um die Pfandprämie des Benutzers zu berechnen und wie viel Gesamteinkommen der Benutzer erzielen kann.

    function calculateReward(address addr, uint slot) public view returns (uint){
        UserSlot memory info = userSlot[addr][slot];
        if (info.leftQuota == 0) {
            return 0;
        }
        uint totalRew = (block.timestamp - info.claimTime) * info.rates;
        if (totalRew >= info.leftQuota) {
            totalRew = info.leftQuota;
        }
        return totalRew;
    }

Dann startete der Benutzer den Flash-Darlehensvorgang.

Zuerst wurden 200 USDT PancakeSwapüber den Anruf (0x16b9a82891338f9ba80e2d6970fdda79d1eb0dae) geliehen , und dann waren der optimistische Transfer- und Rückrufangriffsvertrag im vorherigen Artikel zur Flash-Kreditanalyse sichtbar.swap()函数pancakeCall()函数

Nachdem pancakecall()函数中ein weiterer Flash-Kredit initiiert wurde, wurden 424456 USDT von Pancake LPs(0xa361433e409adac1f87cdf133127585f8a93c67d) geliehen und dann wieder zurückgerufen . Dies ist die Belohnung, die der Benutzer zugesagt hatte, bevor er sich selbst einlöste.swap()函数pancakeCall()函数claimAllReward()函数

Es sollte offensichtlich sein, warum die Prämien hier eingelöst werden. Dies liegt daran, dass die große Menge an Schnellkrediten die Berechnungsmethode der Prämien beeinflusst. Es sollte eine Lücke in der Funktion zur Berechnung der Pfandprämien geben.

Gehen Sie nun zum PancakeSwap: Router v2 | Address 0x10ed43c718714eb63d5aa57b78b54704e256024e | BscScan- Projekt claimAllReward(), um sich den spezifischen Quellcode anzusehen und detaillierte Kommentare abzugeben:

 function claimAllReward() external {
 		//判断是否存在对应的质押
        require(userInfo[msg.sender].userStakeList.length > 0, 'no stake');
        require(!black[msg.sender],'black');
        //获取质押时的,一系列质押记录,包括金额、时间戳等等
        uint[] storage list = userInfo[msg.sender].userStakeList;
        uint rew;
        uint outAmount;
        uint range = list.length;
        //计算对应的奖励
        for (uint i = 0; i < range; i++) {
            UserSlot storage info = userSlot[msg.sender][list[i - outAmount]];
            require(info.totalQuota != 0, 'wrong index');
            //不能超过一个最大奖励
            uint quota = (block.timestamp - info.claimTime) * info.rates;
            if (quota >= info.leftQuota) {
                quota = info.leftQuota;
            }
            //关键步骤,计算对应的奖励,仔细看一下getEGDPrice()函数
            //根据EGD的价格,来确定奖励多少EGD
            rew += quota * 1e18 / getEGDPrice();
            //下面是一些计算账户剩下最大奖励,以及账户余额(+利息)等操作
            info.claimTime = block.timestamp;
            info.leftQuota -= quota;
            info.claimedQuota += quota;
            if (info.leftQuota == 0) {
                userInfo[msg.sender].totalAmount -= info.totalQuota;
                delete userSlot[msg.sender][list[i - outAmount]];
                list[i - outAmount] = list[list.length - 1];
                list.pop();
                outAmount ++;
            }
        }
        //更新相应的质押列表
        userInfo[msg.sender].userStakeList = list;
        //发送响应的奖励
        EGD.transfer(msg.sender, rew);
        userInfo[msg.sender].totalClaimed += rew;
        emit Claim(msg.sender,rew);
    }
    function getEGDPrice() public view returns (uint){
    	//可在phalcon上看到行营的记录
        uint balance1 = EGD.balanceOf(pair);
        uint balance2 = U.balanceOf(pair);
        //EGD的价格仅仅是根据两种代币的实时数量(流动性)来进行计算,可以被攻击者操纵
        return (balance2 * 1e18 / balance1);
    }
    function initialize() public initializer {
        __Context_init_unchained();
        __Ownable_init_unchained();
        rate = [200, 180, 160, 140];
        startTime = block.timestamp;
        referRate = [6, 3, 1, 1, 1, 1, 1, 1, 2, 3];
        rateList = [547,493,438,383];
        dailyStakeLimit = 1000000 ether;
        wallet = 0xC8D45fF624F698FA4E745F02518f451ec4549AE8;
        fund = 0x9Ce3Aded1422A8c507DC64Ce1a0C759cf7A4289F;
        EGD = IERC20(0x202b233735bF743FA31abb8f71e641970161bF98);
        U = IERC20(0x55d398326f99059fF775485246999027B3197955);
        router = IPancakeRouter02(0x10ED43C718714eb63d5aA57B78B54704E256024E);
        pair = IPancakeFactory(router.factory()).getPair(address(EGD),address(U));
    }

Der Preis von EGD wird basierend auf der Anzahl von zwei Token an einer Adresse berechnet. Wir initialize()函数erhalten die Paaradresse in. Die Paaradresse wird basierend auf der Router-Adresse berechnet. Der Router ist ein Proxy-Vertrag. Im Blockchain-Browser können wir sehen Dass die Adresse des Paares 0xa361433e409adac1f87cdf133127585f8a93c67d ist, was ein Vertrag ist, der Liquidität für Pancake bereitstellt, kommt mir bekannt vor.

An diesem Punkt müssen wir herausgefunden haben, warum der Angriff erfolgreich war?

  • Der Benutzer hat sich zunächst Pancake LPseine große Menge USDT von 0xa361 geliehen ... über einen Flash-Kredit, was zur Pancake LPsPaarung von USDT und EGD führte und der Preis von EGD sehr günstig wurde.
  • Zu diesem Zeitpunkt pancakeCall()回调函数löst der Benutzer die Belohnung ein. Die Belohnung wird Pancake LPsbasierend auf der Anzahl der beiden Token im EDG-Preis berechnet. Dadurch ist der Preis von EDG sehr günstig. Dies ist rewdie Berechnungsformel, die der Benutzer sieht erhält eine überschüssige Belohnung.

Das Folgende ist eine kurze Einführung in nachfolgende Aufrufe von Phalcon:

Geben Sie zunächst Pancake LPsden oben geliehenen Schnellkredit zurück und führen Sie dann die entsprechende K-Wert-Überprüfung durch (stellen Sie sicher, dass die Rückzahlung = Originalbetrag + Bearbeitungsgebühr ist).

Führen Sie dann PancakeSwap: WBNB-BSC-USD 2die entsprechende Genehmigungsautorisierung für den geliehenen Schnellkredit durch.

Rufen Sie die Funktion swapExactTokensForTokensSupportingFeeOnTransferTokens auf, um alle erhaltenen EGD in USDT umzutauschen

Anschließend PancakeSwap: WBNB-BSC-USD 2wird der geliehene Flash-Darlehen zurückgegeben und der entsprechende k-Wert überprüft.

Am Ende profitierte der Angreifer von 36044 USDT

Supongo que te gusta

Origin blog.csdn.net/m0_53689197/article/details/135101484
Recomendado
Clasificación