0x7fdd140f7631f62d62f7256ee4a38af51a4723ad5d66adc9b9685bf78f750f2d0xefb4e3cc438ef2854727a7df0d0baf844484edabBlast0xdc4a9779d6084c1ab3e815b67ed5e6780ccf4d90BlastAt Blast block 4619717, an unprivileged account exploited Bazaar's custom LBP vault to exit pool 0xdc4a9779d6084c1ab3e815b67ed5e6780ccf4d90000200000000000000000001 on behalf of Bazaar's pool owner 0xb66585c4e460d49154d50325ce60adc44bc900e9. The exploit burned 850000000000000000000000000 BPT from the owner and extracted 392368916281212373207 wei of WETH-equivalent value plus 880539680680963671973966099 rYOLO. The root cause was a missing authorization check in BazaarVaultBlast.exitPool: the vault trusted the caller-supplied sender argument and forwarded it into Balancer pool burn logic without requiring that the caller be that sender or an approved delegate.
Bazaar did not use Balancer's canonical Vault for this LBP. Instead, it deployed a custom BazaarVault / BazaarVaultBlast wrapper that exposes join, exit, and swap paths. In Balancer-style LBPs, the sender passed into onExitPool is the account whose BPT is burned. That design is safe only when the wrapper vault preserves Balancer's authorization assumptions.
The affected pool owner was the Bazaar-managed address 0xb66585c4e460d49154d50325ce60adc44bc900e9. Collector artifacts show this address was created through Bazaar's createLBP(tuple cfg) flow and later used for pool configuration. Before the exploit, the owner held at least the incident BPT amount and the vault held the corresponding WETH and rYOLO reserves required to satisfy the exit.
The vulnerability is an authorization failure on a privileged withdrawal path. BazaarVault.exitPool is externally callable, accepts arbitrary sender and recipient parameters, and forwards them directly into lbp.onExitPool(...). Unlike Balancer's canonical vault flow, Bazaar's wrapper does not bind sender to msg.sender, does not check relayer approval, and does not offer a signature-based authorization path. In the underlying Balancer pool code, LegacyBasePool.onExitPool burns BPT from the supplied sender by calling _burnPoolTokens(sender, bptAmountIn). That means the first code-level violation occurs as soon as Bazaar's vault forwards an attacker-chosen victim owner address into onExitPool. Once that happens, the pool burns the victim's BPT and the vault transfers the withdrawn assets to the attacker-controlled recipient. The invariant violated is: only the BPT holder or an explicitly authorized actor may cause that holder's BPT to be burned and the corresponding pool reserves to be released.
The verified Bazaar vault implementation shows the missing authorization gate directly:
function exitPool(bytes32 poolId, address sender, address payable recipient, ExitPoolRequest memory request)
external
override
nonReentrant
registeredPool(poolId)
{
(address[] memory tokens, uint256[] memory balances,) = getPoolTokens(poolId);
IBazaarLBP lbp = IBazaarLBP(_getPoolAddress(poolId));
(uint256[] memory amountsOut,) = lbp.onExitPool(poolId, sender, recipient, balances, 0, 0, request.userData);
...
_sendFunds(request.tokens[i], recipient, amountOut);
}
In the underlying Balancer LBP code, the supplied sender is the address whose BPT is burned:
function onExitPool(..., address sender, address recipient, ..., bytes memory userData)
public
virtual
override
onlyVault(poolId)
returns (uint256[] memory, uint256[] memory)
{
(uint256 bptAmountIn, uint256[] memory amountsOut, uint256[] memory dueProtocolFeeAmounts) = _onExitPool(...);
_burnPoolTokens(sender, bptAmountIn);
return (amountsOut, dueProtocolFeeAmounts);
}
function _burnPoolTokens(address sender, uint256 amount) internal {
_burn(sender, amount);
}
The exploit transaction trace shows the attacker-created contract called:
0xefb4...EdaB::exitPool(
0xdc4a...0001,
0xb66585c4e460d49154d50325ce60adc44bc900e9,
0xd31c7a22f4e6f928f1d4adabbc08c7bf88a3e402,
...
)
The corresponding receipt confirms the semantic effect. The pool emitted a BPT burn from the victim owner to the zero address for 850000000000000000000000000, the vault transferred 392368916281212373207 WETH-equivalent value and 880539680680963671973966099 rYOLO to the attacker contract, and the attacker path then withdrew and forwarded the ETH. Because the pool id, token ordering, and exit encoding are all publicly reconstructible from on-chain state and verified source, no privileged information was required.
The adversary flow is fully on-chain and fits the ACT model:
0x3cf5b87726af770c94494e886d2a69c42a203884 funded itself with 0.1 ETH in transaction 0xe69466cf8b2e0e10c442948d8dbf22b63c00476ea571902301d7ab813eaadb0b.0x7fdd140f7631f62d62f7256ee4a38af51a4723ad5d66adc9b9685bf78f750f2d, that EOA deployed transient contract 0xd31c7a22f4e6f928f1d4adabbc08c7bf88a3e402.BazaarVaultBlast.exitPool while supplying victim owner 0xb66585c4e460d49154d50325ce60adc44bc900e9 as sender and itself as recipient.392368916281212373207 wei to 0xfb72b2b32740de44488db791d83125ce39a53d91.The decision point that matters is the caller-controlled sender parameter. Once Bazaar's vault accepted that forged sender value, the remaining Balancer exit logic behaved as designed, but against the wrong account.
The exploit drained the LBP owner's position and the pool reserves corresponding to the burned BPT. The directly measured losses were:
ETH: 392368916281212373207 wei (392.368916281212373207 ETH)rYOLO: 880539680680963671973966099 raw units (880539680.680963671973966099 rYOLO)The collector balance diff additionally shows the attacker cluster's native balance increased by 392368916281212373207 wei while the sender EOA only paid 6256257206526 wei of gas, matching the reported net gain.
0x7fdd140f7631f62d62f7256ee4a38af51a4723ad5d66adc9b9685bf78f750f2d on Blast (81457)BazaarVaultBlast at 0xefb4e3cc438ef2854727a7df0d0baf844484edabBazaarLBPBlast at 0xdc4a9779d6084c1ab3e815b67ed5e6780ccf4d900xb66585c4e460d49154d50325ce60adc44bc900e9/workspace/session/artifacts/collector/