数字经济CTF-COW区块链题目详解
2019-10-25 10:36:05 Author: xz.aliyun.com(查看原文) 阅读量:190 收藏

一、前言

过了一遍“数字经济CTF”的区块链题目,发现题目还可以。在这里将思路以及解题过程做一个总结,希望能够给研究的同学带来一些启发。

比赛包括两道题目,这里先将第一题的分析以及过程做一个总结。

二、题目描述

拿到题目如下所示:

观察后发发现题目没有给传统的基础函数提示,所以我们对合约基本上是一无所知的。所以还是老样子,我们需要逆向合约了。

根据题目我们也知道获得flag的形式还是调用SendFlag函数,传入邮箱获得。

下面让我们具体分析一下题目。

三、解题步骤

扔到decompile中https://ethervm.io/decompile/ropsten/0x0c6a4790e6c8a2Fd195daDee7c16C8E5c532239B#func_func_02FA

得到下面的一些函数:

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]); }

        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;
        var var2 = 0x0de0b6b3a7640000;
        var var3 = msg.value;

        if (!var2) { assert(); }

        var0 = var3 / var2;

        if (var0 >= 0x01) {
            var temp0 = var1 + 0x01;
            storage[temp0] = msg.sender | (storage[temp0] & ~0xffffffffffffffffffffffffffffffffffffffff);
            return;
        } else {
            var1 = 0x05;
            storage[var1] = msg.sender | (storage[var1] & ~0xffffffffffffffffffffffffffffffffffffffff);
            return;
        }
    }

    function func_0654() {
        var var0 = 0x00;
        var var1 = 0x0de0b6b3a7640000;
        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; }

        memory[0x00:0x20] = msg.sender;
        memory[0x20:0x40] = 0x04;
        var temp1 = keccak256(memory[0x00:0x40]);
        storage[temp1] = storage[temp1] - 0xb1b1;
    }

    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) { 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);
    }

    function Cow() {
        var var0 = 0x00;
        var var1 = 0x0de0b6b3a7640000;
        var var2 = msg.value;

        if (!var1) { assert(); }

        var0 = var2 / var1;

        if (var0 != 0x01) { return; }

        storage[0x00] = msg.sender | (storage[0x00] & ~0xffffffffffffffffffffffffffffffffffffffff);
    }
}

其中Unknown的地方是无法解析出名字的。这个也不重要,我们具体来看函数。

为了得到flag,我们当然要看 payforflag(string)

https://ethervm.io/decompile/ropsten/0x0c6a4790e6c8a2Fd195daDee7c16C8E5c532239B#dispatch_6bc344bc

截图中就是最关键的函数。

这里有三个限制,首先是storage[0]、storage[1]、storage[2]均需要等于msg.sender。这真是令人头大,我们稍微查询一下这三个参数都是什么东东。

很明显这个是其他人放进去的地址,目前还不是我们的所以我们需要把它们调整为自己的。

下面看具体的函数:

现在我们翻译一下这个函数的具体含义:首先该函数定义了三个变量,首先var1我们打印出来发现是1 ether。

此时可以跳出if,然后我们得到var0 = var2/1 = var2。(注意这里以ether为单位)

而var2是我们传入的value。我们接着向下看:下面的句子意思是令合约中的storage[user]+var0.

简单来说就类似于让合约中的用户余额+var0 。

之后我们看到if (msg.sender & 0xffff != 0x525b) { return; }。这里是需要我们使用末尾为为525b的账户。例如:

0xxxxxxcF46fA03aFFB24606f402D25A4994b3525b

之后便能进入该函数storage[temp1] = storage[temp1] - 0xb1b1;。这里我们就很熟悉了。由于没有判断函数,所以这里很明显是个整数溢出。那这个有什么帮助呢?我们可以在后面函数中发现相关的作用。

下面我们来看第二个函数:

这个函数比较好理解,简单来说就是用户的合约中余额必须要大于0x0f4240。如果满足了这个时候便会将storage[2]更变为msg.sender。就满足了我们的第一个条件。这里就是为什么我们要通过溢出来达到这个要求,所以我们调用函数1后变能满足这个条件,所以1要在这个函数前执行。

后面storage[0x02] = msg.sender | (storage[0x02] & ~0xffffffffffffffffffffffffffffffffffffffff);我们可以算一算,与完后在或,最后还是msg.sender。

下面看第三个函数:

同样,这里传入var3,然后得到var0 。 并且需要满足>=1。之后temp0就成了1,于是我们就将storage[1]设置为msg.sender。

下面第五个函数:

同样,此时我们需要令var0==1,如何等于1呢?需要我们传入1 ether才可以。最后我们使得storage[0]为msg.sender。

这样我们就能满足调用flag的三个要求了。


文章来源: http://xz.aliyun.com/t/6602
如有侵权请联系:admin#unsafe.sh