对于 OCB?是啥玩意?xs 根本不清楚...hmg 这道题直接杀死我了... 我是真的菜... 噢还过了这么久才开始想要复现一下题目... 还得看着 dw 师傅的 BLOG害慢慢学习(...(没耐心审代码做题的习惯得改改了...不然要死...
对于 OCB 的东西,之前根本就没有见到过... 确实挺稀少的 可能和那个啥 PCBC 差不多是种挺新的东西吧? 我们先来看一张图
这是 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'
我们去这么想,既然发现了他是每次都是最后一个的加密是不一样的,那么这里是否会有缺点漏洞可以让我们去攻击他呢?但是又该怎么去攻击他呢?并们发现其实我们是可以使用服务器的加密解密功能 得到 的值 并且 是我们给出的值。 但是我们使用不了 函数。我们去构造明文
m_1=len(0^n)=15×b'\x00'+b'\x80'\ \\ m_2=16×b'\x00'为什么我们要这样去构造,因为这样构造之后,m1 不会被当作最后一块加密,那么
我们如果只提交 去解密 但是这回被当作最后一块去解密 那么
m_1=c_1\oplus E_K(2E_K(N)\oplus ((n-1)*0+b'\x80')))=2E_K(N)\oplus E_K(2E_K(N)\oplus ((n-1)*0+b'\x80'))\oplus E_K(2E_K(N)\oplus ((n-1)*0+b'\x80')))神奇的事情发生了 , 但是我们还得要去伪造 tag 而此时
而我们现在去提交 去解密,并且当作最后一块解密,( 已知)
m_1=c_1\oplus M_1\oplus E_K(2E_K(N)\oplus((n-1)*0+b'\x80'))而由于我们这里他是 aes 128 位。因此 之前的 b'\x80' 是因为这里的 于是就可以得到
那么我们去解密,只要提交的数据是 是本来的 , 用 , , 空字符串,就能够得到 , 接着我们去逆 Time2 函数就能够得到 . 所以任意 Nounce 我们都可以得到 ,那么我们怎么去做到任意加密解密?设 我们可以知道 然后我们加密函数 使用 于是就能算出
我们把所有的过程看作一个函数 用 nonce 获得 , 因此我们只需要去在本地计算题目所需要的 cipher 和 tag 并且用上原来的一些代码 发送即可
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() |