0x6e799758cee75dae3d84e09d40dc416ecf713652Ethereum0x16296859c15289731521f199f0a5f762df6347d0Ethereum0xd20c245e1224fc2e8652a283a8f5cae1d83b353aEthereumAn unprivileged attacker created a Pendle-valid market around an attacker-controlled SY path, registered that market into Penpie through Penpie's public register helper, and then harvested the fake pool so Penpie treated real LP assets it already held as rewards for the attacker's pool. The failure was not a privileged key compromise or a forged attacker artifact. The core bug was Penpie trusting any Pendle-valid market at admission time and then trusting that market's reward-token list and reward redemption outputs during harvest. That trust let the attacker push unrelated Penpie-held assets into a newly created rewarder they controlled through pool ownership.
Penpie integrates Pendle markets by registering each market inside PendleStaking, creating a receipt token and rewarder, and then exposing the pool through MasterPenpie. At the exploit block, PendleStaking.registerPool() was callable by the configured pendleMarketRegisterHelper, and the configured helper exposed a public registerPenpiePool(address) entrypoint gated only by Pendle factory isValidMarket.
Pendle market validity was therefore not sufficient to establish that a market was safe for Penpie. A malicious market could still control the semantics of its reward tokens and reward redemption flow. Penpie's rewarder implementation also accepted new reward tokens dynamically from its authorized rewardQueuer, which meant a harvested pool could start tracking arbitrary assets if PendleStaking queued them.
The vulnerability class is an admission-control and trust-boundary failure in an ACT exploit path. First, Penpie exposed a public registration helper that admitted any market satisfying Pendle's predicate. Second, Penpie's harvest path trusted the registered market's externally supplied reward token list and reward redemption outputs as if they were legitimate incentives for that pool. Third, the rewarder created for a newly registered pool accepted arbitrary new reward tokens from , so once a malicious market caused Penpie to queue a valuable token, that token became claimable by the fake pool's staker. The exploit therefore needed no privileged sender and no attacker-side replay artifacts. It only required the attacker to create a Pendle-valid market whose reward path returned assets already custodied by Penpie.
isValidMarketPendleStakingAt block 20671878, Penpie's PendleStaking proxy at 0x6e799758cee75dae3d84e09d40dc416ecf713652 used implementation 0xff51c6b493c1e4df4e491865352353eadff0f9f8. That implementation gated registerPool() with _onlyPoolRegisterHelper, not an owner-only check. The configured helper was proxy 0xd20c245e1224fc2e8652a283a8f5cae1d83b353a, and its implementation exposed:
function registerPenpiePool(address _market) external {
_registerMarket(_market, 0);
}
function _registerMarket(address _market, uint256 _allocPoints) internal onlyVerifiedMarket(_market) {
(, IPPrincipalToken PT, ) = IPendleMarket(_market).readTokens();
string memory name = string(abi.encodePacked(PT.symbol(), "-PRT"));
IPendleStaking(pendleStaking).registerPool(_market, _allocPoints, name, name);
}
The only admission test was Pendle factory isValidMarket(_market). There was no victim-side validation of the SY implementation behind the market, no allowlist of acceptable reward tokens, and no check that the market's reward redemption path only surfaced legitimate incentives for that pool.
Once registration succeeded, MasterPenpie.createRewarder() created a rewarder whose constructor trusted PendleStaking as a reward queuer. BaseRewardPoolV2.queueNewRewards() then accepted any previously unseen token by appending it to rewardTokens and provisioning rewards for it.
In the first seed transaction, 0x7e7f9548f301d3dd863eac94e6190cb742ab6aa9d7730549ff743bf84cbd21d1, metadata shows attacker EOA 0x7a2f4d625fb21f5e51562ce8dc2e722e12a61d1b called helper contract 0x4476b6ca46b28182944ed750e74e2bb1752f87ae. The trace then shows that helper calling the public Penpie register helper at 0xd20c245e..., which in turn forwarded the attacker-created market into Penpie's pool registration flow. That is the violating admission step.
In the second seed transaction, 0x42b2ec27c732100dd9037c76da415e10329ea41598de453bb0c0c9ea7ce0d8e5, the harvest path realized the theft. The trace shows Penpie approving attacker rewarder 0xF89140E3CEAe2d8c5dA63fd120fD0be845edEB1B, calling queueNewRewards() with real Pendle LP market tokens 0x6010676bc2534652ad1ef5fa8073dcf9ad7ebfbe and 0x038c1b03dab3b891afbca4371ec807edaa3e6eb6, and then multiclaim() transferring those assets to attacker helper 0x4476.... The tx2 balance diffs independently confirm PenpieStaking lost 416513404704868480076 units of the first LP token and 458670606639349413222 units of the second LP token while the attacker helper gained the corresponding balances.
The exploit was executed in two adversary-crafted Ethereum mainnet transactions.
In tx 0x7e7f9548f301d3dd863eac94e6190cb742ab6aa9d7730549ff743bf84cbd21d1, the attacker used helper contract 0x4476... to create a Pendle-valid market around an attacker-controlled SY path, call the public registerPenpiePool(address) function on 0xd20c245e..., create the Penpie rewarder for the fake pool, and deposit fake LP into that pool. This turned the attacker-created market into a live Penpie pool without any privileged approval.
In tx 0x42b2ec27c732100dd9037c76da415e10329ea41598de453bb0c0c9ea7ce0d8e5, the attacker harvested the fake market through Penpie's normal public harvesting flow. Because the malicious market returned real Penpie-held LP tokens as reward tokens, Penpie queued those LP tokens into the fake pool's rewarder. The attacker then immediately called multiclaim() for the fake pool and withdrew the queued LP balances back to the attacker helper.
The directly evidenced asset loss was borne by Penpie's staking contract, which held the real LP balances before harvest and lost them after the malicious reward flow executed.
PENDLE-LPT at 0x6010676bc2534652ad1ef5fa8073dcf9ad7ebfbe: 416513404704868480076 raw units, 18 decimalsPENDLE-LPT at 0x038c1b03dab3b891afbca4371ec807edaa3e6eb6: 458670606639349413222 raw units, 18 decimalsThe impact is broader than a single queueing bug: Penpie's trust model allowed attacker-controlled market semantics to redefine what counted as pool rewards, converting protocol-custodied assets into claimable rewards for a fake pool.
0x7e7f9548f301d3dd863eac94e6190cb742ab6aa9d7730549ff743bf84cbd21d1 on Ethereum mainnet0x42b2ec27c732100dd9037c76da415e10329ea41598de453bb0c0c9ea7ce0d8e5 on Ethereum mainnetPendleStaking proxy 0x6e799758cee75dae3d84e09d40dc416ecf713652MasterPenpie proxy 0x16296859c15289731521f199f0a5f762df6347d0PendleMarketRegisterHelper proxy 0xd20c245e1224fc2e8652a283a8f5cae1d83b353aPendleStaking, MasterPenpie, PendleMarketRegisterHelper, and BaseRewardPoolV2