BtyeCTF 2021 Crypto-easyxor Write up

这篇博客介绍了作者如何分析ByteCTF2021比赛中的Crypto-easyxor题目,通过理解其分组加密和流加密机制,成功破解了前后半段的加密,最终揭示出完整的flag。作者详细展示了OFB和CBC模式下的关键步骤,以及使用Z3库进行爆破和密钥恢复的过程。
摘要由CSDN通过智能技术生成

BtyeCTF 2021 Crypto-easyxor Write up

第一次打字节跳动的比赛,题目质量真的高且难,密码只搞懂了一道题。

题目:

#! /usr/bin/env python
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 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 encrypt(m, k, iv, mode='CBC'):
    assert len(m) % 8 == 0
    num = len(m) // 8
    groups = []
    for i in range(num):
        groups.append(bytes_to_long(m[i * 8: (i + 1) * 8]))
    last = iv
    cipher = []
    if mode == 'CBC':
        for eve in groups:
            cur = eve ^ last
            cur_c = convert(cur, k)
            cipher.append(cur_c)
            last = cur_c
    elif mode == 'OFB':
        for eve in groups:
            cur_c = convert(last, k)
            cipher.append(cur_c ^ eve)
            last = cur_c
    else:
        print 'Not supported now!'
    return ''.join([hex(eve)[2:].strip('L').rjust(16, '0') for eve in cipher])


if __name__ == '__main__':
    from secret import flag
    if len(flag) % 8 != 0:
        flag += '$' * (8 - len(flag) % 8)
    length = len(flag)
    num = length // 8
    keys = [randint(-32, 32) for _ in range(4)]
    IV = getrandbits(64)
    front = flag[:length // 2]
    back = flag[length // 2:]
    cipher1 = encrypt(front, keys, IV, mode='OFB')
    cipher2 = encrypt(back, keys, IV)
    print cipher1 + cipher2

分析题目:

典型的分组密码但是其中掺杂流加密,查看主函数得知明文分两半加密,前半段模式为OFB,后半段为CBC,而且flag长度是8的整数倍,如果原flag不足8的整数倍结尾填充字符’$’。
每半段又分成三组进行对应模式的加密,三组小密文hex之后插入队列中,转字符串之后两端拼接形成完整密文。

前半段:

elif mode == 'OFB':
        for eve in groups:
            cur_c = convert(last, k)
            cipher.append(cur_c ^ eve)
            last = cur_c

flag头为BtyeCTF,所以前半段flag的第一组必定是’BtyeCTF{’,所以bytes_to_long之后与其对应的密文异或后得到cur_c,cur_c又将值赋给last,给第二组加密,这时候就已知last,所以爆破key就好了。

exp(前半段):

from Crypto.Util.number import *
import string
def shift(m, k, c):
    if k < 0:
        return m ^ m >> (-k) & c
    return m ^ m << k & c

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
c1 = [9923871592170792776, 17307588413236231692, 13501143373495638285]
m1 = b'ByteCTF{'
m1 = bytes_to_long(m1)
eve1 = m1 ^ c1[0]
print(eve1)
f = open('result1.txt','w')
ff = open('result2.txt','w')
for key0 in range(-32,33):
   for key1 in range(-32,33):
        for key2 in range(-32, 33):
            for key3 in range(-32, 33):
               key=[key0,key1,key2,key3]
               cur_c1 = convert(eve1,key)
               m = c1[1] ^ cur_c1
               flag = long_to_bytes(m)
               try:
                   if all(c in string.printable for c in flag.decode()):#筛去不可打印字符
                       f.write(flag.decode() + str(key) + '\n')
               except:
                   pass

#创建特殊字符集a,并筛选爆破结果
f.close()
a = ['"','*','~','&','$','\\','|','!','{','}','#','^','?','=','(',')','@','>','<','%','+',':',';','`','/','.']
f = open('out.txt','r')
for i in f.readlines():
    if all(x not in a for x in i):
        ff.write(i+'\n')
ff.close()
f.close()
#筛过后剩余大约500项左右,由于见过的flag都是有一定特征或规律性,不可能是大小写字母与数字混杂的乱码,所以找出一个只含小写字母和数字的串,拿到它对应的key[]
key = [-12,26,-3,-31]
front = b''
for i in c1:
    front+=long_to_bytes(eve1 ^ i)
    eve1 = convert(eve1,key)
print(front)
#ByteCTF{5831a241s-f30980

后半段:

if mode == 'CBC':
        for eve in groups:
            cur = eve ^ last
            cur_c = convert(cur, k)
            cipher.append(cur_c)
            last = cur_c

拿到key了就好办了,用z3库一把梭就好了。

exp(后半段):

from Crypto.Util.number import *
from z3 import *

key = [-12,26,-3,-31]
c2 = [4284382136876262159, 11722938993126464060, 10997124791941970194]

c_list = [0x37386180af9ae39e, 0xaf754e29895ee11a, 0x85e1a429a2b7030c, 0x964c5a89f6d3ae8c]

ivc2=[14682254609762378035]+c2#将前半段中的eve1与c2一起带入求解,可以求得iv和cur
for i in ivc2:
    s=Solver()#创建约束求解器
    m=BitVec('m', 100)#定义一个100比特的变量
    def shift(m, key, c_list):
        for i in range(4):
            if key[i]<0:
                m=m ^ m >> (-key[i]) & c_list[i]
            else:
                m=m ^ m << key[i] & c_list[i]
        return m
    s.add(shift(m,key,c_list)==i)#约束求解条件
    if s.check()==sat:#如果有解
        print(s.model())#输出解,且仅有一组解
iv= 16476971533267772345
last=[16476971533267772345,c2[0],c2[1]]
cur=[10780708739817148043,738617756395427640,10936161096540945944]
back=b''
for i in range(3):
    back+=long_to_bytes(cur[i]^last[i])
print(back)
#q535af-2156547475u2t}

拼接后得到 ByteCTF{5831a241s-f30980q535af-2156547475u2t}。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值