Order Factory Exploit Explained: Missing Authorization Allowed Attackers to Drain Buyer ETH | PandaSecurity
Web3 Security

Order Factory Exploit Explained: Missing Authorization Allowed Attackers to Drain Buyer ETH

PandaSecurity Team •
#Smart Contract Vulnerability#Ethereum Security#DeFi Audit
Ethereum smart contract exploit analysis and DeFi security audit

Smart contract vulnerabilities often come from simple logic mistakes rather than complicated technical failures.

The Order Factory incident is a good example. The protocol allowed anyone to create an order on behalf of another buyer without verifying authorization. By controlling the seller field during order creation, an attacker was able to become the privileged seller and later withdraw the buyer’s ETH.

According to the available analysis, the attack transaction drained 20.247 ETH from five buyers, with the total reported incident loss around 24.7 ETH.

At PandaSecurity, we frequently see similar patterns during smart contract reviews: a function appears convenient for users, but missing authorization checks can turn it into a critical attack surface.

Incident Overview

The affected component was an Ethereum Order Factory contract.

The vulnerable flow involved:

The security assumption was that only an authorized party should be able to create an order for a buyer.

However, the implementation did not enforce this assumption.

Key information:

Chain: Ethereum

Vulnerability Type:
Missing authorization / access control

Impact:
Approximately 20.247 ETH drained in the reproduced attack transaction

Root Cause:
Anyone could call createOrderForBuyer() with an arbitrary buyer and attacker-controlled seller

The Vulnerable Logic

The vulnerable function was identified by selector:

0xbde886fc

The factory contract was not verified, but analysis of transaction traces and function selectors reconstructed the following logic:

function createOrderForBuyer(
    address buyer,
    address seller,
    ...
) external {

    require(getState(buyer) == 1);

    // create order
    // move buyer funds
    // assign seller
}

The critical problem was the authorization logic.

The contract only checked:

getState(buyer) == 1

This confirmed that the buyer had an active request, but it did not verify:

As a result, any external account could create an order for another user.

Attack Flow Explained

The attack consisted of several steps.

Step 1: Find Active Buyers

The attacker searched for buyers whose state was active:

getState(buyer) == 1

These users also had ETH stored in the account system.

Step 2: Create an Order for Another User

Instead of the buyer creating the order, the attacker called:

createOrderForBuyer(
    buyer,
    attackerAddress,
    ...
)

The attacker supplied their own address as the seller.

Because the contract only checked the buyer state, the transaction succeeded.

Step 3: Become the Privileged Seller

During order initialization, the attacker-controlled seller address was stored.

The order contract effectively recorded:

seller = attacker

This gave the attacker access to seller-only functionality.

Step 4: Abort the Order and Withdraw ETH

The order contract contained an abort function:

function abort(address to) external {

    require(msg.sender == seller);

    payable(to).transfer(address(this).balance);

}

Since the attacker was now the seller, the authorization check passed.

The attacker could then transfer the entire ETH balance of the order to themselves.

Root Cause Analysis

The main issue was not a complicated Solidity bug.

It was a missing authorization design.

The protocol trusted user-controlled input:

seller = caller provided address

without verifying whether the buyer agreed to that seller.

The contract assumed:

“Someone creating an order for a buyer must be authorized.”

But the code only checked:

“Does this buyer currently have an active request?”

Those are completely different security assumptions.

Why This Vulnerability Was Dangerous

This vulnerability affected user funds because the factory performed several sensitive actions:

Any one of these operations requires strict authorization.

Combining them created a direct fund-loss path.

Lessons for Smart Contract Developers

1. Never confuse state validation with authorization

A check like:

require(userExists);

does not prove:

require(msg.sender == user);

Developers should clearly separate:

Both checks may be required.

2. Use signatures for delegated actions

If a protocol wants someone else to create an order on behalf of a user, it should require explicit authorization.

Common solutions include:

Example:

verifySignature(
    buyer,
    seller,
    amount,
    nonce
);

The buyer should approve the exact action being executed.

3. Avoid attacker-controlled privileged roles

Any parameter that affects permissions should receive extra scrutiny.

Dangerous examples:

owner = userInput;

seller = userInput;

admin = userInput;

If a user-controlled value becomes a privileged identity, the entire security model may fail.

How PandaSecurity Reviews Similar Issues

At PandaSecurity, we analyze smart contracts by looking beyond individual functions.

Our review process focuses on:

Many critical vulnerabilities come from differences between protocol design intentions and actual execution paths.

Final Thoughts

The Order Factory exploit demonstrates an important lesson in smart contract security:

Authorization must be enforced by code, not assumptions.

A function that allows “creating an order for a buyer” sounds useful, but without proper verification it becomes a powerful attack primitive.

For DeFi protocols managing user assets, every action involving ownership, permissions, and fund movement should be reviewed from an attacker’s perspective.

PandaSecurity helps Web3 teams identify smart contract vulnerabilities through manual security research, protocol analysis, and comprehensive smart contract audits.

Related articles

← Back to Blog