2026-05-07 · Loss: 65,645.94 USDT · Price Manipulation
On 2026-05-07 08:14:47 UTC, the White Eagle withdrawal contract on BNB Chain was exploited through a spot-price manipulation logic flaw in its withdrawal path. The attacker used an orchestrator plus 11 helper contracts to call withdraw() repeatedly, then sold each WEGL payout into the WEGL/USDT PancakeSwap pool before the next withdrawal repriced the same USD-denominated balance into even more WEGL. The withdrawal contract lost 9888.680557825521425533 WEGL, while the profit receiver collected 65645.944170470965224333 USDT. In short, the contract converted internal USD balances into WEGL using a live AMM quote, so each same-transaction dump made subsequent withdrawals overpay in WEGL.
White Eagle withdrawal contract at 0x0079517e4157f95cefda99d295fd74fc9b9896eb on BNB Chain. It is not a proxy per the planner stage. Source type: verified at artifacts/analysis_0x538c8964c841369bee9fd52d3ad87de9202fd182caaa32acf58095d02f6b359d/0x0079517e4157f95cefda99d295fd74fc9b9896eb/weglcontract.sol.
withdraw() (0x3ccfd60b) in weglcontract.sol, with pricing delegated to getWeglAmountFromUsd(uint256).
function withdraw() public nonReentrant {
User storage user = users[msg.sender];
require(user.isExist, "User not exist");
uint256 roiPending = _calculatePendingROI(msg.sender);
uint256 levelPending = _calculatePendingLevelIncome(msg.sender);
uint256 uplinePending = _calculatePendingUplineIncome(msg.sender);
if (roiPending > 0) {
user.earnedFromRoi += roiPending;
_distributeRoiToInvestments(msg.sender, user);
}
if (levelPending > 0) {
user.earnedFromLevel += levelPending;
user.accumulatedLevelIncome = 0;
emit LevelIncome(msg.sender, levelPending);
}
if (uplinePending > 0) {
user.earnedFromUpline += uplinePending;
emit UplineIncome(msg.sender, uplinePending);
}
user.lastLevelWithdraw = block.timestamp;
user.lastUplineWithdraw = block.timestamp;
_creditIncome(user, roiPending + levelPending + uplinePending);
uint256 payoutUSD = user.availableWalletUSD;
require(payoutUSD > 0, "No funds");
user.availableWalletUSD = 0;
uint256 totalWegl = getWeglAmountFromUsd(payoutUSD); // <-- VULNERABILITY
require(weglToken.balanceOf(address(this)) >= totalWegl, "Contract Low Bal");
uint256 burnAmount = (totalWegl * 5) / 100;
uint256 userAmount = totalWegl - burnAmount;
if (msg.sender == COMPANY_ID) {
// ...
} else {
_safeTransfer(weglToken, msg.sender, userAmount);
}
emit Withdraw(msg.sender, userAmount, payoutUSD);
}
function getWeglAmountFromUsd(uint256 _usdAmount) public view returns (uint256) {
address[] memory path = new address[](2);
path[0] = address(usdtToken);
path[1] = address(weglToken);
return pancakeRouter.getAmountsOut(_usdAmount, path)[1]; // <-- VULNERABILITY
}
Expected behavior: a withdrawal contract that tracks balances internally in USD should redeem WEGL using a stable conversion source, a stored exchange rate, or a mechanism that cannot be worsened by the same withdrawal sequence. One withdrawal should not make the next user with the same USD balance receive more WEGL just because the previous recipient dumped into the same AMM pool.
Actual behavior: withdraw() zeroes availableWalletUSD, then converts that USD amount to WEGL through PancakeSwap router getAmountsOut() on the live WEGL/USDT pool. The trace shows each helper immediately selling its received WEGL into the same pool, so the spot quote observed by the next withdraw() returns a larger WEGL amount for the same USD-denominated balance.
This makes the primary flaw a logic_error, with price_manipulation as the secondary technique. The attack does not rely on reentrancy or missing auth in withdraw(); it relies on coupling payout sizing to a manipulable spot price that the attacker moves between identical withdrawal calls in the same transaction.
withdraw() function.withdraw() priced the helper’s USD-denominated wallet balance into WEGL using PancakeSwap’s live getAmountsOut() quote.9888.680557825521425533 WEGL from the withdrawal contract.0xcd935e0462c5b32ee4c11fb5e2d5179508ee5d7e → 0xfc565edbfa06ee13edfa7b8b4677f89ab2382854 claim() (0x4e71d92d) via CALL.0xfc565edbfa06ee13edfa7b8b4677f89ab2382854 → helper 0x5650c7a41ef11dfa30dc295fc4d857b7c9bbc87a Claim(bool) (0xe60b3625) via CALL.0x0079517e4157f95cefda99d295fd74fc9b9896eb withdraw() (0x3ccfd60b) via CALL.0x10ed43c718714eb63d5aa57b78b54704e256024e getAmountsOut(uint256,address[]) (0xd06ca61f) via STATICCALL.0x9247a22981ad64e9bd1d8b9b303a2122cd88838c getReserves() (0x0902f1ac) via STATICCALL.0x5722ce859d00d6c59354c0750263180ffdc8ab7f balanceOf(address) (0x70a08231) via STATICCALL.burn(uint256) (0x42966c68) via CALL.transfer(address,uint256) (0xa9059cbb) to helper via CALL.balanceOf(address) (0x70a08231) via CALL.swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256,uint256,address[],address,uint256) (0x5c11d795) via CALL.transferFrom(address,address,uint256) (0x23b872dd) via CALL.getReserves() (0x0902f1ac) via STATICCALL.swap(uint256,uint256,address,bytes) (0x022c0d9f) via CALL.transfer(address,uint256) (0xa9059cbb) to 0xa0d511169468ffb4dc986dbb7b58fb7fc190102b via CALL.0x316350633c88caeb464911354f6431dc2948f84d, 0x300270ee7353069b1841a5f6d676e99bbf8b9c38, 0x4e76db7e93f4c5c52e0d6ba88c2490a49a3626a9, 0x141379b7e1d5d272e05e00e58bf88efd1ddbdf3b, 0x14a61089e423c3845c411fa33461fe31f90ea3e5, 0xe1915a78ba553f310189e3071e5407a3c9288664, 0x7c8186a60b7edcb2d6f4e29aab183f20c5267575, 0x9e290f287f64e5fede397b1735b0e4885e5f8927, 0xb758b842f35dc4bf9c231c36a06a1355d3779b8d, and 0xba4d2e4300b227245701d2c5bf93948fb5c30e71.The recovered orchestrator confirms this batching behavior: 0xfc565edbfa06ee13edfa7b8b4677f89ab2382854/recovered.sol hard-codes 11 helper calls, and each helper’s recovered code shows Claim(bool) calling withdraw() and then swapping WEGL to USDT for profit receiver 0xa0d511169468ffb4dc986dbb7b58fb7fc190102b.
The primary victim was the White Eagle withdrawal contract, which lost 9888.680557825521425533 WEGL according to funds_flow.json. The paired PancakeSwap pool absorbed 9188.709810105814000167 WEGL, while 699.970747719707425366 WEGL was burned across the withdrawal and transfer path.
The profit receiver 0xa0d511169468ffb4dc986dbb7b58fb7fc190102b gained 65645.944170470965224333 USDT across the 11 swaps. The largest single payout in the trace was 11719.558694305319350229 USDT, and later payouts declined as pool state changed, which matches the attack pattern of repeatedly converting the same internal USD logic into progressively more WEGL while extracting external USDT liquidity.
Because the payout asset was the protocol’s own WEGL inventory, the exploit directly impaired the solvency of the withdrawal path. The contract’s status field in receipt.json is 0x1, confirming the full drain sequence executed successfully in a single transaction.
receipt.json:1878 shows transaction status 0x1.trace_callTracer.json shows withdraw() (0x3ccfd60b) called 11 times, each followed by a helper swap through PancakeSwap router and pair.selectors.json resolves the key selectors used in the report: claim(), Claim(bool), withdraw(), getAmountsOut(uint256,address[]), swapExactTokensForTokensSupportingFeeOnTransferTokens(uint256,uint256,address[],address,uint256), and swap(uint256,uint256,address,bytes).funds_flow.json records the withdrawal contract’s net loss of 9888.680557825521425533 WEGL and the profit receiver’s gain of 65645.944170470965224333 USDT.receipt.json logs confirm the burn-and-transfer pattern for each withdrawal: WEGL is first burned from the withdrawal contract, then transferred to the helper, then transferred from the helper into the Pancake pair.