Decoding Poly Community $34 Billion Hack

[ad_1]

Learn Time: 8 minutes

Abstract:

On July 2nd, 2023, Poly Community suffered a hack of what was initially reported to be 34 billion {dollars}. The principle accuse of this can be a compromise of personal keys or a multi-signature service assault. The hacker exploited solid proofs to provoke withdrawal operations on the cross-chain bridge contracts across multiple chains. 

About Project:

Poly cross-chain ecosystem provides a platform for various chains to interact and transfer data along with carrying out cross-chain transactions. 

To learn more about the Project, check out the official documentation.


Vulnerability Analysis & Impact:

On-Chain Details:

Attacker Address: List of issued Poly Network Hackers 

Attack Transaction: List of attack transactions 

The Root Cause:

The root cause of the hack was not a logical bug but most likely a case of stolen/misused private keys.

To understand this, let’s first get the hang of Poly Network’s cross-chain interaction 

Below is a  poly’s sample cross-chain interaction between chains .

source

Notice that the DApp contract invoked by the users invokes the Cross-Chain Manager Contract 

This contract allows tokens to be transferred from the source chain to the destination chain.

A  Merkle Proof is required for the transaction’s legitimacy and to ensure that a transaction has been created and occurred on the relay chain. 

This gets encoded with other arguments that together withdraw these tokens in the destination chain, 

You can read more about the process from the official documentation.

The function verifyHeaderAndExecuteTx is responsible for the verification of the header and then the execution of the transaction. 

This is the central access point, though, where users can “unlock” tokens on the destination chain that was “locked” in the source chain. 

It takes 3 parameters 

  1. Transaction Merkle proof
  2. Header containing state root to verify the Merkle proof 
  3. A converted signature derived from Poly chain’s “keepers” (EOA’s)

The transfer of the token from the original chain is what Poly called as “lock”, and the retrieval of the token is termed as “unlock”.

Poly has a set of Externally Owned Accounts. These EOAs need to sign the “unlock” event at the destination chain also includes other relevant data of the source chain, for the transaction to happen. 

At this point, either 

  • The private keys that made the converted signature were compromised. or,
  • There is a logical bug somewhere in the implementation that returns Merkle root 

To verify the signature, this function is used 

function verifySig(bytes memory _rawHeader, bytes memory _sigList, address[] reminiscence _keepers, uint _m) inner pure returns (bool){

//_rawHeader = 0x0000000000000000000000001e8bb7336ce3a75ea668e10854c6b6c9530dab7…
//_sigList = // Listing of three signatures from
// 0x3dFcCB7b8A6972CDE3B695d3C0c032514B0f3825,
// 0x4c46e1f946362547546677Bfa719598385ce56f2,
// 0x51b7529137D34002c4ebd81A2244F0ee7e95B2C0

//_keepers= [“0x3dFcCB7b8A6972CDE3B695d3C0c032514B0f3825″,”0x4c46e1f946362547546677Bfa719598385ce56f2″,”0xF81F676832F6dFEC4A5d0671BD27156425fCEF98″,”0x51b7529137D34002c4ebd81A2244F0ee7e95B2C0”]

//_m = 3

        bytes32 hash = getHeaderHash(_rawHeader);
        uint sigCount = _sigList.size.div(POLYCHAIN_SIGNATURE_LEN);
        tackle[] reminiscence signers = new tackle[](sigCount);
//   signers = [
        //     0x4c46e1f946362547546677Bfa719598385ce56f2,
        //     0x3dFcCB7b8A6972CDE3B695d3C0c032514B0f3825,
        //     0x51b7529137D34002c4ebd81A2244F0ee7e95B2C0
        // ]

        bytes32 r;
        bytes32 s;
        uint8 v;
        for(uint j = 0; j  < sigCount; j++){
            r = Utils.bytesToBytes32(Utils.slice(_sigList, j*POLYCHAIN_SIGNATURE_LEN, 32));
            s =  Utils.bytesToBytes32(Utils.slice(_sigList, j*POLYCHAIN_SIGNATURE_LEN + 32, 32));
            v =  uint8(_sigList[j*POLYCHAIN_SIGNATURE_LEN + 64]) + 27;
            signers[j] =  ecrecover(sha256(abi.encodePacked(hash)), v, r, s);
            if (signers[j] == tackle(0)) return false;
        }
        return Utils.containMAddresses(_keepers, signers, _m);
    }

For the transaction to occur, the signature must be verified. 

Upon investigation, it has been clear that the attacker appropriately invoked this operate. 

It’s value noting that the listing of keepers has remained fixed for a very long time and has not been modified.

The speculation of key compromise was additional solidified when additional inspection was carried out on the Merkle Prover operate in hopes of discovering a logical bug within the implementation of good contract.

/* @discover                  Confirm Poly chain transaction whether or not exist or not
    *  @param _auditPath        Poly chain merkle proof
    *  @param _root             Poly chain root
    *  @return                  The verified worth included in _auditPath
    */
    operate merkleProve(bytes reminiscence _auditPath, bytes32 _root) inner pure returns (bytes reminiscence) {
        uint256 off = 0;
        bytes reminiscence worth;
        (worth, off)  = ZeroCopySource.NextVarBytes(_auditPath, off);

        bytes32 hash = Utils.hashLeaf(worth);
        uint dimension = _auditPath.size.sub(off).div(33);
        bytes32 nodeHash;
        byte pos;
        for (uint i = 0; i < dimension; i++) {
            (pos, off) = ZeroCopySource.NextByte(_auditPath, off);
            (nodeHash, off) = ZeroCopySource.NextHash(_auditPath, off);
            if (pos == 0x00) {
                hash = Utils.hashChildren(nodeHash, hash);
            } else if (pos == 0x01) {
                hash = Utils.hashChildren(hash, nodeHash);
            } else {
                revert(“merkleProve, NextByte for place data failed”);
            }
        }
        require(hash == _root, “merkleProve, count on root isn’t equal precise root”);
        return worth;
    }

The above operate takes 2 inputs 

_auditPath – It will comprise a leaf node adopted by a path by means of the Merkle tree that acts as a proof of it’s existence 

_root – This will take the state root of the Merkle tree 

According to this implementation –

The hash of the leaf node needing to correspond to the state root hash for the proof to succeed implies that the integrity of the data stored within the leaf node is crucial for the verification process. If this requirement was not in place, the attacker could have potentially manipulated the implementation by exploiting the flexibility allowed by the verifier.

Without the condition that the hash of the leaf node must match the state root hash, the attacker could have provided a zero-length witness and an empty path as proof. In this scenario, the attacker could have crafted an artificial state root containing an unlock command to send tokens to themselves. By bypassing the need for a valid leaf node hash, the attacker would have been able to create fraudulent proof that falsely claimed ownership or authorisation for certain actions.

However, because the hash of the leaf node must correspond to the state root hash for the proof to succeed, it ensures the integrity and authenticity of the data stored within the leaf node. This requirement prevents attackers from manipulating the implementation by constructing artificial state roots and gaining unauthorised access or control over the system.

This means that the possibility of private key exploitation has more weight out of the two possibilities. 

Despite this, there is no definitive proof that the private keys were stolen. 

Possibilities of a rug pull or a compromised off-chain software running 3 out of 4 keepers still holds true for the scenario described above 

In short, 

Poly network had a simple 3 of 4 multisig arrangement. 

The attacker used keys to sign proof that they owed BNB. 

The header containing the state root was correctly signed by 3 out of 4 addresses. This leads us to the direction of a private key exploit.

Attack Process:

  • The attacker called the lock function on the LockProxy cross-chain bridge contract to lock a small amount of Lever Token. 
  • The toChainId 6 corresponds to the BNB chain, which can be viewed at https://explorer.poly.network. If a transaction is visible on the Poly Network explorer, it indicates that it has been validated through the relay chain.
  • Switching to the BNB chain, the attacker used the verifyHeaderAndExecuteTx function to initiate withdrawal operations, but the quantity involved does not match the original lock amount. However, no record of this transaction was found upon querying the relay chain network.
  • The hacker tricked the “EthCrossChainManager” contract into thinking that the parameter was valid and authentic & executed it without any further checks. 
  • This allowed the hacker to issue tokens from PolyNetwork’s Ethereum pool to their own address on other chains, such as Metis, Polygon, and Binance Smart Chain.
  • The hacker repeated this process for other chains supported by PolyNetwork, such as Heco and Avalanche, by using similar malicious parameters and exploiting similar vulnerabilities in the corresponding contracts.
  • The hacker repeated this process for other chains supported by PolyNetwork, such as Heco and Avalanche, by using similar malicious parameters and exploiting similar vulnerabilities in the corresponding contracts.

Flow of Funds: 

The breakdown of assets minted by the hacker on different chains is as follows: – 

  • 99,999,184 BNB and 10 billion BUSD issued on Metis 
  • 999.8127T SHIB issued on Heco 
  • 87,579,118 COW and 999,998,434 OOE issued on Polygon 
  • 636,643,868 STACK and 88,640,563 GM issued on Polygon 
  • 2,175,053 03 issued on Polygon 
  • 378,028,371 STACK, 82,854,568 XTM, 11,026,341 SPAY, and 89,383,712 GM issued on Avalanche
  • 8,882,911 METIS, 926,160,132 DOV, 978,102,855 SLD

The attacker used multiple addresses to withdraw funds from cross-chain bridge contracts. 

All cross-chain operations were directed towards chain ID 6. 

 These are the addresses that hold the majority of the portion of assets:  

  1. 0xc8Ab4aa93949c377C32c069272425bd42738C42F
  2. 0x23f4CA51aa75d9d3f28888748d514173394Cc671
  3. 0xfD3E731AFf8B930337302f26EEf015CFA022b778
  4. 0x11c924f0B50c51CbF9Ac31a20365A38F24D4A4E8
  5. 0x2F6C25E3c93c0FC7fdDe2Ece8e370AE152a57B82
  6. 0xc8Ab4aa93949c377C32c069272425bd42738C42F

complete resolution image here

On Ethereum, assets worth $4.3 million were swapped for 674 ETH, with 1592 ETH unlocked, totalling 2267 ETH. The hacker still holds 10M BNB and 10B BUSD, which can’t be processed. 

Attacker’s Wallets: Here is a snippet of hacker’s wallet .


After the Exploit

  July 2nd, 2023 06:47:20 PM UTC, the Project acknowledge the incident and announced it through their Twitter.

July 3rd, 2023 The project continues to suspend it’s services for the user as shared via Twitter.


How could they have prevented the Exploit?

Fast Monitoring systems could have prevented this attack or at least reduce the impact.

The attacker would not have a chance if the response from Poly Network was faster. It took 7 hours for them to react to the situation. 

Poly Network bridge had its private keys compromised, leading to the loss of the bridged token. Bridges should have been decentralized.

Web3 security- Need of the hour

Why QuillAudits For Web3 Security? QuillAudits is well-equipped with tools and expertise to provide cybersecurity solutions saving the loss of millions in funds.

Want more Such Security Blogs & Reports?

Connect with QuillAudits on :

Linkedin | Twitter | Website | Newsletter | Discord | Telegram

Partner with QuillAudits :

12 Views



[ad_2]

Deixe um comentário

Damos valor à sua privacidade

Nós e os nossos parceiros armazenamos ou acedemos a informações dos dispositivos, tais como cookies, e processamos dados pessoais, tais como identificadores exclusivos e informações padrão enviadas pelos dispositivos, para as finalidades descritas abaixo. Poderá clicar para consentir o processamento por nossa parte e pela parte dos nossos parceiros para tais finalidades. Em alternativa, poderá clicar para recusar o consentimento, ou aceder a informações mais pormenorizadas e alterar as suas preferências antes de dar consentimento. As suas preferências serão aplicadas apenas a este website.

Cookies estritamente necessários

Estes cookies são necessários para que o website funcione e não podem ser desligados nos nossos sistemas. Normalmente, eles só são configurados em resposta a ações levadas a cabo por si e que correspondem a uma solicitação de serviços, tais como definir as suas preferências de privacidade, iniciar sessão ou preencher formulários. Pode configurar o seu navegador para bloquear ou alertá-lo(a) sobre esses cookies, mas algumas partes do website não funcionarão. Estes cookies não armazenam qualquer informação pessoal identificável.

Cookies de desempenho

Estes cookies permitem-nos contar visitas e fontes de tráfego, para que possamos medir e melhorar o desempenho do nosso website. Eles ajudam-nos a saber quais são as páginas mais e menos populares e a ver como os visitantes se movimentam pelo website. Todas as informações recolhidas por estes cookies são agregadas e, por conseguinte, anónimas. Se não permitir estes cookies, não saberemos quando visitou o nosso site.

Cookies de funcionalidade

Estes cookies permitem que o site forneça uma funcionalidade e personalização melhoradas. Podem ser estabelecidos por nós ou por fornecedores externos cujos serviços adicionámos às nossas páginas. Se não permitir estes cookies algumas destas funcionalidades, ou mesmo todas, podem não atuar corretamente.

Cookies de publicidade

Estes cookies podem ser estabelecidos através do nosso site pelos nossos parceiros de publicidade. Podem ser usados por essas empresas para construir um perfil sobre os seus interesses e mostrar-lhe anúncios relevantes em outros websites. Eles não armazenam diretamente informações pessoais, mas são baseados na identificação exclusiva do seu navegador e dispositivo de internet. Se não permitir estes cookies, terá menos publicidade direcionada.

Visite as nossas páginas de Políticas de privacidade e Termos e condições.