# Reflexer GEB: unauthenticated quitSystem drains library-owned SAFEs

Shared GebProxyActions library was registered as SAFE owner; its unauthenticated quitSystem let anyone migrate four SAFEs' collateral and drain ~5.94 ETH.

- Payout: Real-world hack
- Severity: high
- Program: ReflexerGEB
- Disclosed: 2026-09-01
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-08/ReflexerGEB_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260901-reflexergeb
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** GebProxyActions shared library / GebSafeManager SAFE ownership
**Root cause:** Several SAFEs had ownsSAFE set to the shared GebProxyActions library address instead of a per-user DSProxy. GebProxyActions.quitSystem is public and unauthenticated, so calling it directly makes msg.sender equal the registered owner and passes safeAllowed.
**Steps:**
1. Identify SAFEs 3, 5, 8, 18 where `MANAGER.ownsSAFE(id) == 0x84FE...` (GebProxyActions).
2. Call `SAFE_ENGINE.approveSAFEModification(MANAGER)` and `MANAGER.allowHandler(PROXY_ACTIONS, 1)`.
3. For each SAFE: `GLOBAL_SETTLEMENT.processSAFE(ETH_A, handler)` to clear debt and free collateral.
4. Call `PROXY_ACTIONS.quitSystem(MANAGER, safeId, address(this))` directly on the library; manager sees msg.sender == library == ownsSAFE.
5. Call `GLOBAL_SETTLEMENT.freeCollateral(ETH_A)` to credit internal ETH-A tokenCollateral.
6. After loop: `ETH_A_JOIN.exit(this, amount)` then `WETH.withdraw(WETH.balanceOf(this))` to ETH.
**How found:** Likely source/on-chain review spotted that ownsSAFE for several SAFEs pointed at the shared GebProxyActions library rather than per-user DSProxies, and that quitSystem has no auth and relies solely on msg.sender matching ownsSAFE.
**Exploitation:** Permissionless: any EOA deploys a contract, approves the manager, and directly invokes the unauthenticated library function. Because the library itself is the registered SAFE owner, the manager's ownership check passes. In Global Settlement, processSAFE clears debt, quitSystem migrates free collateral to the attacker, freeCollateral converts it to internal ETH-A, then CollateralJoin.exit and WETH.withdraw yield ETH. Four SAFEs drained in one tx for ~5.9436 ETH.
**Insight:** In proxy-actions architectures, check whether any SAFE/CDP/vault is registered with the shared library itself as owner; a public unauthenticated function that trusts msg.sender will then be callable directly to migrate collateral. Also check Global Settlement states for free-collateral paths.
