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

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

ocb

这是 OCB 的加密模式:

Message Encrypting:

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

Δ=EK(N);Δ=2Δ=2EK(N\Delta=E_K(N);\Delta=2\Delta=2E_K(N)

C1=21EK(N)EK(M121EK(N))C_1=2^1E_K(N)\oplus E_K(M_1\oplus2^1E_K(N))

......

Cn1=2n1EK(N)EK(Mn12n1EK(N)C_{n-1}=2^{n-1}E_K(N)\oplus E_K(M_{n-1}\oplus2^{n-1}E_K(N)

Δ=2nEK(N)\Delta=2^n E_K(N)

Cn=EK(length(Mn)2nEK(N))MnC_n=E_K(length(M_n)\oplus2^nE_K(N))\oplus M_n

Tag Producing :

Δ=32nEK(N)\Delta=3*2^nE_K(N)

checksum=i=1nmichecksum=\oplus^n_{i=1}m_i

Tag=AuthEK(32nEK(N)Checksum)Tag=Auth\oplus E_K(3*2^nE_K(N)\oplus Checksum)

若 header>0, 则 Tag=TagheaderTag=Tag\oplus header

Message Decrypting:

解密我们需要 N,M,Tag

m1=D(c121EK(N))21EK(N)m_1=D(c_1\oplus2^1E_K(N))\oplus2^1E_K(N)

......

mn1=D(cn12n1EK(N))2n1EK(N)m_{n-1}=D(c_{n-1}\oplus 2^{n-1}E_K(N))\oplus2^{n-1}E_K(N)

mn=cnEK(2nEK(N)(length(mn)))m_n=c_n\oplus E_K(2^nE_K(N)\oplus(length(m_n)))

checksum=i=1nmichecksum=\oplus^n_{i=1}m_i

checktag=EK(32nEK(N)checksum)checktag=E_K(3*2^nE_K(N)\oplus checksum)

若 header>0, 则

checktag=checktagheaderchecktag=checktag\oplus header

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

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

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

m_1=len(0^n)=15×b'\x00'+b'\x80'\ \\ m_2=16×b'\x00'

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

c1=2EK(N)EK(2EK(N)m1)c_1=2E_K(N)\oplus E_K(2E_K(N)\oplus m_1)

c2=m2EK(4EK(N)length(m2))=EK(4EK(N)length(m2))c_2=m_2\oplus E_K(4E_K(N)\oplus length(m_2))=E_K(4E_K(N)\oplus length(m_2))

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

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')))

m1=2EK(N)m_1=2E_K(N)

神奇的事情发生了 ,EK(N)=m1//2E_K(N)=m_1//2 但是我们还得要去伪造 tag 而此时

checktag=EK(6EK(N)m1)=EK(6EK(N)2EK(N))=EK(4EK(N))checktag=E_K(6E_K(N)\oplus m_1)=E_K(6E_K(N)\oplus 2E_K(N))=E_K(4E_K(N))

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

m_1=c_1\oplus M_1\oplus E_K(2E_K(N)\oplus((n-1)*0+b'\x80'))

m1=M12EK(N)m_1=M_1\oplus 2E_K(N)

checktag=EK(6EK(N)M12EK(N))=EK(4EK(N)M1)checktag=E_K(6E_K(N)\oplus M_1\oplus2E_K(N))=E_K(4E_K(N)\oplus M_1)

而由于我们这里他是 aes 128 位。因此 之前的 b'\x80' 是因为这里的 M1=128M_1=128 于是就可以得到 checktag=c2checktag=c_2

那么我们去解密,只要提交的数据是 nouncenounce 是本来的 nouncenounce ,tagtagc2c_2 , c=c1c=c_1 ,headerheader 空字符串,就能够得到 2EK(N)2E_K(N) , 接着我们去逆 Time2 函数就能够得到 EK(N)E_K(N) . 所以任意 Nounce 我们都可以得到 EK(N)E_K(N) ,那么我们怎么去做到任意加密解密?设 A=EK(N),B=EK(2Am1)A=E_K(N),B=E_K(2A\oplus m_1) 我们可以知道B=c12AB=c_1\oplus2A 然后我们加密函数 使用 nounce=2BEK(message2B2B)nounce =2B\oplus E_K(message\oplus 2B\oplus 2B) 于是就能算出

EK(message)=c2BE_K(message)=c\oplus 2B

我们把所有的过程看作一个函数 用 nonce 获得EK(message)E_K(message) , 因此我们只需要去在本地计算题目所需要的 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()

请我喝[茶]~( ̄▽ ̄)~*

De3B4to 微信支付

微信支付

De3B4to 支付宝

支付宝