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

2019-10-25 约 1065 字 预计阅读 5 分钟

声明:本文 【数字经济CTF-COW区块链题目详解】 由作者 Pinging 于 2019-10-25 09:36:05 首发 先知社区 曾经 浏览数 15 次

感谢 Pinging 的辛苦付出!

一、前言

过了一遍“数字经济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的三个要求了。

关键词:[‘安全技术’, ‘CTF’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now