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

一、前言

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

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

二、题目描述

拿到题目如下所示:

image.png

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

根据题目我们也知道获得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

image.png

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

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

image.png

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

下面看具体的函数:

image.png

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

image.png

此时可以跳出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;。这里我们就很熟悉了。由于没有判断函数,所以这里很明显是个整数溢出。那这个有什么帮助呢?我们可以在后面函数中发现相关的作用。

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

image.png

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

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

下面看第三个函数:

image.png

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

下面第五个函数:

image.png

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

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

image.png
image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容