本文主要记录整个合约分析的过程。具体代码可以到各大区块链大佬的博客来找。
题目
数字经济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的效果。