To1in 的BUU刷题记录2

发布于 2021-07-27  48 次阅读






To1in 的BUU刷题记录2

To1in 的BUU刷题记录——2

4.[XNUCA2018]baby_crypto

The 26 letters a, b, c, ..., y, z correspond to the integers 0, 1, 2, ..., 25
len(key_a) = m
len(key_k) = n
c[i] = (p[i] * key_a[i % m] + key_k[i % n]) % 26

p is plain text, only lowercase letters are refered to.
c is encrypted text

I have appended the flag at the end of plain text, the format of which is like 'flagis......'
Now you have the encrypted text, Good luck!

这道题只有一个加密公式以及输出的cipher,分级加密公式发现,加密方式和维吉尼亚密码的加密方式很像,只是多了key_a作为一个系数相乘。所以我们可以参考维吉尼亚的破译方式,对这道题进行分析。

维吉尼亚的破译方式一般是,先通过重合指数分析获得key的长度,再通过枚举以及计算交互重合指数,确定最终的key,最后进行还原。

下面介绍重合指数(IC)以及交互重合指数(MIC)的概念

  • 当计算某个密文的重合指数(又名:重合概率,Index of Coincidence)时,即相当于求在某个密文中随机无放回地抽取其中的两位,这两位的字母相同的概率。值得注意的是,在单表代换的情况下, 明文与密文的IC值是相同的。

    我们已经知道,维吉尼亚密码可以被分解为若干组平移密码来破译,而一个明文足够长的平移密码的重合指数接近0.0687。换言之,如果我们选取某个t值,使得分组后的密文的重合指数接近0.065,则说明选取的t值与密钥的长度是一致的。

  • 假设x和y是两个长度分别为n和n’的字符串,x和y的交互重合指数定义为x中的一个随机元素与y中的一个随机元素相同的概率,记为,假设英文字母在x,y中出现次数分别为,那么交互重合指数就是:

    交互重合指数的性质等同于重合指数的性质。

有了上面的知识,我们就可以开始写脚本了。

import gmpy2

dic_index = {'a': 0.08167, 'b': 0.01492, 'c': 0.02782, 'd': 0.04253, 'e': 0.12702, 'f': 0.02228, 'g': 0.02015,
             'h': 0.06094, 'i': 0.06966, 'j': 0.00153, 'k': 0.00772, 'l': 0.04025, 'm': 0.02406, 'n': 0.06749,
             'o': 0.07507, 'p': 0.01929, 'q': 0.00095, 'r': 0.05987, 's': 0.06327, 't': 0.09056, 'u': 0.02758,
             'v': 0.00978, 'w': 0.02360, 'x': 0.00150, 'y': 0.01974, 'z': 0.00074}
c = open('encrypted_message.txt', 'r').read()
best_index = 0.065  # 英文文本的重合指数
alpha = 'abcdefghijklmnopqrstuvwxyz'


# 重合指数
def Coincidence_index(s):
    frequency = {}
    # 初始化
    for i in alpha:
        frequency[i] = 0
    # 统计字母出现次数
    for i in s:
        frequency[i] += 1
    # 计算重合指数
    index = 0
    for i in alpha:
        index += frequency[i] * (frequency[i] - 1)
    return index / (len(s) * (len(s) - 1))


# 计算key_a key_b的最小公倍数,即周期
def get_t(c):
    t = []
    # 这里我们假设了周期不超过100
    for i in range(2, 100):
        index = 0
        for j in range(i):
            # s是从cipher中取出的长为n*t的子密文段
            s = ''.join(c[j + i * x] for x in range(len(c) // i))
            index += Coincidence_index(s)
        # 最后计算t=i时,平均的重合指数,并与标准的进行对比
        if abs(index / i - best_index) < 0.01:
            t.append(i)
    return t


print(get_t(c))
# [6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]
t = 6


# 下面的部分是爆破key_a key_b
# 计算交互重合指数
def mutual_index_of_coincidence(m):
    frequency = {}
    # 初始化
    for i in alpha:
        frequency[i] = 0
    # 统计字母出现次数
    for i in m:
        frequency[i] += 1
    index = 0
    for i in alpha:
        index += frequency[i] / len(m) * dic_index[i]
    return index


# 获得明文
def decrypt(c, a, b):
    m = ''
    for i in c:
        m += alpha[((alpha.index(i) - b) * gmpy2.invert(a, 26)) % 26]
    return m


# 获得一组密钥
def getkey(c):
    for i in range(26):
        if gmpy2.gcd(i, 26) != 1:
            continue
        for j in range(26):
            m = decrypt(c, i, j)
            index = mutual_index_of_coincidence(m)
            if abs(index - best_index) < 0.01:
                return i, j


# 获得t组密钥
def get_key_in_t(c, t):
    for i in range(t):
        cipher = ''.join(c[i + x * t] for x in range(len(c) // t))
        print(getkey(cipher))


get_key_in_t(c, t)
# (19, 10)
# (7, 9)
# (23, 3)
# (19, 24)
# (7, 14)
# (23, 15)


# 据此我们可以猜测key_a=[19,7,23],key_b=[10,9,3,24,14,15]
key_a = [19, 7, 23]
key_b = [10, 9, 3, 24, 14, 15]
m = ''
a = len(key_a)
b = len(key_b)
for i in c:
    m += alpha[((alpha.index(i) - key_b[c.index(i) % b]) * gmpy2.invert(key_a[c.index(i) % a], 26)) % 26]
print(m)
#reference https://blog.csdn.net/weixin_44110537/article/details/107947158

5.[CISCN2018]sm

from Crypto.Util.number import getPrime,long_to_bytes,bytes_to_long
from Crypto.Cipher import AES
import hashlib
from random import randint
def gen512num():
    order=[]
    while len(order)!=512:
        tmp=randint(1,512)
        if tmp not in order:
            order.append(tmp)
    ps=[]
    for i in range(512):
        p=getPrime(512-order[i]+10)
        pre=bin(p)[2:][0:(512-order[i])]+"1"
        ps.append(int(pre+"0"*(512-len(pre)),2))
    return ps

def run():
    choose=getPrime(512)
    ps=gen512num()
    print "gen over"
    bchoose=bin(choose)[2:]
    r=0
    bchoose = "0"*(512-len(bchoose))+bchoose
    for i in range(512):
        if bchoose[i]=='1':
            r=r^ps[i]
    flag=open("flag","r").read()

    key=long_to_bytes(int(hashlib.md5(long_to_bytes(choose)).hexdigest(),16))
    aes_obj = AES.new(key, AES.MODE_ECB)
    ef=aes_obj.encrypt(flag).encode("base64")

    open("r", "w").write(str(r))
    open("ef","w").write(ef)
    gg=""
    for p in ps:
        gg+=str(p)+"\n"
    open("ps","w").write(gg)

run()

可以看到题目用choose和bchoose生成了给我们的文件,以及aes加密过后的flag,所以这道题需要求bchoose。与其求解有关的是r和ps两个文件,其中r是由ps中一部分数异或生成的,异或是二进制下的加法,所以第25-27行我们可以把他写成矩阵形式:

所以我们只需要两边同乘ps构成的矩阵的逆即可求出bchoose。

ps=open('ps','r').readlines()
c=[]
for i in ps:
    c.append(eval(i.strip()))
A=[]
for i in c:
    A.append([int(x) for x in bin(i)[2:].zfill(512)])
r=eval(open('r','r').readline())
B=[int(x) for x in bin(r)[2:].zfill(512)]
A=matrix(GF(2),A)
B=matrix(GF(2),B)
key=B*A.inverse()
List=[]
for i in key:
   List.append(i)
print('List=',List)
print('key=',key)
#以上在sage中运行
#reference https://blog.csdn.net/weixin_44110537/article/details/107941247
from Crypto.Util.number import getPrime, long_to_bytes, bytes_to_long
from Crypto.Cipher import AES
import hashlib
import base64
key=(1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1)
choose=0
for i in range(512):
    choose=choose<<1
    choose+=key[i]
key = long_to_bytes(int(hashlib.md5(long_to_bytes(choose)).hexdigest(), 16))
aes_obj = AES.new(key, AES.MODE_ECB)
ef=base64.b64decode(open('ef','rb').read().strip())
flag = aes_obj.decrypt(ef)
print(flag)