# OrderFactory: missing access control in createOrderForBuyer drains buyers

OrderFactory's permissionless createOrderForBuyer let any caller create an order for an arbitrary buyer, pulling their full ETH balance into an order where the attacker-controlled seller could abort() and sweep it.

- Payout: Real-world hack
- Severity: critical
- Program: OrderFactory
- Disclosed: 2026-09-08
- Write-up quality: 8/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-09/OrderFactory_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260908-orderfactory
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:critical tech:evm tech:solidity technique:reverse-engineering technique:source-review vuln:access-control vuln:business-logic

**Target:** Ethereum smart contract order factory / account system
**Root cause:** createOrderForBuyer only checks getState(buyer)==1 and never verifies msg.sender==buyer or a buyer signature/nonce, and trusts the attacker-supplied seller; the factory then pulls the buyer's full balance into a new order proxy and writes the attacker's seller address into the order's privileged slot.
**Steps:**
1. Fork mainnet at block 25933638 (parent of attack tx).
2. For each targeted buyer, confirm `getState(buyer)==1` (the only gate).
3. Predict order proxy address via `vm.computeCreateAddress(DEPLOYER, nonce)`.
4. Call factory selector `0xbde886fc` with `(buyer, seller=attacker, P2, 0, attacker, symbols, prices, params, 0)` where params[0] matches the buyer's stored order amount.
5. Factory moves buyer's full `accountBalances(buyer)` into the new order proxy and stores attacker as seller.
6. Attacker calls `order.abort(attacker)` — passes seller check and forwards full ETH to attacker.
7. Repeat per buyer; assert total profit equals sum of buyer balances (~20.247 ETH).
**How found:** Contracts unverified; researcher recovered the createOrderForBuyer selector (0xbde886fc) and 9-arg tuple from the tx trace, 4-byte-matched getState/abort/accountBalances names, and confirmed via direct reads that getState==1 was the only gate and accountBalances matched the drained amounts.
**Exploitation:** Attacker helper contract calls the permissionless createOrderForBuyer for each victim buyer with itself as seller; the factory pulls each buyer's entire ETH balance into a freshly deployed order proxy and records the attacker as the privileged seller; the attacker then calls abort(address) on each order to sweep the ETH to itself. One tx drained 5 buyers for 20.247 ETH.
**Escalation:** Looping over multiple active buyers in a single transaction scaled the drain from one account to five (20.247 ETH in this tx, ~24.7 ETH incident total).
**Insight:** On unverified contracts, reconstruct the ABI from the tx trace and 4-byte selector matching, then check whether state-changing entrypoints gate on msg.sender or only on a stored 'active' flag — order-creation factories that accept a caller-supplied privileged role (seller) are prime targets for permissionless drain via the role's sweep function.
