2022网鼎杯青龙组wp

本次网鼎杯的Crypto部分其实还是比较简单的,解出的人数也较多。感觉405题略难一些,花了比较多的时间,恰巧最后几小时给出的162所用到的求解方式之前遇到过,有幸作为一个CTF新手把这次的比赛密码学部分AK掉,值得纪念!

2022网鼎杯青龙组wp@fgps

crypto091

搜索问题描述里面USENIX Security 2021的论文得知,苹果使用的是sha256加密。结合170号段首批放号的联通号码开头是1709,加上国家代码86,用下面exp爆破得到flag。

import hashlib

table = '1234567890'
start = '861709'
len = len(table)
m = 'c22a563acc2a587afbfaaaa6d67bc6e628872b00bd7e998873881f7c6fdc62fc'

for a in range(len):
    for b in range(len):
        for c in range(len):
            for d in range(len):
                for e in range(len):
                    for f in range(len):
                        for g in range(len):
                            phone = start + \
                                table[a] + table[b] + table[c] + \
                                table[d] + table[e] + table[f] + table[g]
                            phone_hash = hashlib.sha256(
                                phone.encode('utf-8')).hexdigest()
                            if phone_hash == m:
                                print("flag{"+phone+"}")

# flag{8617091733716}

得到flag{8617091733716}

crypto405

这道题写完之后看了一些其它的解答过程,大部分都是将初始的k数组通过前五个字符列方程的形式求出(至于解法可能不大相同),应该是一种更加直接的想法。不过本人在做题的时候使用的方法感觉有点是找数学规律了,适用性应该不强。

查看py脚本,使用一组5个未知数的初始向量,对每一个字符在模p的有限域下,进行乘法运算加密,并且将结果保留用于下一轮计算。其中未知量有模数p和初始向量。不妨将42轮加密所用的一维数组 k [ i ] k[i] k[i]变为二维数组 k [ i ] [ j ] k[i][j] k[i][j],其意义为,第 i i i 轮加密(从0开始)时,第 j j j 个乘法运算。
从flag前5个字符即"flag{"的加密来推导解题思路,下列为了方便起见将 k [ i ] [ j ] k[i][j] k[i][j]简写为kij,省略左右模p,同时其中的data0,data1…意为output中的参数:
{ k 11 = f ∗ k 01 , k 12 = f ∗ k 01 ∗ k 02 , . . . k 21 = l ∗ k 11 , k 22 = l ∗ k 11 ∗ k 12 , . . . k 31 = a ∗ k 21 , k 32 = a ∗ k 21 ∗ k 22 , . . . \begin{cases}k11=f*k01,k12=f*k01*k02,...\\ k21=l*k11,k22=l*k11*k12,...\\ k31=a*k21,k32=a*k21*k22,...\\ \end{cases} k11=fk01,k12=fk01k02,...k21=lk11,k22=lk11k12,...k31=ak21,k32=ak21k22,...
进一步得:
{ d a t a 0 = f ∗ k 01 ∗ k 02 ∗ k 03 ∗ k 04 ∗ k 05 = k 15 d a t a 1 = l ∗ k 11 ∗ k 12 ∗ k 13 ∗ k 14 ∗ k 15 = k 25 = k 24 ∗ k 15 d a t a 2 = a ∗ k 21 ∗ k 22 ∗ k 23 ∗ k 24 ∗ k 25 = k 35 = k 34 ∗ k 25 = k 33 ∗ k 24 ∗ k 25 \begin{cases} data0=f*k01*k02*k03*k04*k05=k15\\ data1=l*k11*k12*k13*k14*k15=k25=k24*k15\\ data2=a*k21*k22*k23*k24*k25=k35=k34*k25=k33*k24*k25 \end{cases} data0=fk01k02k03k04k05=k15data1=lk11k12k13k14k15=k25=k24k15data2=ak21k22k23k24k25=k35=k34k25=k33k24k25
最后得:
{ k 15 = d a t a 0    ( m o d   p ) k 24 = d a t a 1 ∗ k 1 5 − 1    ( m o d   p ) k 33 = d a t a 2 ∗ ( k 24 ∗ k 25 ) − 1    ( m o d   p ) . . . \begin{cases} k15 = data0~~(mod~p)\\ k24=data1*k15^{-1}~~(mod~p)\\ k33=data2*(k24*k25)^{-1}~~(mod~p)\\ ... \end{cases} k15=data0  (mod p)k24=data1k151  (mod p)k33=data2(k24k25)1  (mod p)...
那么可以看出,由42条 d a t a data data 可以推出所有的 k [ i ] [ 5 ] k[i][5] k[i][5],从 i = 2 i = 2 i=2开始的 k [ i ] [ 4 ] k[i][4] k[i][4],从 i = 3 i = 3 i=3开始的 k [ i ] [ 3 ] k[i][3] k[i][3],从 i = 4 i = 4 i=4开始的 k [ i ] [ 2 ] k[i][2] k[i][2],从 i = 5 i = 5 i=5开始的 k [ i ] [ 1 ] k[i][1] k[i][1],即从"flag{“开始所有的字符都能够通过output中的数据求得。
但上述求解的条件是一个确定的模数p,而题目所给了p是一个16位的素数,从output中最大数开始,到 2 17 2^{17} 217 结束,逐一爆破其中素数,只需要最后一位的ASCII码与”}"的相同即可。
解题exp如下:

from Crypto.Util.number import *
import gmpy2

data = [0x2066, 0xa222, 0xcbb1, 0xdbb4, 0xdeb4, 0xb1c5, 0x33a4, 0xc051, 0x3b79, 0x6bf8, 0x2131, 0x2c40, 0x91ba, 0x7b44, 0x5f25, 0x0208, 0x7edb, 0x62b5, 0xcec5, 0x5ab3, 0x3c46,
        0xc272, 0x714b, 0x9e0b, 0x48ee, 0x44cc, 0x05a0, 0x3da3, 0x11b1, 0x259f, 0x899d, 0xa130, 0xe58f, 0x23f3, 0x5829, 0x6beb, 0x3681, 0x0054, 0xa189, 0x2765, 0xc63d, 0xbc68]
max = max(data)
# 为k开出二维数组空间
k = [0]*(50*5)

# 计算区间内所有素数
db = []
for i in range(max, pow(2, 17)):
    if gmpy2.is_prime(i):
        db.append(i)

# 求解出k51,k52,k53,k54,k55后即可推出所有内容
def decrypt(p):
    for i in range(0, 42):
        k[(i+1)*5+5] = data[i]
    for i in range(1, 42):
        k[(i+1)*5+4] = data[i] * gmpy2.invert(k[i*5+5], p) % p
    for i in range(2, 42):
        k[(i+1)*5+3] = data[i] * gmpy2.invert(k[i*5+4]*k[i*5+5], p) % p
    for i in range(3, 42):
        k[(i+1)*5+2] = data[i] * gmpy2.invert(k[i*5+3]*k[i*5+4]*k[i*5+5], p) % p
    for i in range(4, 42):
        k[(i+1)*5+1] = data[i] * gmpy2.invert(k[i*5+2]
                                              * k[i*5+3]*k[i*5+4]*k[i*5+5], p) % p


# 逐一爆破素数库
for p in db:
    decrypt(p)
    # 循环求解至最后一个字符,判断是否为"}"
    for i in range(5, 42):
        x = data[i] * gmpy2.invert(k[i*5+1]*k[i*5+2]
                                   * k[i*5+3]*k[i*5+4]*k[i*5+5], p) % p
    if x == 125:
        flag = 'flag{'
        # 重新遍历一次,并将结果转换为字符
        for i in range(5, 42):
            x = data[i] * gmpy2.invert(k[i*5+1]*k[i*5+2]
                                       * k[i*5+3]*k[i*5+4]*k[i*5+5], p) % p
            flag = flag + chr(x)
        print(flag)

# flag{749d39d4-78db-4c55-b4ff-bca873d0f18e}

得到flag{749d39d4-78db-4c55-b4ff-bca873d0f18e}

crypto162

恰巧在picoCTF2022中做过一个很相似的题Sequences,个人当时给出的wp在这:(picoCTF2022_wp@fgps_fly1ng_pengu1ns的博客-CSDN博客
用到了这篇文章Solving Linear Recurrence Relation | HackerEarth中提到的矩阵法求解递推数列。
将给的cal函数稍微整理一下得到下式方程:
f ( x ) = { 1 ,    x = 0 2 ,    x = 1 3 ,    x = 2 c o f ( 0 ) ∗ f ( x − 1 ) + c o f ( 1 ) ∗ f ( x − 2 ) + c o f ( 2 ) ∗ f ( x − 3 ) ,    x ⩾ 4 f(x) = \begin{cases} 1,\,\,x = 0\\ 2,\,\,x=1\\ 3,\,\,x=2\\ cof(0)*f(x-1) + cof(1)*f(x-2) + cof(2)*f(x-3),\,\,x\geqslant4\\ \end{cases} f(x)= 1,x=02,x=13,x=2cof(0)f(x1)+cof(1)f(x2)+cof(2)f(x3),x4
那么可以有初始矩阵 a 0 a_0 a0
[ 1 2 3 ] \begin{bmatrix} {1}\\ {2}\\ {3}\\ \end{bmatrix} 123
和变换矩阵 T T T
[ 0 1 0 0 0 1 c o f [ 2 ] c o f [ 1 ] c o f [ 0 ] ] \begin{bmatrix} {0}&{1}&{0}\\ {0}&{0}&{1}\\ {cof[2]}&{cof[1]}&{cof[0]}\\ \end{bmatrix} 00cof[2]10cof[1]01cof[0]
想要求得 f ( x ) f(x) f(x)只需要计算 T x − 1 ∗ a 0 T^{x-1}*a_0 Tx1a0的第一项即可。此后直接照搬后续加密代码,改写为解密形式即可。
使用sage解决矩阵方便一点,解题exp如下:

from hashlib import md5,sha256
from Crypto.Cipher import AES
import sys

cof_t = [[353, -1162, 32767], [206, -8021, 42110], [262, -7088, 31882], [388, -6394, 21225], [295, -9469, 44468], [749, -3501, 40559], [528, -2690, 10210], [354, -5383, 18437], [491, -8467, 26892], [932, -6984, 20447], [731, -6281, 11340], [420, -5392, 44071], [685, -6555, 40938], [408, -8070, 47959], [182, -9857, 49477], [593, -3584, 49243], [929, -7410, 31929], [970, -4549, 17160], [141, -2435, 36408], [344, -3814, 18949], [291, -7457, 40587], [765, -7011, 32097], [700, -8534, 18013], [267, -2541, 33488], [249, -8934, 12321], [589, -9617, 41998], [840, -1166, 22814], [947, -5660, 41003], [206, -7195, 46261], [784, -9270, 28410], [338, -3690, 19608], [559, -2078, 44397], [534, -3438, 47830], [515, -2139, 39546], [603, -6460, 49953], [234, -6824, 12579], [805, -8793, 36465], [245, -5886, 21077], [190, -7658, 20396], [392, -7053, 19739], [609, -5399, 39959], [479, -8172, 45734], [321, -7102, 41224], [720, -4487, 11055], [208, -1897, 15237], [890, -4427, 35168], [513, -5106, 45849], [666, -1137, 23725], [755, -6732, 39995], [589, -6421, 43716], [866, -3265, 30017], [416, -6540, 34979], [840, -1305, 18242], [731, -6844, 13781], [561, -2728, 10298], [863, -5953, 23132], [204, -4208, 27492], [158, -8701, 12720], [802, -4740, 16628], [491, -6874, 29057], [531, -4829, 29205], [363, -4775, 41711], [319, -9206, 46164], [317, -9270, 18290], [680, -5136, 12009], [880, -2940, 34900], [162, -2587, 49881], [997, -5265, 20890], [485, -9395, 23048], [867, -1652, 18926], [691, -7844, 11180], [355, -5990, 13172], [923, -2018, 23110], [214, -4719, 23005], [921, -9528, 29351], [349, -7957, 20161], [470, -1889, 46170], [244, -6106, 23879], [419, -5440, 43576], [930, -1123, 29859], [151, -5759, 23405], [843, -6770, 36558], [574, -6171, 33778], [772, -1073, 44718], [932, -4037, 40088], [848, -5813, 27304], [194, -6016, 39770], [966, -6789, 14217], [219, -6849, 40922], [352, -6046, 18558], [794, -8254, 29748], [618, -5887, 15535], [202, -9288, 26590], [611, -4341, 46682], [155, -7909, 16654], [935, -5739, 39342], [998, -6538, 24363], [125, -5679, 36725], [507, -7074, 15475], [699, -5836, 47549]]

# 构造初始矩阵和变换矩阵
a0 = matrix([1, 2, 3]).transpose()
T = matrix(ZZ, 3, 3)
T[0] = [0, 1, 0]
T[1] = [0, 0, 1]
T[2] = [3, 2, 1]

# 类似于快速幂加速处理过程
def cnt_Ti(i):
    if i == 1:
        return T
    elif i % 2:
        return T*cnt_Ti(i-1)
    else:
        return cnt_Ti(i//2) ^ 2

# 计算数列第i项的值
def m_func(i):
    tmp = cnt_Ti(i)*a0
    return tmp[0][0]

if __name__ == "__main__":
    s = 0
    for i in range(100):
        T[2] = list(reversed(cof_t[i]))
        s += m_func(200000)
    # 直接照搬解密流程
    s = str(s)[-2000:-1000]
    key = md5(s.encode()).digest()
    check = sha256(key).hexdigest()
    verify = '2cf44ec396e3bb9ed0f2f3bdbe4fab6325ae9d9ec3107881308156069452a6d5'
    data = "4f12b3a3eadc4146386f4732266f02bd03114a404ba4cb2dabae213ecec451c9d52c70dc3d25154b5af8a304afafed87"
    assert(check == verify)
    aes = AES.new(key, AES.MODE_ECB)
    print(aes.decrypt(bytes.fromhex(data)))

# flag{519427b3-d104-4c34-a29d-5a7c128031ff}

得到结果,将末尾多余的\x00去掉后得到flag{519427b3-d104-4c34-a29d-5a7c128031ff}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值