ctf
April 30, 2021

Crypto:babyForgery- HMGCTF2021

对于OCB?是啥玩意?xs根本不清楚…hmg这道题直接杀死我了…我是真的菜…噢还过了这么久才开始想要复现一下题目…还得看着dw师傅的BLOG害慢慢学习(…(没耐心审代码做题的习惯得改改了…不然要死…

对于OCB的东西,之前根本就没有见到过…确实挺稀少的 可能和那个啥PCBC差不多是种挺新的东西吧? 我们先来看一张图

ocb

这是OCB的加密模式:

Message Encrypting:

观察这张图 加密需要 N 和 M

Tag Producing :


若header>0,则

Message Decrypting:

解密我们需要 N,M,Tag


若header>0,则

同样与上面相同,若检测出来被改过就会去返回False,否则就返回message了

好了,到这里我们大概已经知道了OCB是怎么样去实现他的加密的。那我们现在来看题目,已经知道的是服务器需要我们去伪造出来我们是Alice的效果,否则就得不到flag .即: associate_data=b’from Alice’ , 并且 message = b’please_give_me_the_flag’

我们去这么想,既然发现了他是每次都是最后一个的加密是不一样的,那么这里是否会有缺点漏洞可以让我们去攻击他呢?但是又该怎么去攻击他呢?并们发现其实我们是可以使用服务器的加密解密功能 得到 的值 并且 是我们给出的值。 但是我们使用不了函数。我们去构造明文

为什么我们要这样去构造,因为这样构造之后,m1不会被当作最后一块加密,那么

我们如果只提交 去解密 但是这回被当作最后一块去解密 那么

神奇的事情发生了 , 但是我们还得要去伪造tag 而此时

而我们现在去提交 去解密,并且当作最后一块解密,( 已知)


而由于我们这里他是aes 128位。因此 之前的 b’\x80’是因为这里的 于是就可以得到

那么我们去解密,只要提交的数据是 是本来的 , , , 空字符串,就能够得到 ,接着我们去逆Time2函数就能够得到 .所以任意 Nounce我们都可以得到 ,那么我们怎么去做到任意加密解密?设 我们可以知道 然后我们加密函数 使用 于是就能算出

我们把所有的过程看作一个函数 用nonce 获得 ,因此我们只需要去在本地计算题目所需要的cipher和tag并且用上原来的一些代码 发送即可

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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
from pwn import*
from hashlib import*
import string
from Crypto.Util.number import*
from gmpy2 import*
import os
import math
strr= string.ascii_letters+string.digits

def times2(input_data,blocksize = 16):
assert len(input_data) == blocksize
output = bytearray(blocksize)
carry = input_data[0] >> 7
for i in range(len(input_data) - 1):
output[i] = ((input_data[i] << 1) | (input_data[i + 1] >> 7)) % 256
output[-1] = ((input_data[-1] << 1) ^ (carry * 0x87)) % 256
assert len(output) == blocksize
return output

def times3(input_data):
assert len(input_data) == 16
output = times2(input_data)
output = xor_block(output, input_data)
assert len(output) == 16
return output

def back_times2(output_data,blocksize = 16):
assert len(output_data) == blocksize
input_data = bytearray(blocksize)
carry = output_data[-1] & 1
for i in range(len(output_data) - 1,0,-1):
input_data[i] = (output_data[i] >> 1) | ((output_data[i-1] % 2) << 7)
input_data[0] = (carry << 7) | (output_data[0] >> 1)
# print(carry)
if(carry):
input_data[-1] = ((output_data[-1] ^ (carry * 0x87)) >> 1) | ((output_data[-2] % 2) << 7)
assert len(input_data) == blocksize
return input_data

def xor_block(input1, input2):
assert len(input1) == len(input2)
output = bytearray()
for i in range(len(input1)):
output.append(input1[i] ^ input2[i])
return output

def hex_to_bytes(input):
return bytearray(long_to_bytes(int(input,16)))

def pow():
p.recvuntil("XXXX+")
str1 = str(p.recvuntil(")")[:-1],encoding='utf-8')
p . recvuntil("= ")
sha = p.recvuntil("\n")[:-1]
for i in strr:
for j in strr:
for k in strr:
for m in strr:
s = sha256((i+j+k+m+str1).encode()).hexdigest()
if s == str(sha,encoding='utf-8'):
p.recvuntil("XXXX:")
p.sendline(i+j+k+m)
return 1

def giveusername():
p.recvuntil("> ")
p.sendline('014')

def get_encrypt(msg):
nonce = bytearray(os.urandom(16))#get nonce
p.recvuntil("Enter option > ")
p.sendline ("1")
p.recvuntil("Enter nonce > ")
p.sendline (nonce.hex())
p.recvuntil("Enter message > ")
fake_m = bytearray(b'\x00'*15+b'\x80'+b'\x00'*16)
p.sendline(fake_m.hex())
p.recvuntil("ciphertext: ")
ciphertext = p.recvuntil("\n")[:-1]
p.recvuntil("tag: ")
tag = p.recvuntil("\n")[:-1]#get ciphertext and tag
p.recvuntil("Enter option > ")
p.sendline ("2")
p.recvuntil("Enter nonce > ")
p.sendline (nonce.hex())
p.recvuntil("Enter ciphertext > ")
m0 = bytearray(b'\x00'*15+b'\x80')
m1 = bytearray(b'\x00'*16)
c0 = hex_to_bytes(ciphertext[:32])
p.sendline (xor_block(c0,m0).hex())
p.recvuntil("tag > ")
c1 = ciphertext[32:]
p.sendline (c1)
p.recvuntil("data > ")
p.sendline ("")
p.recvuntil("message: ")
enc = xor_block(bytearray(hex_to_bytes(p.recvuntil("\n")[:-1])),m0)
A = back_times2(enc)
B = enc
C = xor_block(B,c0)
msg = bytearray(msg)
p.recvuntil("option > ")
p.sendline ("1")
p.recvuntil("nonce > ")
p.sendline (xor_block(B,m0).hex())
p.recvuntil("message > ")
p.sendline (xor_block(msg,times2(C)).hex()+m1.hex())
p.recvuntil("ciphertext: ")
enc = bytearray(hex_to_bytes(p.recvuntil("\n")[:-1])[:16])
p.recvline()
return xor_block(enc,times2(C))

def my_pmac(header, blocksize = 16):
assert len(header)
m = int(max(1, math.ceil(len(header) / float(blocksize))))
offset = get_encrypt(bytearray([0] * blocksize))
offset = times3(offset)
offset = times3(offset)
checksum = bytearray(blocksize)
for i in range(m - 1):
offset = times2(offset)
H_i = header[(i * blocksize):(i * blocksize) + blocksize]
assert len(H_i) == blocksize
xoffset = xor_block(H_i, offset)
encrypted = get_encrypt(xoffset)
checksum = xor_block(checksum, encrypted)
offset = times2(offset)
H_m = header[((m - 1) * blocksize):]
assert len(H_m) <= blocksize
if len(H_m) == blocksize:
offset = times3(offset)
checksum = xor_block(checksum, H_m)
else:
H_m.append(int('10000000', 2))
while len(H_m) < blocksize:
H_m.append(0)
assert len(H_m) == blocksize
checksum = xor_block(checksum, H_m)
offset = times3(offset)
offset = times3(offset)
final_xor = xor_block(offset, checksum)
auth = get_encrypt(final_xor)
return auth

def my_ocb_encrypt(plaintext, header, nonce, blocksize = 16):
assert nonce
m = int(max(1, math.ceil(len(plaintext) / float(blocksize))))
offset = get_encrypt(nonce)
checksum = bytearray(blocksize)
ciphertext = bytearray()
for i in range(m - 1):
offset = times2(offset)
M_i = plaintext[(i * blocksize):(i * blocksize) + blocksize]
assert len(M_i) == blocksize
checksum = xor_block(checksum, M_i)
xoffset = get_encrypt(xor_block(M_i, offset))
ciphertext += xor_block(offset, xoffset)
assert len(ciphertext) % blocksize == 0
M_m = plaintext[((m - 1) * blocksize):]
offset = times2(offset)
bitlength = len(M_m) * 8
assert bitlength <= blocksize * 8
tmp = bytearray(blocksize)
tmp[-1] = bitlength
pad = get_encrypt(xor_block(tmp, offset))
tmp = bytearray()
C_m = xor_block(M_m, pad[:len(M_m)])
ciphertext += C_m
tmp = M_m + pad[len(M_m):]
assert len(tmp) == blocksize
checksum = xor_block(tmp, checksum)
offset = times3(offset)
tag = get_encrypt(xor_block(checksum, offset))
if len(header) > 0:
tag = xor_block(tag, my_pmac(header))
return (tag, ciphertext)

def last_io():
p.recvuntil("option > ")
p.sendline("3")
p.recvuntil("nonce > ")
p.sendline(lastnonce.hex())
p.recvuntil("ciphertext > ")
p.sendline(lastcipher)
p.recvuntil("tag > ")
p.sendline(lasttag)
flag = p.recvuntil("}")
print(flag)
p.close()

if __name__ == "__main__":
p = remote("127.0.0.1",10007)
pow()
giveusername()
lastnonce = bytearray(hex_to_bytes('55'*16))
lasttag,lastcipher = (my_ocb_encrypt(bytearray(b'please_give_me_the_flag'),bytearray(b'from Alice'),lastnonce))
lasttag = lasttag.hex()
lastcipher = lastcipher.hex()
last_io()

About this Post

This post is written by deebato, licensed under CC BY-NC 4.0.

#ctf#cryptography#ocb