Summary
On May 26, 2026 at 17:47:44 UTC, the attacker 0x83b9e7edc5b3127e4853a4f4945b92aa88eef0c8 exploited the SKP token contract 0xecbdc0b76142740bb564b8aa1bcd061cb151a666 and extracted value from the SKP/USDT pair 0x47c8c3b123de467892ac7df6dfcf7ca3db901733.
The attacker assembled 204,950,260.192547 USDT from Moolah, Venus, and eight Pancake V3 flash sources, used that capital to almost completely drain SKP from the pair, then sold 61,699.359005 SKP back through Pancake’s fee-on-transfer router path. During that sell, the execution trace shows SKP calling the pair’s sync() from inside the token transfer flow before the router completed the follow-up pair swap. That reserve refresh locked in a pair state of 205,184,395.144168 USDT against only 0.000000004 SKP, and the next swap paid out 205,184,395.144151 USDT for just 49,359.487204 net SKP delivered to the pair.
All borrowed liquidity was repaid in the same transaction. The attacker finished with 162,854.210641 USDT and 74.877563 BNB, worth about $212,195 at the transaction’s own exit rate.
Exploit path
The transaction started when the attacker funded the executor 0xe924853dcdfcb89292335042ab10d68c7315d7c1 with 66,767.034100 SKP and used a pair callback to enter the main execution path.
Inside the first callback chain, the executor borrowed 1,676.742751 BTCB from Moolah, posted it into Venus as vBTC collateral, and borrowed 87,985,709.380996 USDT. A second callback chain added another 14,519,650.142120 USDT from Moolah plus 102,445,900.669431 USDT from eight Pancake V3 flash pools, bringing the immediately usable USDT stack to 204,950,260.192547.
That USDT was then routed into the SKP/USDT pair. The first router-driven swap removed 21,549,429.242094 SKP from the pair, and the pair’s post-swap reserve event showed 205,184,395.144168 USDT against 24,679.743602 SKP. A second SKP transfer from the pair immediately reduced the pair’s recorded SKP reserve to 0.000000004 while leaving the USDT side unchanged at 205,184,395.144168.
The decisive leg came next. The executor approved the Pancake router and called swapExactTokensForTokensSupportingFeeOnTransferTokens with 61,699.359005 SKP. On-chain transfer events show that nominal SKP amount was split into:
- 3,084.967950 SKP sent to the burn address
- 9,254.903851 SKP sent to
0x163dc1bb5ca273ac659fc80065b620d43a6ac7c4 - 49,359.487204 SKP sent to the SKP/USDT pair
Within that same transferFrom path, the trace shows SKP querying the pair reserves, reading the pair’s token and USDT balance, and then calling sync(). The pair emitted a Sync event with reserves still fixed at 205,184,395.144168 USDT and 0.000000004 SKP. Immediately afterward, the router called the pair’s swap, and the pair transferred 205,184,395.144151 USDT back to the executor.
The executor then unwound every temporary borrow: it repaid all eight Pancake V3 flash pools, repaid the Moolah USDT flash loan, repaid the Venus USDT borrow, redeemed the BTCB collateral, and returned the original BTCB flash loan. Finally it swapped 50,000 USDT for 75.877563 WBNB/BNB, sent 1 BNB to a third address, and transferred the remaining 162,854.210641 USDT plus 74.877563 BNB back to the external attacker address.
Root cause
The primary issue was a logic error in SKP’s custom transfer-side market logic, with the AMM pricing effect expressed through reserve manipulation.
The execution trace proves three facts that are sufficient for the root-cause claim:
- the attacker sold SKP through Pancake’s fee-on-transfer router path,
- SKP itself called the pair’s
sync()during that token transfer flow, - the pair’s
sync()snapshot preserved a nearly empty SKP reserve immediately before the profitable USDT-paying swap.
The recovered SKP token code is only medium-confidence, so it should be used as supporting context rather than as authoritative source. That recovered code does show a non-standard _transfer path that updates balances, then conditionally enters _runSpecialPairFlow, where the token queries pair state and may call sync() for the hardcoded SKP/USDT pair. That matches the trace-level behavior. What remains unresolved is the exact predicate inside _shouldTriggerSync(...): the recovered artifact does not fully recover the arithmetic and branching that decide when sync fires, so the report should not overclaim the exact condition.
The pair itself behaved like a standard Pancake-style AMM. sync() copied current token balances into stored reserves, and the later swap priced against those stored reserves. Once SKP’s custom transfer logic refreshed reserves at the wrong moment in the attacker-controlled sell sequence, the pair priced the final trade against a state that showed 205,184,395.144168 USDT backing only 0.000000004 SKP. That let 49,359.487204 net SKP pull out essentially the entire USDT side of the pool.
This was not a Venus or Moolah insolvency event. Those venues only supplied temporary leverage and were fully repaid inside the same transaction. The irreversible loss occurred in the SKP token and the SKP/USDT liquidity pool.
Vulnerable code
// [recovered — approximation]
function _transfer(address from, address to, uint256 amount) internal {
slot_0x00_balances[from] = fromBalance - amount;
slot_0x00_balances[to] += amount;
emit Transfer(from, to, amount);
if (slot_0x0f_lockFlag == 0) {
bool touchesConfiguredMarket =
from == slot_0x09_specialAddress ||
to == slot_0x09_specialAddress ||
from == PANCAKE_PAIR ||
to == PANCAKE_PAIR;
bool flaggedParticipant = slot_0x0b_flags[from] || slot_0x0b_flags[to];
if (touchesConfiguredMarket || flaggedParticipant) {
_runSpecialPairFlow(from, to, amount); // <-- VULNERABILITY: transfer-side market hook can alter pair reserves
}
}
}
// [recovered — approximation]
function _runSpecialPairFlow(address from, address to, uint256 amount) internal {
(uint112 reserve0, uint112 reserve1, ) = IPairLike(PANCAKE_PAIR).getReserves();
address token0 = IPairLike(PANCAKE_PAIR).token0();
address token1 = IPairLike(PANCAKE_PAIR).token1();
uint256 stableBalance = IERC20Like(USDT).balanceOf(PANCAKE_PAIR);
bool shouldSync = _shouldTriggerSync(
from, to, amount, reserve0, reserve1, token0, token1, stableBalance
);
if (shouldSync) {
IPairLike(PANCAKE_PAIR).sync(); // <-- VULNERABILITY: token contract can force reserve refresh during its own transfer flow
slot_0x0d_lastSyncTimestamp = block.timestamp;
}
}
The recovered artifact supports two careful conclusions and no more: SKP embeds a hardcoded dependency on the SKP/USDT pair, and its transfer path can conditionally call sync() on that pair. The exact predicate inside _shouldTriggerSync(...) remains only partially recovered, so the trace is the authoritative evidence for when the hook actually fired in this exploit.
Impact
The attacker realized 162,854.210641 USDT and 74.877563 BNB. Using the transaction’s own 50,000 USDT to 75.877563 WBNB exit swap as the clearing rate, the BNB leg was worth about 49,341.04 USDT, for a total realized profit of approximately $212,195.