Post Mortem of a reentrancy attack using bridged tokens on the Gnosis Chain
====
On 15th March 2022 tokens bridged with the OmniBridge were used in attacks on the Agave and Hundred Finance protocols on the Gnosis Chain.
Attack transactions:
- https://blockscout.com/xdai/mainnet/tx/0xa262141abcf7c127b88b4042aee8bf601f4f3372c9471dbd75cb54e76524f18e
- https://blockscout.com/xdai/mainnet/tx/0x534b84f657883ddc1b66a314e8b392feb35024afdec61dfe8e7c510cfac1a098
*The following text does not illustrate the exact details of the exploit but describes the specifics of OmniBridge bridged tokens utilized in the attack.*
The Omnibridge inherited functionality from the original version of the TokenBridge, using the ERC677 extension for the token contract to simplify the UX. ERC677-compatible tokens are bridged using the `transferAndCall` invocation rather than a sequence of `approve` and `transferFrom` calls.
This behavior was extended to the `transfer` method to accomodate users accidentally calling `transfer` rather than `transferAndCall` when bridging tokens. When called, tokens are bridged instead of locked in the bridge contract.
https://github.com/poanetwork/tokenbridge-contracts/blob/a620e47b1c6974a3e549bab22edf648681da2f1e/contracts/ERC677BridgeToken.sol
```
function transfer(address _to, uint256 _value) public returns (bool) {
require(superTransfer(_to, _value));
callAfterTransfer(msg.sender, _to, _value);
return true;
}
function callAfterTransfer(address _from, address _to, uint256 _value) internal {
if (AddressUtils.isContract(_to) && !contractFallback(_from, _to, _value, new bytes(0))) {
require(!isBridge(_to));
emit ContractFallbackCallFailed(_from, _to, _value);
}
}
function isBridge(address _address) public view returns (bool) {
return _address == bridgeContractAddr;
}
function contractFallback(address _from, address _to, uint256 _value, bytes _data) private returns (bool) {
return _to.call(abi.encodeWithSelector(ON_TOKEN_TRANSFER, _from, _value, _data));
}
```
This version of the contract was audited by QuantStamp 8 January, 2020: https://github.com/poanetwork/tokenbridge/blob/master/audit/quantstamp/POA-Network-Token-bridge-security-assessment-report.pdf
Bridging was organized so that any token transferred from the Ethereum Mainnet using the initial Omnibridge deployment implemented this behavior:
https://blockscout.com/xdai/mainnet/address/0xf8D1677c8a0c961938bf2f9aDc3F3CFDA759A9d9
---
A 2nd audit on 9 November, 2020 (also conducted by Quantstamp)(https://github.com/poanetwork/tokenbridge/blob/master/audit/quantstamp/POA-Network-TokenBridge-contracts-5.4.1-security-assessment-report.pdf) revealed an incompatibility with the ERC20 standard.
![](https://i.imgur.com/4xKVJg4.png)
As a result, the behavior of the `callAfterTransfer` method was modified as follows:
https://github.com/poanetwork/tokenbridge-contracts/blob/f70426c8418ca8a96e1081e3ac9d5b97fb5f80fa/contracts/ERC677BridgeToken.sol
```
function callAfterTransfer(address _from, address _to, uint256 _value) internal {
if (isBridge(_to)) {
require(contractFallback(_from, _to, _value, new bytes(0)));
}
}
```
This means that even if a contract is executed as part of the `transfer` call, the contract must be registered as a bridge contract in the token contract storage.
This implementation was then deployed and used for subsequent tokens bridged through the OmniBridge:
https://blockscout.com/xdai/mainnet/address/0x199084efbd7fe14d217BBF22FDC6E2eD7266dDD4
However, this new implementation could not be used for previously bridged tokens since the token contracts are not upgradable. Although a proxy template was used by the bridged tokens to reduce gas usage for the tx executing the very first token transfer, the upgradability functionality was not implemented for security reasons.
Prior to the new implementation, [517 tokens had already been bridged](https://gist.github.com/akolotov/8860a966f0b5bcc7cb50f99ca3f1393b) to the Gnosis Chain including WETH, USDC, GNO, LINK, WBTC and many others.
While the behavior of these bridged tokens does not strictly comply with the ERC20 standard, it was deemed safe for standard user operations though it is possible to execute arbitrary code within the `transfer` and `transferFrom` methods.
Audit reports and functional descriptions are available in the [bridge documentation](https://docs.tokenbridge.net/) but this information was not proactively distributed to developers deploying applications on the Gnosis Chain. Projects that forked existing protocols without implementing additional security processes related to bridged tokens became vulnerable to the reentrancy attack.