Fork me on GitHub
pikachu's Blog

华为云安全场区块链 ethenc

前言

  • 华为云安全场区块链题目
  • 其实很简单,和区块链关系不大

Source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
pragma solidity ^0.6.12;

contract EthEnc {
address private owner; //0

uint private key; //1

uint private delta; //2

uint public output; //3

uint32 private sum;
uint224 private tmp_sum=0; //4

uint32 private key0;
uint224 private t0=0; //5
uint32 private key1;
uint224 private t1=0; //6
uint32 private key2;
uint224 private t2=0; //7
uint32 private key3;
uint224 private t3=0; //8
uint randomNumber = 0;
string private s;

event OhSendFlag(address addr);

constructor() public payable {
// key = 0x74686974 5f69735e 5f746573 746b6579
key = 0x746869745f69735e5f746573746b6579;
delta = 0xb3c6ef3720;
}

modifier auth {
require(msg.sender == owner || msg.sender == address(this), "EthEnc: not authorized");
_;
}

function payforflag() public auth {
require(output == 2282910687825444608285583946662268071674116917685196567156);
emit OhSendFlag(msg.sender);
selfdestruct(msg.sender);
}

function Convert(string memory source) internal pure returns (uint result) {
bytes32 tmp;
assembly {
tmp := mload(add(source, 32))
}
result = uint(tmp) / 0x10000000000000000;
}

// 正 0x 5f5f6f68 5f66616e 74616e73 69746963 5f626162 795f5f5f __oh_fantansitic_baby___

function set_s(string memory _s) public {
s = _s;
}

function Encrypt() public {
uint tmp = Convert(s);
assembly {
let first,second
sstore(5, and(shr(96, sload(1)), 0xffffffff))
sstore(6, and(shr(64, sload(1)), 0xffffffff))
sstore(7, and(shr(32, sload(1)), 0xffffffff))
sstore(8, and(sload(1), 0xffffffff))

let step := 1
for { let i := 1 } lt(i, 4) { i := add(i, 1) } {

first := and(shr(mul(add(sub(24, mul(i, 8)), 4), 8), tmp), 0xffffffff)
second := and(shr(mul(sub(24, mul(i, 8)), 8), tmp), 0xffffffff)

sstore(4, 0)

for {let j := 0 } lt(j, 32) { j := add(j, 1) } {

let tmp11,tmp12
let tmp21,tmp22

tmp11 := and(add(xor(and(mul(second, 16), 0xffffffff), and(div(second, 32), 0xffffffff)), second), 0xffffffff)
switch and(and(sload(4),0xffffffff), 3)
case 0 {
tmp12 := and(add(and(sload(4),0xffffffff), and(sload(5),0xffffffff)), 0xffffffff)
}
case 1 {
tmp12 := and(add(and(sload(4),0xffffffff), and(sload(6),0xffffffff)), 0xffffffff)
}
case 2 {
tmp12 := and(add(and(sload(4),0xffffffff), and(sload(7),0xffffffff)), 0xffffffff)
}
default {
tmp12 := and(add(and(sload(4),0xffffffff), and(sload(8),0xffffffff)), 0xffffffff)
}
first := and(add(first, xor(tmp11, tmp12)), 0xffffffff)

sstore(4, and(add(and(sload(4), 0xffffffff), shr(5, sload(2))), 0xffffffff))

tmp21 := and(add(xor(and(mul(first, 16), 0xffffffff), and(div(first, 32), 0xffffffff)), first), 0xffffffff)
switch and(and(shr(11, and(sload(4),0xffffffff)), 0xffffffff), 3)
case 0 {
tmp22 := and(add(and(sload(4),0xffffffff), and(sload(5),0xffffffff)), 0xffffffff)
}
case 1 {
tmp22 := and(add(and(sload(4),0xffffffff), and(sload(6),0xffffffff)), 0xffffffff)
}
case 2 {
tmp22 := and(add(and(sload(4),0xffffffff), and(sload(7),0xffffffff)), 0xffffffff)
}
default {
tmp22 := and(add(and(sload(4),0xffffffff), and(sload(8),0xffffffff)), 0xffffffff)
}
second := and(add(second, xor(tmp21, tmp22)), 0xffffffff)

}

sstore(3, add(sload(3), add(shl(sub(192, mul(step, 32)), first), shl(sub(192, mul(i, 64)), second))))
step := add(step, 2)
}
}
}

receive() external payable {
if(msg.value == 0) {
this.payforflag();
} else {
this.Encrypt();
}
}
}

Analyse

  • 题目需要逆向,上面是源码
  • 考点有三个:
    • 考察对 evm 逆向能力
    • 考察 xtea 加解密
    • 考察 fallbackmsg.sender 的理解

Exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <stdint.h>
#include <string.h>

/* take 64 bits of data in v[0] and v[1] and 128 bits of key[0] - key[3] */

void encipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], sum=0, delta=0x9E3779B9;
for (i=0; i < num_rounds; i++) {
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
sum += delta;
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
}
v[0]=v0; v[1]=v1;
}

void decipher(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4]) {
unsigned int i;
uint32_t v0=v[0], v1=v[1], delta=0x9E3779B9, sum=delta*num_rounds;
for (i=0; i < num_rounds; i++) {
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]);
sum -= delta;
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
}
v[0]=v0; v[1]=v1;
}

int main()
{
unsigned int i;
uint32_t const k[4]={0x74686974,0x5f69735e,0x5f746573,0x746b6579};
unsigned int r=32;

// 2282910687825444608285583946662268071674116917685196567156 = 0x5d1ab31f6a103c8f364d33e96dbdd5cdbd40d15e55c23274
uint32_t enc1[2] = {0x5d1ab31f, 0x6a103c8f};
uint32_t enc2[2] = {0x364d33e9, 0x6dbdd5cd};
uint32_t enc3[2] = {0xbd40d15e, 0x55c23274};
char flag[50];
decipher(r, enc1, k);
decipher(r, enc2, k);
decipher(r, enc3, k);
printf("解密后的数据:%x %x %x %x %x %x\n",enc1[0],enc1[1],enc2[0],enc2[1],enc3[0],enc3[1]);

// 0x5f5f6f685f66616e74616e73697469635f626162795f5f5f
int s[24] = {0x5f, 0x5f, 0x6f, 0x68, 0x5f, 0x66, 0x61, 0x6e, 0x74, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x63, 0x5f, 0x62, 0x61, 0x62, 0x79, 0x5f, 0x5f, 0x5f};
for (i=0; i<24; i++) {
printf("%c", s[i]);
}

return 0;
}
  • 上面的 exp 可以计算出 s
  • 然后调用 set_s(s),调用 Encrypt()
  • 最后给合约转账金额 0,触发 receive 函数调用 payforflag 即可
---------------- The End ----------------
谢谢大爷~

Author:pikachu
Link:https://hitcxy.com/2020/ethenc/
Contact:hitcxy.cn@gmail.com
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布
转载请注明出处,谢谢!