ByteCTF 2021(Crypto部分)

easyxor

核心加密算法convert就是四个shift,写个逆就行,flag被分为两部分,前半部分用OFB模式加密,后半部分用CBC模式加密,由于CBC模式的缺陷,当我们只考虑最后一块时,将倒数第二块作为iv,凭借flag的格式做判断即可爆破出key,再利用OFB模式的漏洞,结合key与已知的flag格式即可逆推出iv,此时iv和key尽知,直接解密即可

from Crypto.Util.number import bytes_to_long, long_to_bytes
from random import randint, getrandbits

def shift(m, k, c):
    if k < 0:
        return m ^ m >> (-k) & c
    return m ^ m << k & c

def unshift(value, k, mask, bits=64):
    if(k == 0):
        return value ^ value & mask
    tmp = value
    if k < 0:
        for _ in range(bits // (-k)):
            tmp = value ^ tmp >> (-k) & mask
    else:
        for _ in range(bits // k):
            tmp = value ^ tmp << k & mask
    assert shift(tmp, k, mask) == value
    return tmp

def convert(m, key):
    c_list = [0x37386180af9ae39e, 0xaf754e29895ee11a, 0x85e1a429a2b7030c, 0x964c5a89f6d3ae8c]
    for t in range(4):
        m = shift(m, key[t], c_list[t])
    return m

def unconvert(m, key):
    tmp = m
    c_list = [0x37386180af9ae39e, 0xaf754e29895ee11a, 0x85e1a429a2b7030c, 0x964c5a89f6d3ae8c]
    for t in range(3,-1,-1):
        m = unshift(m, key[t], c_list[t])
    assert convert(m, key) == tmp
    return m

def decrypt(c, k, iv, mode='CBC'):
    cipher = []
    for i in range(0,len(c),16):
        cipher.append(int(c[i:i+16],16))
    groups = []
    if mode == 'CBC':
        cipher = cipher[::-1]
        last = cipher[0]
        for eve in (cipher[1:] + [iv]):
            cur_c = last
            cur = unconvert(cur_c, k)
            groups.append(cur ^ eve)
            last = eve
        groups = groups[::-1]
    elif mode == 'OFB':
        last = iv
        for eve in cipher:
            cur_c = convert(last, k)
            groups.append(cur_c ^ eve)
            last = cur_c
    else:
        print('Not supported now!')
    m = b''
    for i in groups:
        m += long_to_bytes(i)
    assert len(m) % 8 == 0
    return m

flag = b'ByteCTF{'
padding = bytes_to_long(flag)
cipher = '89b8aca257ee2748f030e7f6599cbe0cbb5db25db6d3990d3b752eda9689e30fa2b03ee748e0da3c989da2bba657b912'
cipher1, cipher2 = cipher[:len(cipher) // 2], cipher[len(cipher) // 2:]
c_list = []
for i in range(0,len(cipher2),16):
    c_list.append(int(cipher2[i:i+16],16))

def get_key():
    for a in range(-32,33):
        for b in range(-32,33):
            for c in range(-32,33):
                for d in range(-32,33):
                    try:
                        plain = decrypt(cipher2[-16:],[a,b,c,d],c_list[-2])
                        if(plain.endswith(b'$$$') and plain.strip(b'$').endswith(b'}')):
                            print(a,b,c,d,plain)
                            return [a,b,c,d]
                    except:
                        continue

key = get_key()
# key = [-12, 26, -3, -31]
# print(cipher1)
c_list = []
for i in range(0,len(cipher1),16):
    c_list.append(int(cipher1[i:i+16],16))
E = (padding ^ c_list[0])
iv = unconvert(E, key)
# print(iv)
flag = b''
flag += (decrypt(cipher1, key, iv, mode='OFB'))
flag += (decrypt(cipher2, key, iv))

print(flag.strip(b'$'))

# ByteCTF{5831a241s-f30980q535af-2156547475u2t}

JustDecrypt

AES_CFB模式,只提供解密,并且使用了不安全的unpad函数,虽然iv不知道,但是iv并不会影响后续的解密,所以我们可以通过提交一个固定块来保证iv不变,但是由于有unpad的存在,我们需要知道我们解密后的结果,就需要一个至少大于256长度的尾来保证我们想要的东西不会被删掉,这里我们称为padding,所以我们先给服务器打两个过去,一来检测一下是否可用,二来调整一下服务器的iv值

接下来我们需要伪造目标字符串,根据每次的返回值算出该位密位应该是多少,逐位修改一下即可,最后计算一下padding最后一位的数值,将后面多余的部分利用unpad删掉即可


from Crypto.Util.number import bytes_to_long, long_to_bytes
from pwn import *
import hashlib

POST = '39.105.181.182'
HOST = 30001
r = remote(POST,HOST)
# context.log_level = 'debug'

table = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'
def passpow():
    rev = r.recvuntil("sha256(XXXX+")
    suffix = r.recv(28).decode()
    r.recvuntil(" == ")
    res = r.recv(64).decode()
    def f(x):
        hashresult = hashlib.sha256((x+suffix).encode()).hexdigest()
        if hashresult == res:
            return 1
        else:
            return 0
    prefix = util.iters.mbruteforce(f,table,4,'upto')
    r.recvuntil("XXXX > ")
    r.sendline(str(prefix))

def talk(msg):
    r.recvuntil("hex > ")
    r.sendline(msg)
    r.recvuntil("hex: \n")
    plain = r.recvline(False)
    # print(plain)
    plain = long_to_bytes(int(plain.decode(),16))
    return plain

def xor(a,b):
    assert len(a) == len(b)
    return bytes([i ^ j for (i,j) in zip(a,b)])

aim = b"Hello, I'm a Bytedancer. Please give me the flag!"
passpow()
padding = b''
for i in range(256):
    padding += hex(i)[2:].zfill(2).encode()
# print(padding)
# print(cipher)
plain = talk(padding*2)
# print(plain)
# plain = talk(padding)
# print(plain)
if len(plain) <= 32:
    r.close()
    exit()
# plain = talk(b'\x00'*32 + padding)[:32]
payload = bytearray(b'\x00' * 16 * 4)
plain = plain[256:256+32]
# print(plain)

for i in range(len(aim)):
    payload[i] = aim[i] ^ plain[i]
    # print(payload.hex().encode())
    plain = talk(payload.hex().encode() + padding)
    # print(plain)

padnum = len(payload.hex().encode()) + len(payload) - len(plain)
padnum //= 2
# print(padnum)
length = len(plain) - len(aim)
length = length // 16 
length *= 16
padding = long_to_bytes(int((padding[length*2:]).decode(),16))
payload = payload + padding
plain = talk(payload.hex())
# print(plain)
length = len(payload) - len(plain)
payload[-1] = payload[-1] ^ length ^ (len(payload) - len(aim))
plain = talk(payload.hex())
print(plain)

r.recvlines(2)
flag = r.recvline(False).strip()
print(flag)
r.close()
# r.interactive()

abusedkey

阅读理解题,先从协议二构造 Q c = h c ⋅ Q s Q_c = h_c \cdot Q_s Qc=hcQs ,此时返回的两个点满足 Y s ⋅ h s = = Y c Y_s \cdot h_s == Y_c Yshs==Yc ,所以直接爆破 π s \pi_s πs 即可

再打协议一,令 T c = − P c T_c=-P_c Tc=Pc ,此时 K c s = d s ⋅ T c K_cs = d_s \cdot T_c Kcs=dsTc ,直接解密即可


from Crypto.Util.number import long_to_bytes
from random import getrandbits, randint
from Crypto.Cipher import AES
from hashlib import sha256
from tqdm import tqdm
from curve import *
import requests

def talk(mode, msg):
    host = 'http://39.105.181.182:30000'
    if mode == 11:
        url = host + '/abusedkey/server/msg11'
    elif mode == 13:
        url = host + '/abusedkey/server/msg13'
    elif mode == 21:
        url = host + '/abusedkey/server/msg21'
    elif mode == 23:
        url = host + '/abusedkey/ttp/msg23'
    elif mode == 25:
        url = host + '/abusedkey/server/msg25'
    return requests.get(url,data=msg).text

def H(msg):
    return int(sha256(msg).hexdigest(), 16)

pi_C = b'\xFF\xFF'
Curve = curve()

def get_hint():
    r_c = randint(0,Curve.p)
    R_c = Curve.G * r_c
    h_c = H(pi_C)
    Q_c = h_c * R_c
    sid2 = getrandbits(256)
    sid2 = hex(sid2)[2:].zfill(64)
    msg21 = sid2
    msg22 = talk(21,msg21)
    Q_s = msg22
    msg23 = str(Q_c) + Q_s
    msg24 = (talk(23,msg23))
    # print(msg24)
    # print(len(msg24))
    # print(len(msg23))
    Y_c, Y_s = msg24[:128], msg24[128:]
    # print(Y_c, Y_s)
    Y_c, Y_s = Point(int(Y_c[:64],16), int(Y_c[64:],16)), Point(int(Y_s[:64],16), int(Y_s[64:],16))
    msg25 = sid2 + str(Y_c)
    msg26 = talk(25,msg25)
    print(msg26)
    msg26 = long_to_bytes(int(msg26,16))
    # print(msg26)
    iv, cipher, tag = msg26[:12], msg26[12:-16], msg26[-16:]
    Z_cs = r_c * Y_s
    sk2 = long_to_bytes(H(long_to_bytes(Z_cs.x)))
    aes = AES.new(sk2, AES.MODE_GCM, iv)
    hint = aes.decrypt_and_verify(cipher, tag)
    print(hint)
    return

def get_pis():
    sid2 = getrandbits(256)
    sid2 = hex(sid2)[2:].zfill(64)
    msg21 = sid2
    msg22 = talk(21,msg21)
    Q_s = Point(int(msg22[:64],16), int(msg22[64:],16))
    h_c = H(pi_C)
    Q_c = h_c * Q_s
    msg23 = str(Q_c) + str(Q_s)
    msg24 = talk(23,msg23)
    # print(msg24)
    Y_c, Y_s = msg24[:128], msg24[128:]
    Y_c, Y_s = Point(int(Y_c[:64],16), int(Y_c[64:],16)), Point(int(Y_s[:64],16), int(Y_s[64:],16))
    # print(Y_c)
    # print(Y_s)
    for i in tqdm(range(0xffff + 1)):
        h_s = H(b'\x00' * (2 - len(long_to_bytes(i))) + long_to_bytes(i))
        if(Y_s * h_s == Y_c):
            # print(i)
            return long_to_bytes(i)

pi_s = get_pis()
# pi_s = long_to_bytes(36727)
h_s = H(pi_s)

# check pi_s
sid2 = getrandbits(256)
sid2 = hex(sid2)[2:].zfill(64)
msg21 = sid2
msg22 = talk(21,msg21)
Q_s = Point(int(msg22[:64],16), int(msg22[64:],16))
h_c = H(pi_C)
Q_c = h_c * Curve.G
Q_s = h_s * Curve.G
msg23 = str(Q_c) + str(Q_s)
msg24 = talk(23,msg23)
# print(msg24)
Y_c, Y_s = msg24[:128], msg24[128:]
Y_c, Y_s = Point(int(Y_c[:64],16), int(Y_c[64:],16)), Point(int(Y_s[:64],16), int(Y_s[64:],16))
assert(Y_c == Y_s)

# solve
d_s = H(pi_s)
P_cx = int('b5b1b07d251b299844d968be56284ef32dffd0baa6a0353baf10c90298dfd117', 16)
P_cy = int('ea62978d102a76c3d6747e283091ac5f2b4c3ba5fc7a906fe023ee3bc61b50fe', 16)
P_c = Point(P_cx, P_cy)
T_c = Point(P_cx, -P_cy % Curve.p)
sid1 = getrandbits(256)
sid1 = hex(sid1)[2:].zfill(64)
msg11 = sid1
msg12 = talk(11,msg11)
msg13 = sid1 + str(T_c)
msg14 = talk(13,msg13)
# print(msg14)
msg14 = long_to_bytes(int(msg14,16))
iv, cipher, tag = msg14[:12], msg14[12:-16], msg14[-16:]
K_cs = d_s * T_c
sk1 = long_to_bytes(H(long_to_bytes(K_cs.x)))
# print(sk1, iv, cipher, tag)
cip = AES.new(sk1, AES.MODE_GCM, iv)
plaintext = cip.decrypt_and_verify(cipher, tag)
print(plaintext)

Overheard

HNP问题

依次提交 g a , g a + 1 , g a + 2 . . . g^{a},g^{a+1},g^{a+2}... ga,ga+1,ga+2... 可得 g a b , g ( a + 1 ) b , g ( a + 2 ) b . . . g^{ab},g^{(a+1)b},g^{(a+2)b}... gab,g(a+1)b,g(a+2)b...

构造如下格子即可得出
在这里插入图片描述

from pwn import *
from random import*
p = 62606792596600834911820789765744078048692259104005438531455193685836606544743
g = 5
sh=remote("39.105.38.192","30000")
print(sh.recv().decode())
sh.sendline(b"1")
a=int(sh.recvuntil(b"\n",drop=True).decode())
print(sh.recv())
sh.sendline(b"2")
sh.recvuntil(b"$ ",drop=True)
b=int(sh.recvuntil(b"\n",drop=True).decode())

sh.sendline(b"3")
sh.sendline(str(a).encode())
sh.recvuntil(b"Bob: ")
r=(int(sh.recvuntil(b"\n").decode()))
sh.recvuntil(b"exit\n")

cnt=0
B=[]
A=[]
for i in range(1,15):
    sh.sendline(b"3")
    sh.sendline(str(a*pow(g,i,p)%p).encode())
    sh.recvuntil(b"Bob: ")
    B.append(int(sh.recvuntil(b"\n").decode()))
    A.append(pow(b,i,p))
    sh.recvuntil(b"exit\n")
M=[]
s=16
k=1
for i in range(s):
    l=[0]*s
    l[i]=p*k
    if(i==s-2):
        for j in range(s-2):
            l[j]=A[j]*k
    if(i==s-1):
        for j in range(s-2):
            l[j]=B[j]*k
    M.append(l)
M[-2][-2]=1
M[-1][-1]=2**60
m=M.copy()
m[-2][-2]=1/2^189
m=matrix(QQ,m)
print(m.LLL()[0][-1]//m[-1][-1]==1)
print(m.LLL()[0][-2]*2^189)
print(r)
sh.sendline(b'4')
sh.sendline(str(abs(m.LLL()[0][-2]*2^189)%p).encode())
print(sh.recvall())
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值