# ParticleTrade: unvalidated Lien data in onERC721Received drains ETH

ParticleTrade's onERC721Received trusted attacker-supplied Lien data, letting a fake NFT callback inflate account balance and withdraw ~50 ETH.

- Payout: Real-world hack
- Severity: high
- Program: ParticleTrade
- Analysis by: DeFiHackLabs
- Disclosed: 2024-02-16
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2024-02/ParticleTrade_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20240216-particletrade
- Tags: area:web3 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:** ParticleExchange NFT lending/bidding contract (onERC721Received callback)
**Root cause:** onERC721Received processes a caller-supplied Lien struct and lienId without validating that the callback originates from a real NFT transfer or that the lien data matches stored state, so arbitrary lien/price fields are trusted.
**Steps:**
1. Fork mainnet at block 19231445.
2. Call `proxy.offerBid(address(this), 0, 0, 0)` to obtain a lienId.
3. Craft a fake `Lien` struct with attacker-controlled fields and ABI-encode it with `(lien, lienId, amount, Reservoir, zero, "0x")`.
4. Call `proxy.onERC721Received(zero, zero, tokenId, bytecode)` directly to inject the forged lien.
5. Repeat with a second forged `Lien` (lien2) and call `onERC721Received` again to inflate `accountBalance`.
6. Call `proxy.withdrawAccountBalance()` to drain ETH to the attacker.
**How found:** Likely source review of the exchange callback: noticing onERC721Received decodes and trusts caller-supplied Lien/lienId without verifying msg.sender is the NFT collection or that the lien exists.
**Exploitation:** Attacker directly invoked the exchange's onERC721Received callback with forged Lien data instead of going through a real NFT transfer, causing the contract to credit a fabricated balance which was then withdrawn as ~50 ETH.
**Insight:** When a contract exposes an ERC721/ERC1155 receiver callback that decodes business logic from the `data` blob, check whether it validates msg.sender and that the referenced lien/token state actually exists before crediting balances.
