首发于

Blockchain in CTF

题目逆向分析

Retr_0

这个人太懒了,签名都懒得写一个

本文主要记录整个合约分析的过程。具体代码可以到各大区块链大佬的博客来找。

题目 数字经济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
    }
}

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

寻找payforflag的条件。

从这里我们可以看出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的效果。

发布于2021-09-18 15:52:35
+13赞
0条评论
收藏
内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66