QANX Bridge disclosure analysis
[continuously updated]

QANplatform
QANplatform

--

On the 18th of May, 2022 the QANX Bridge suffered an attack between 15:01:40 UTC and 18:20:25 UTC. The exploiter was able to drain 100,450,000 QANX from the QANX Bridge and sold it for 325 ETH on Uniswap which was later tunnelled into Tornado Cash. By the 26th of May, the hacker sold all of the stolen QANX Tokens. The issue was found and fixed and the bridge will be re-initiated after a comprehensive audit of all components. The missing amount of QANX tokens from the Bridge pool will be filled back from the Reserve Pool.

Let’s dive right into the details to have a deeper understanding of what happened.

What is a Bridge and why is it a prime target for attacks?

Blockchain bridges (or Network bridges / Cross-chain bridges) are a crucial part of the cryptocurrency ecosystem. Bridges are applications that allow people to move cryptocurrencies from one blockchain to another. With the appearance of network bridges, projects now have the possibility to have their tokens available on multiple blockchains at the same time. This benefits both the project and the token holders, as they can trade their tokens on a wider range of network ecosystems while benefiting from network-specific advantages, like cheaper transaction fees.

QANplatform launched the QANX Bridge on the 2nd of November, 2021. QANX Token holders were able to swap their QANX Tokens between Ethereum (ETH) and Binance Smart Chain (BSC) networks.

With the widespread adoption of bridges, they became a prime target for attacks. Nearly $1 billion in crypto assets have been stolen from blockchain bridges only in 2022. The largest known bridge hacks were Axie Infinity’s Ronin bridge hack ($624 Million), Poly Network ($611 Million), and Wormhole ($326 Million).

Bridges and blockchain interoperability

Chain interoperability is often misunderstood as connecting two chains together directly through some special, magical cryptography. This is almost never the case. Unless two specific chains were intentionally set up for cross-communication this is just not possible. To move assets across blockchain networks users have to depend on a trustee to accept funds on the depositing chain and release an equivalent value on the withdrawal chain.

This trustee might be composed of several participants (like a committee) but it will never be as decentralized as the chains themselves they are bridging between, no matter what the bridge authors say. Furthermore, since multi-party verification usually implies additional cost (time, compute, etc.), companies mostly run their own verification mechanisms which are completely centralized.

Components of the QANX Bridge

A properly designed bridge includes at least these components:

1] Smart contract on Deposit and Withdrawal chain

The smart contract is the piece of software which is responsible for the accounting of bridged assets on all chains being bridged.

Ideally, this is open source with verified code visible on all chain explorers (e.g. Etherscan, BscScan, etc.) .

Also ideally it should not use any proxy logic or other smart contract upgradability mechanisms to avoid elevated privileges which could easily backfire in case of a key compromise scenario.

The QANX Bridge smart contract fulfills both requirements as it is completely open source and does not feature any kind of upgradability.

The smart contract component is:

  1. Deployed on all chains being bridged.
  2. Ideally fully identical code on all chains.
  3. Responsible for receiving deposited assets.
  4. Responsible for making nonce reuse impossible.
  5. Responsible for validating the signature.
  6. Responsible for sending withdrawn assets.

2] User interface (UI)

This is the frontend (visible) part which is usually a web interface that connects with a wallet provider like MetaMask.

The responsibilities of it are:

  1. Give a comfortable, easy to understand user experience.
  2. Implement client-side part of DDoS protection (e.g. ReCaptcha).
  3. Send token deposit transaction to the deposit chain.
  4. Receive deposit transaction hash from deposit chain.
  5. Send deposit transaction hash to the API.
  6. Receive remaining block confirmation count from the API and display it to the user.
  7. If enough confirmations passed, display “awaiting signature” status to the user.
  8. Await and receive signature from the Signer.
  9. Send withdrawal transaction along with the received signature to the withdrawal chain to get the tokens.

3] API

This is the backend (non-visible) part of the user interface which fulfills the following:

  1. Validate DDoS prevention token received from User interface (e.g. ReCaptcha).
  2. Receive transaction hash of the deposit transaction which was submitted on the User interface by the user.
  3. Check whether the transaction was successfully completed on the deposit chain.
  4. If successful, check the block confirmation count of that transaction.
  5. If less confirmations than required, update the User interface about the remaining confirmation count.
  6. If enough confirmations, start advertising the hash of the successful transaction to the signer.
  7. Update the User interface to indicate that it is waiting for a signature now.
  8. Once the signature arrives from the Signer module, pass it to the User interface.

4] Signer

The signer is the entity that is responsible for producing the cryptographic signature for a given transaction. This signature will be validated by the smart contract (step 5. above in the smart contract component) when someone is trying to withdraw tokens.

This component must be very well protected as it is exposed to multiple threat models.

4.1. Multiple threat models of the Signer

4.1.1. Remote breach

Remote breaches can happen for mainly two reasons:

  1. Freshly discovered so-called Zero-Day Exploits which are impossible to protect against (e.g. Black-hat hackers discovering hardware vulnerabilities).
  2. Initially bad security configuration (e.g. System administrator not following current security best practices).

QANX Bridge’s signer module sits behind a regularly updated firewall which only allows it to communicate with the API. Furthermore, it does not have any open ports and has no means of any remote access to prevent this type of attack.

4.1.2. Signature key compromise

Signature key compromise can happen either physically (obtaining the drive containing the key) or as the next step of a Remote breach where the attacker could read the key from the filesystem after they gained access to the system.

QANX Bridge’s signer module loads the private key used for producing signatures to memory from a removable drive during the startup process which drive is then physically removed from the system to prevent this type of attack.

4.1.3. Manipulated data from API

Since APIs are a frequent target of various attacks due to being exposed to the public internet, it is often used to feed maliciously manipulated data to the signer.

QAN’s signer module does not trust any data returned by the API, it fetches and verifies transactions itself directly from the deposit chain (as detailed below).

4.2. Operational workflow of QANX Bridge Signer

4.2.1. Periodically asks the API for transaction hashes that are advertised as successful and have enough confirmations.
4.2.2. Once it receives such a transaction hash, it will load the transaction itself directly from the deposit chain.
4.2.3. If the transaction could be retrieved from the deposit chain, the signer will re-check again if:

  • The transaction was successfully completed indeed, which requires the deposited tokens to be transferred from the sender to the bridge smart contract (line 72).
  • Enough confirmations have passed indeed.
  • Input parameters (beneficiary, amount, withdrawChain) can be parsed from the transaction’s input calldata.
  • The nonce on the withdrawal chain is one less than the nonce on the deposit chain.

4.2.4. If all checks were successfully validated by the Signer itself as well, it will produce a signature.
4.2.5. This signature will be sent back to the API along with the transaction hash, so the API knows which transaction hash this particular signature belongs to so it can be returned to the correct user.

Good in the bad

After the bridge was shut down we reached out to multiple incident response teams and we had quite a hard time together to find the root cause of the problem which could be exploited.

We realized we could benefit from this situation in a way of finding great people to work together with. Anyone who is willing to give a shot at figuring out which part of the architecture could have been exploited and point out a simple fix is more than welcome in our engineering team.

If you are interested in the bug hunt please fill out this form: https://forms.gle/soZN5AvG2AKzJF8k9

  • Name
  • Email
  • Phone number
  • Expected salary
  • GitHub username

Accepted bug hunt participants will obtain a copy of the whole Bridge codebase in 24 hours, their only task is to find the root cause and propose the most elegant and simple fix to prevent it from happening ever again. Hint: it only takes a single line of code!

We will release the whole analysis regardless of the outcome of the bug hunt on June 12 (Sunday). The sha256 hash of the already completed analysis document is: 0x98111615061dcd6804c399824269601ed74fde0b68f06e338b1d920e5deb7d0d

UPDATE: June 12, 2022

The QANX Bridge source code was sent out to selected applications based on their GitHub activities and experience in this field. We would like to extend the bug hunt till Wednesday (June 15, 2022) 15:00 GMT+3 since till today nobody could find the bug. Hint: it only takes a single line of code!

23 developers signed to participate in the bug hunt. The bridge code was sent to 17 of these developers who have blockchain / cybersecurity experience. From the 17 developers only 1 could find the issue. We hereby congratulate to the successful bug hunter. We will reach out to him tomorrow.

As written previously, the QAN Team found the issue before the bug hunt. The sha256 hash of the already completed analysis document is: 0x98111615061dcd6804c399824269601ed74fde0b68f06e338b1d920e5deb7d0d — the original document can be found HERE to verify the sha256 hash.

Let’s dive right into the details to have a deeper understanding of what happened.

How the hacker could make the attack happen

After examining the above principles and operational logic it seems quite impossible to execute a successful attack against the system.

However, there is a piece of information which was not validated at all, that being the recipient of the deposit transaction, which must always be the address of the smart contract component.

This is what happened in detailed steps on 18–05–2022 before the bridge was shut down:

  1. 15:01:40 BSC 0xf1ac0681f19484e9b01bba2cfcd570542e0a65d3742db65d277debea89b53220
    The hacker clones the original bridge smart contract, modifies it and deploys it onto BSC. We will refer to this as the “fake bridge contract” from now on. The modification is that it will accept any amount of QANX tokens in the deposit function as a parameter, but will only make an actual transfer of 1 QANX token later, in contrast to the original contract, which would transfer the amount which was defined in the function parameter obviously.
  2. 15:31:03 BSC 0x71556a35cfe0f47ae4ef1cad22021b748b1be79cfc959aab92a7d41cc615a7b3
    The hacker sends a deposit to the fake bridge contract with the value of 4,500,000 QANX defined in the parameter, but only 1 QANX token is actually transferred due to the modification described in the previous step.
  3. 16:18:58 BSC 0x4c6f3a8c6e37cf8a0dcbdc4f396128302da73c2858af5292165bf0e6bd794000
    The hacker sends a deposit transaction on BSC using 1 QANX as amount to the real bridge contract to increase his nonce which is tracked and checked by the signer before signing. Note that 1 QANX token is defined as a parameter and 1 QANX token gets transferred acccordingly. This does not matter, the sole purpose of this transaction is to increase the nonce which serves as an input in the calculation of the Bridge Transaction ID (BTXID).
  4. 16:20:54 API 0x71556a35cfe0f47ae4ef1cad22021b748b1be79cfc959aab92a7d41cc615a7b3
    The hacker feeds the hash of the transaction which was sent to the fake bridge contract into the API module. The API checks that the transaction was successful and enough confirmations have passed. It also checks that the transaction calldata parameters can be decoded according to the ABI (Application Binary Interface) of the real bridge contract. The API did not check the tx.to field of the transaction, which points to the fake bridge contract instead of the real one. This is the first step where the attack could have been stopped, but this issue is not fatal (yet). Since the implemented checks of completeness, confirmations and decoding passed, the API starts advertising the TX hash 0x71556a35cfe0f47ae4ef1cad22021b748b1be79cfc959aab92a7d41cc615a7b3 to the signer for further verification and signing.
  5. 16:21:47 SIG 0x71556a35cfe0f47ae4ef1cad22021b748b1be79cfc959aab92a7d41cc615a7b3
    The signer module receives the advertised TX hash from the API. It will now pull the transaction by hash directly from the chain (BSC in this case) and performs the same checks as the API again:
  • ensure transaction completion
  • check confirmation count of the transaction
  • parse the calldata parameters of the transaction

Unfortunately the signer did not check the tx.to field of the transaction either, which points to the fake bridge contract instead of the real bridge contract. This is the cause of the fatal issue which caused the compromise:

The signer then calculates the bridge transaction ID (BTXID) from the calldata parameters (which includes the fake amount of 4,500,000 QANX tokens) and signs it, although only 1 QANX tokens were actually transferred.

FINAL Step: 16:23:14 ETH 0x13f4cf41e2e5436cf69bf7d687c6a3bed8aef66fe24437baee7931844efd78f7
The hacker sends the signature obtained from the Signer module as a result of step 5. in a withdrawal request to the real bridge contract on the Ethereum side. This is the first successful withdrawal on ETH in amount of 4,410,000 QANX which equals the fake deposited 4,500,000 QANX tokens minus the bridge fees which were 2% at the time of the attack.

What next?

The issue was found and fixed and the bridge will be re-initiated after a comprehensive audit of all components. The missing amount of QANX tokens from the Bridge pool will be filled back from the Reserve Pool.

We will announce the Bridge relaunch with the new audit report.

--

--

QANplatform
QANplatform

QANplatform is the Quantum-resistant Layer 1 hybrid blockchain platform.