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, getrandbitsdef shift(m, k, c):if k < 0:return m ^ m >> (-k) & creturn m ^ m << k & cdef unshift(value, k, mask, bits=64):if(k == 0):return value ^ value & masktmp = valueif k < 0:for _ in range(bits // (-k)):tmp = value ^ tmp >> (-k) & maskelse:for _ in range(bits // k):tmp = value ^ tmp << k & maskassert shift(tmp, k, mask) == valuereturn tmpdef convert(m, key):c_list = [0x37386180af9ae39e, 0xaf754e29895ee11a, 0x85e1a429a2b7030c, 0x964c5a89f6d3ae8c]for t in range(4):m = shift(m, key[t], c_list[t])return mdef unconvert(m, key):tmp = mc_list = [0x37386180af9ae39e, 0xaf754e29895ee11a, 0x85e1a429a2b7030c, 0x964c5a89f6d3ae8c]for t in range(3,-1,-1):m = unshift(m, key[t], c_list[t])assert convert(m, key) == tmpreturn mdef 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 = lastcur = unconvert(cur_c, k)groups.append(cur ^ eve)last = evegroups = groups[::-1]elif mode == 'OFB':last = ivfor eve in cipher:cur_c = convert(last, k)groups.append(cur_c ^ eve)last = cur_celse:print('Not supported now!')m = b''for i in groups:m += long_to_bytes(i)assert len(m) % 8 == 0return mflag = 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:continuekey = 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 hashlibPOST = '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 1else:return 0prefix = 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 plaindef 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
閱讀理解題,先從協議二構造 Qc=hc?QsQ_c = h_c \cdot Q_sQc?=hc??Qs? ,此時返回的兩個點滿足 Ys?hs==YcY_s \cdot h_s == Y_cYs??hs?==Yc? ,所以直接爆破 πs\pi_sπs? 即可
再打協議一,令 Tc=?PcT_c=-P_cTc?=?Pc? ,此時 Kcs=ds?TcK_cs = d_s \cdot T_cKc?s=ds??Tc? ,直接解密即可
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 requestsdef 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).textdef 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_ch_c = H(pi_C)Q_c = h_c * R_csid2 = getrandbits(256)sid2 = hex(sid2)[2:].zfill(64)msg21 = sid2msg22 = talk(21,msg21)Q_s = msg22msg23 = str(Q_c) + Q_smsg24 = (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_ssk2 = 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)returndef get_pis():sid2 = getrandbits(256)sid2 = hex(sid2)[2:].zfill(64)msg21 = sid2msg22 = talk(21,msg21)Q_s = Point(int(msg22[:64],16), int(msg22[64:],16))h_c = H(pi_C)Q_c = h_c * Q_smsg23 = 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問題
依次提交ga,ga+1,ga+2...g^{a},g^{a+1},g^{a+2}...ga,ga+1,ga+2... 可得gab,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...
構造如下格子即可得出
總結
以上是生活随笔為你收集整理的ByteCTF 2021(Crypto部分)的全部內容,希望文章能夠幫你解決所遇到的問題。