Attackathon _ Fuel Network 32965 - [Blockchain_DLT - Critical] Messages to L included even on revert
Submitted on Mon Jul 08 2024 15:33:31 GMT-0400 (Atlantic Standard Time) by @NinetyNineCrits for Attackathon | Fuel Network
Report ID: #32965
Report type: Blockchain/DLT
Report severity: Critical
Target: https://github.com/FuelLabs/fuel-core/tree/v0.31.0
Impacts:
Direct loss of funds
Description
Brief/Intro
Messages to the L1 bridge are included in the block even in case of reverts. This allows theft of all tokens from the bridge.
Vulnerability Details
The function executor/src/executor.rs::update_execution_data
adds all message_ids from the MessageOut receipts of the current tx to the execution data even if the tx itself has reverted:
This means its possible to send out messages to the L1 but revert the tx, which un-does the burn of the bridged tokens. This fake-withdraw can be repeated over and over again and messages can still be relayed on L1, allowing theft of all tokens from the bridge
Impact Details
Theft of all tokens from the bridge on L1
References
Not applicable
Proof of concept
Proof of Concept
Link to the gist: https://gist.github.com/99crits/3ee583a149bc41ca62e76ccfbf7c0e89
This poc is based on the tests/bridge_erc20.ts
file in the fuel-bridge repository. It demonstrates the following flow:
Attacker and victim both deposit the same amount of the same token
Attacker withdraws 2 times, first time he reverts his tx at the end and the second time follows the regular flow. He can relay twice and will have double the amount of tokens he originally deposited.
Victim withdraws, L2 side will be successful and his tokens will be burned. The relay on L1 will fail, due to underflow (no tokens left)
This test requires a minor modification to the message_proof
endpoint on the client. Currently this endpoint does an early return if the tx has not succeeded:
However any attacker can run his own client and modify the endpoint like this:
All the necessary data for the proof is already on-chain, this modification just allows an easy retrieval of the necessary message-proof.
So the setup steps are the following:
Clone
fuel-core
from commit c5b425e2b3e05899e83bed0090865a7f4ec30c78 (latest one as of 2024-07-08)Add the line
Some(TransactionStatus::Failed { block_height, .. }) => block_height,
as additional matching branch toquery/message.rs
as shown aboveRun
docker build -t 99crits-fuel-core . -f deployment/Dockerfile
(command from the README with custom tag)Now in the
fuel-bridge
repository: Indocker/fuel-core/Dockerfile
change theFROM
directive toFROM 99crits-fuel-core:latest
Change to
fuel-bridge/docker
directory and runmake clean
and thenmake up
Then, go to the
fuel-bridge/packages/integration-tests/
directory and runpnpm install
Finally, paste the gist from above as
bridge_erc20_multi_withdrawal_using_reverts.ts
intointegration-tests/tests
and then runpnpm mocha -b -r ts-node/register 'tests/bridge_erc20_multi_withdrawal_using_reverts.ts'
Last updated