Keep Rising Public Sell LP Drain
Exploit Transactions
0x2abf871eb91d03bc8145bf2a415e79132a103ae9f2b5bbf18b8342ea9207ccd70xbafe05fd604e03a52b8606671e765f9a46cd39ac0ed1f59f0ec7562b87c719bc0xc446c67ceae627e45711773778bfdfc6d2aaffad482feded78e4ba1efe505337Victim Addresses
0x15b1ed79ca9d7955af3e169d7b323c4f1eeb5d12BSC0xad1e7bf0a469b7b912d2b9d766d0c93291ca2656BSCLoss Breakdown
Similar Incidents
EEECOIN Public Helper LP Drain
37%SOF Sell-Hook Reserve Manipulation Drains PancakeSwap V2 USDT Liquidity
35%CS Pair Balance Burn Drain
34%NeverFallToken LP Drain
34%SafeMoon LP Burn Drain
34%STO Pending-Sell Burn Reserve Manipulation
34%Root Cause Analysis
Keep Rising Public Sell LP Drain
1. Incident Overview TL;DR
On BNB Smart Chain, Keep Rising token contract 0x15b1ed79ca9d7955af3e169d7b323c4f1eeb5d12 exposed a public sellKr(uint256) path that let any caller redeem protocol-owned KR/BSC-USD liquidity without first surrendering KR. An attacker EOA, 0x835b45d38cbdccf99e609436ff38e31ac05bc502, called sellKr(uint256) three times in transactions 0x2abf871eb91d03bc8145bf2a415e79132a103ae9f2b5bbf18b8342ea9207ccd7, 0xbafe05fd604e03a52b8606671e765f9a46cd39ac0ed1f59f0ec7562b87c719bc, and 0xc446c67ceae627e45711773778bfdfc6d2aaffad482feded78e4ba1efe505337.
Those three public sells drained the KR/BSC-USD pair 0xad1e7bf0a469b7b912d2b9d766d0c93291ca2656 from 19052868446490986344141 wei of BSC-USD at block 33267984 to 5809121824168935973 wei at block 33268082. The attacker started with zero KR and ended with 16190000425966794796941 wei of BSC-USD, while two owner-linked fee wallets received another 2857058898700022611224 wei from the same LP unwind. The root cause is a direct accounting failure in sellKr(uint256): the function prices a sale from protocol LP and pays out BSC-USD, but never debits msg.sender's KR balance or any seller-specific claim.
2. Key Background
Keep Rising is an unverified token contract, but on-chain introspection shows name() = "Keep Rising", symbol() = "KR", and owner() = 0x1c558aee3ed18ec142d3dc2ee46bd9ec11d5641f. Runtime selector extraction resolves public functions including buyKr(uint256), sellKr(uint256), setUSDTLimit(address,uint256), and withdrawToken(address,address,uint256), which is consistent with a token that manages its own liquidity and owner-directed payout controls.
The intended acquisition path is visible in transaction 0xff7bbf45eb8ebc30125b301a8e22b4d01d827918a86a03b8f1ad5d3602cc2adb. In that transaction, buyer 0xc9cf57d58e18acac09c2aba77e46e61dc04aefe7 transferred 1 BSC-USD to the Keep Rising contract and received 1361976271170172532677 KR from the KR/BSC-USD pair. That transaction establishes the expected model: users are supposed to acquire KR first, then later sell it.
The later sell behavior breaks that model. The same buyer successfully called sellKr(uint256) in transactions 0x9b8fcc635a0cb0b7f133d237c96808d711ab7a09f9d4c1c57f3a5770a7462d58 and 0x14b9e48c6d215a48ce98af9e0099c122a36f4264fbf391c954271797f2902dcc, yet historical balance checks show that the buyer still held exactly 1361976271170172532677 KR at blocks 33261926, 33264271, 33264272, and 33264285. The sell path therefore did not burn, escrow, or otherwise debit the seller's KR even for a non-attacker user.
3. Vulnerability Analysis & Root Cause Summary
This incident is an ATTACK, not a pure MEV arbitrage. The broken invariant is straightforward: a KR sale must reduce the seller's KR balance, or a seller-specific redeemable claim, before BSC-USD leaves protocol-controlled liquidity. Keep Rising violates that invariant inside sellKr(uint256).
Independent bytecode analysis and execution evidence agree on the mechanism. Selector 0x4fa4b9f0 resolves to sellKr(uint256), and the victim runtime dispatches that selector into the code block beginning at bytecode offset 0x0b31. From there, the function queries KR balance in the pair, pair totalSupply(), and BSC-USD balance in the pair, calculates how much LP to redeem, and reaches helper 0x2561. That helper emits external selector 0xbaa2abde, which resolves to PancakeRouter removeLiquidity(address,address,uint256,uint256,uint256,address,uint256).
The critical failure is what does not happen. Neither the seed trace nor any of the successful sell receipts contains a transferFrom(msg.sender, ...) call on KR, a KR Transfer event from the seller, or a reduction in seller KR balances. Instead, the contract spends LP that it already owns, receives underlying assets back from the pair, and forwards BSC-USD to the caller plus fee wallets. Because sellKr(uint256) trusts a caller-supplied amount without debiting the caller, any unprivileged EOA could repeat the drain until the pair was nearly empty.
4. Detailed Root Cause Analysis
4.1 Victim Bytecode Confirms the Sell Path
The victim is unverified, but its runtime bytecode is sufficient to confirm the public control flow. The dispatcher maps selector 0x4fa4b9f0 to the sell routine, and the sell routine branches into an internal helper that prepares the router call:
Victim runtime disassembly
000001c6: PUSH4 0x4fa4b9f0
000001cb: EQ
000001cc: PUSH2 0x0377
000001cf: JUMPI
...
00000386: PUSH2 0x0b31
...
00000b31: JUMPDEST
...
00000d5e: PUSH2 0x2561
...
00002561: JUMPDEST
...
000025b0: PUSH4 0xbaa2abde
Selector resolution gives:
0x4fa4b9f0 -> sellKr(uint256)
0xbaa2abde -> removeLiquidity(address,address,uint256,uint256,uint256,address,uint256)
This is the code-level breakpoint reported in the analysis: the sell path reaches an internal router helper that removes liquidity held by the Keep Rising contract itself.
4.2 The Seed Trace Shows LP Redemption, Not Seller Payment
The first attacker transaction, 0x2abf871eb91d03bc8145bf2a415e79132a103ae9f2b5bbf18b8342ea9207ccd7, makes the exploit sequence explicit:
Seed execution trace for tx 0x2abf871e...
0x15b1Ed79...::sellKr(101562500000000007791968256)
0x15b1Ed79...::balanceOf(0xAD1e7BF0...)
0xAD1e7BF0...::totalSupply()
BEP20USDT::balanceOf(0x15b1Ed79...)
0x10ED43C7...::removeLiquidity(
0x15b1Ed79..., 0x55d39832..., 1346433352214122139990595,
91406250000000007012771430, 0, 0x15b1Ed79..., 1699309388
)
0xAD1e7BF0...::transferFrom(
0x15b1Ed79..., 0xAD1e7BF0..., 1346433352214122139990595
)
The contract is redeeming its own LP position from the pair. The LP transferFrom is from the Keep Rising contract into the pair, not from the attacker into the token contract or pair.
The receipt from the same transaction then shows the BSC-USD payout flowing out to the attacker:
Receipt transfers for tx 0x2abf871e...
BSC-USD Transfer:
from 0x15b1Ed79cA9D7955AF3E169d7B323c4F1eeb5D12
to 0x835b45d38cbdccf99e609436ff38e31ac05bc502
value 15174477688361091148525
BSC-USD Transfer:
from 0x15b1Ed79cA9D7955AF3E169d7B323c4F1eeb5D12
to 0xa11c1c398b3b5c5718ed9a8a56f65625d612d7f6
value 894401567278694901813
BSC-USD Transfer:
from 0x15b1Ed79cA9D7955AF3E169d7B323c4F1eeb5D12
to 0x44a07c78c9c515dc05fae48bc85ab6a5c1b12fb3
value 1783447436549732947926
No KR transfer from the attacker appears anywhere in the trace or receipt.
4.3 Historical Balances Prove KR Was Never Required
Historical RPC reads show that the attacker never held KR across the exploit blocks:
- Block
33267984: attacker KR balance =0 - Block
33267985: attacker KR balance =0 - Block
33268026: attacker KR balance =0 - Block
33268027: attacker KR balance =0 - Block
33268081: attacker KR balance =0 - Block
33268082: attacker KR balance =0
The sell path also fails for a legitimate holder in the same way. Buyer 0xc9cf57d58e18acac09c2aba77e46e61dc04aefe7 bought KR in tx 0xff7bbf45..., then successfully sold in tx 0x9b8fcc63... and tx 0x14b9e48c..., but still held 1361976271170172532677 KR before and after those sells. The two sell receipts show BSC-USD payouts to the buyer and fee wallets, yet no KR transfer out of the buyer.
That evidence rules out alternative explanations such as hidden approvals or off-path seller settlement. The only inventory being consumed is protocol-owned LP.
4.4 ACT Conditions and Exhaustion Boundary
The ACT opportunity existed as long as four conditions held:
- The Keep Rising contract still held KR/BSC-USD LP in pair
0xad1e7bf0a469b7b912d2b9d766d0c93291ca2656. - The pair still held meaningful BSC-USD liquidity.
sellKr(uint256)was callable and the reentrancy guard was clear.- The adversary could submit a plain public transaction to
sellKr(uint256).
The pre-state at block 33267984 satisfied those conditions. The Keep Rising contract held 1436936643050352943616636 pair LP, and the pair held 19052868446490986344141 wei of BSC-USD.
The exploit remained permissionless until the reserve was nearly exhausted. A follow-up transaction, 0x53a50e8e94f8436acc1f3afa40641c4900626ba438168d1d3043893dc154b938, called sellKr(uint256) again at block 33268083 and reverted with ds-math-sub-underflow. That failure happened only after the first three public sells had already drained the pair down to 5809121824168935973 wei of BSC-USD.
5. Adversary Flow Analysis
The adversary flow is unusually simple because no helper contract or privileged setup was required.
-
In tx
0x2abf871eb91d03bc8145bf2a415e79132a103ae9f2b5bbf18b8342ea9207ccd7, EOA0x835b45d38cbdccf99e609436ff38e31ac05bc502calledsellKr(uint256)directly with selector0x4fa4b9f0. The contract redeemed1346433352214122139990595LP from its own pair position and paid15174477688361091148525wei of BSC-USD to the attacker. -
In tx
0xbafe05fd604e03a52b8606671e765f9a46cd39ac0ed1f59f0ec7562b87c719bc, the same EOA repeated the same public entrypoint without any new approvals or funding. The call paid another875450635866985892052wei of BSC-USD to the attacker and further reduced the protocol LP position. -
In tx
0xc446c67ceae627e45711773778bfdfc6d2aaffad482feded78e4ba1efe505337, the attacker executed a third public sell and received140072101738717756364wei of BSC-USD. After that call, the pair was effectively empty for practical purposes.
Across the three transactions, the attacker paid gas on 694955 units at 3 gwei and ended with 16190000425966794796941 wei of BSC-USD. The attacker's KR balance remained zero the entire time, so the exploit sequence is fully aligned with the ACT model: an unprivileged EOA realized profit using only public calldata, on-chain state, and a permissionless contract call.
6. Impact & Losses
The measurable loss is the BSC-USD removed from the KR/BSC-USD pool:
- Pair BSC-USD reserve before exploit block:
19052868446490986344141 - Pair BSC-USD reserve after third sell block:
5809121824168935973 - Total BSC-USD depleted from the pair:
19047059324666817408168
The value split was:
- Attacker direct gain:
16190000425966794796941wei of BSC-USD - Owner-linked fee wallets:
2857058898700022611224wei of BSC-USD
The pair also lost a large portion of the LP held by the Keep Rising contract itself, because sellKr(uint256) redeemed protocol-owned LP on every successful call. Operationally, the exploit destroyed the backing liquidity that legitimate KR holders depended on and left the pair with only dust-level BSC-USD liquidity.
7. References
- Victim token: Keep Rising
0x15b1ed79ca9d7955af3e169d7b323c4f1eeb5d12 - Pair: KR/BSC-USD Pancake pair
0xad1e7bf0a469b7b912d2b9d766d0c93291ca2656 - Quote token: BSC-USD
0x55d398326f99059ff775485246999027b3197955 - Legitimate buy transaction:
0xff7bbf45eb8ebc30125b301a8e22b4d01d827918a86a03b8f1ad5d3602cc2adb - Legitimate sell transactions:
0x9b8fcc635a0cb0b7f133d237c96808d711ab7a09f9d4c1c57f3a5770a7462d58,0x14b9e48c6d215a48ce98af9e0099c122a36f4264fbf391c954271797f2902dcc - Successful adversary sells:
0x2abf871eb91d03bc8145bf2a415e79132a103ae9f2b5bbf18b8342ea9207ccd7,0xbafe05fd604e03a52b8606671e765f9a46cd39ac0ed1f59f0ec7562b87c719bc,0xc446c67ceae627e45711773778bfdfc6d2aaffad482feded78e4ba1efe505337 - Failed post-drain copycat sell:
0x53a50e8e94f8436acc1f3afa40641c4900626ba438168d1d3043893dc154b938 - Public function selectors used in validation:
0x4fa4b9f0 = sellKr(uint256),0xbaa2abde = removeLiquidity(address,address,uint256,uint256,uint256,address,uint256)