1591 words
8 minutes
2024 浙江省大学生网络与信息安全技能赛

MyCode#

题面:#

import numpy as np

def substitute(state, sub_box):
    return [sub_box[b & 0xF] | (sub_box[(b >> 4) & 0xF] << 4) for b in state]

def generate_round_keys(base_key, rounds):
    round_keys = []
    temp_key = base_key
    for _ in range(rounds):
        round_keys.append(temp_key & 0xFFFFFFFF)
        temp_key ^= ((temp_key << 1) & 0xFFFFFFFF) | ((temp_key >> 31) & 0x1)
    return round_keys

def process_state(base_key, state, rounds, encrypt):
    sub_box = [0x9, 0x4, 0xA, 0xB, 0xD, 0x1, 0x8, 0x5, 0x6, 0x2, 0x0, 0x3, 0xC, 0xE, 0xF, 0x7]
    inv_sub_box = [0xA, 0x5, 0x9, 0xB, 0x1, 0x7, 0x8, 0xF, 0x6, 0x0, 0x2, 0x3, 0xC, 0x4, 0xD, 0xE]

    round_keys = generate_round_keys(base_key, rounds)

    if encrypt:
        for round in range(rounds):
            state = substitute(state, sub_box)
            state = [s ^ ((round_keys[round] >> (i * 8)) & 0xFF) for i, s in enumerate(state)]
    else:
        for round in range(rounds - 1, -1, -1):
            state = [s ^ ((round_keys[round] >> (i * 8)) & 0xFF) for i, s in enumerate(state)]
            state = substitute(state, inv_sub_box)

    return state

def encrypt(plaintext, key, rounds=10):
    length = len(plaintext)
    padded_length = length if length % 4 == 0 else length + (4 - (length % 4))
    plaintext += b'\x00' * (padded_length - length)

    ciphertext = bytearray(padded_length)
    for i in range(0, padded_length, 4):
        state = list(plaintext[i:i+4])
        state = process_state(key, state, rounds, True)
        ciphertext[i:i+4] = state

    return ciphertext

def decrypt(ciphertext, key, rounds=10):
    length = len(ciphertext)
    plaintext = bytearray(length)
    for i in range(0, length, 4):
        state = list(ciphertext[i:i+4])
        state = process_state(key, state, rounds, False)
        plaintext[i:i+4] = state

    return plaintext.rstrip(b'\x00')

def main():
    plaintext = b"DASCTF{******}"
    key = 0xECB... # 4 bytes
    ciphertext = encrypt(plaintext, key)
    print("Ciphertext:", ''.join(f"{b:02X}" for b in ciphertext))

if __name__ == "__main__":
    main()
# Ciphertext: A6B343D2C6BE1B268C3EA4744E3AA9914E29A0789F299022820299248C23D678442A902B4C24A8784A3EA401

分析:#

一个签到题,甚至给了decrypt,我们唯一要做的部分在

print("Ciphertext:", ''.join(f"{b:02X}" for b in ciphertext))

题解:#

import numpy as np
from Crypto.Util.number import *
from tqdm import *


def substitute(state, sub_box):
    return [sub_box[b & 0xF] | (sub_box[(b >> 4) & 0xF] << 4) for b in state]

def generate_round_keys(base_key, rounds):
    round_keys = []
    temp_key = base_key
    for _ in range(rounds):
        round_keys.append(temp_key & 0xFFFFFFFF)
        temp_key ^= ((temp_key << 1) & 0xFFFFFFFF) | ((temp_key >> 31) & 0x1)
    return round_keys

def process_state(base_key, state, rounds, encrypt):
    sub_box = [0x9, 0x4, 0xA, 0xB, 0xD, 0x1, 0x8, 0x5, 0x6, 0x2, 0x0, 0x3, 0xC, 0xE, 0xF, 0x7]
    inv_sub_box = [0xA, 0x5, 0x9, 0xB, 0x1, 0x7, 0x8, 0xF, 0x6, 0x0, 0x2, 0x3, 0xC, 0x4, 0xD, 0xE]

    round_keys = generate_round_keys(base_key, rounds)
    if encrypt:
        for round in range(rounds):
            state = substitute(state, sub_box)
            state = [s ^ ((round_keys[round] >> (i * 8)) & 0xFF) for i, s in enumerate(state)]
    else:
        for round in range(rounds - 1, -1, -1):
            state = [s ^ ((round_keys[round] >> (i * 8)) & 0xFF) for i, s in enumerate(state)]
            state = substitute(state, inv_sub_box)

    return state

def encrypt(plaintext, key, rounds=10):
    length = len(plaintext)
    padded_length = length if length % 4 == 0 else length + (4 - (length % 4))
    plaintext += b'\x00' * (padded_length - length)

    ciphertext = bytearray(padded_length)
    for i in range(0, padded_length, 4):
        state = list(plaintext[i:i+4])
        state = process_state(key, state, rounds, True)
        ciphertext[i:i+4] = state

    return ciphertext

def decrypt(ciphertext, key, rounds=10):
    length = len(ciphertext)
    plaintext = bytearray(length)
    for i in range(0, length, 4):
        state = list(ciphertext[i:i+4])
        state = process_state(key, state, rounds, False)
        plaintext[i:i+4] = state

    return plaintext.rstrip(b'\x00')


Ciphertext = 'A6B343D2C6BE1B268C3EA4744E3AA9914E29A0789F299022820299248C23D678442A902B4C24A8784A3EA401'

ciphertext = []
for i in range(0,len(Ciphertext),2): 
    ciphertext.append(int(Ciphertext[i:i+2],16))

print(ciphertext)

plaintext = b'DASCTF{'
key = 0xECB
print(key.bit_length())

for i in tqdm(range(2**21)):
    flag = decrypt(ciphertext,key*2**20+i)
    if b'DASCTF{' in flag:
        print(flag)

DlcgH_r#

题面:#

from Crypto.Util.number import *
from gmpy2 import *

flag = b'DASCTF{******}'
def iterate_function(seed, coeff_a, coeff_b, prime_modulus):
    return (coeff_a * seed + coeff_b) % prime_modulus

def iterate_multiple_times(seed, num_iterations, coeff_a, coeff_b, prime_modulus):
    for _ in range(num_iterations):
        seed = iterate_function(seed, coeff_a, coeff_b, prime_modulus)
    return seed

p = getPrime(600)
a = getPrime(512)
b = getPrime(512)
s = getPrime(512)
k = getPrime(512)
t = getPrime(512)

A = iterate_multiple_times(s, k, a, b, p)
B = iterate_multiple_times(s, t, a, b, p)

print("p =", p)
print("a =", a)
print("b =", b)
print("s =", s)
print("A =", A)
print("B =", B)

secret1 = iterate_multiple_times(A, k, a, b, p)
secret2 = iterate_multiple_times(B, t, a, b, p)

assert secret1 == secret2
'''
p = 2565258348684709722726260231955260453241716968378483821594041597297293609376806025180965681289016169408781752953380586044352169083397987333072306444539318806255242559916564022662479
a = 7703427441632069990122897903141278700284019287330080801753208940444135129072547305259960648105321270085533531118395452229965873504176368162947864923497711
b = 8477265953761650860710068507342719089504862957398782381045770264963932696457722724393775545810962476516315838411812248360284564925846788951219272632661157
s = 9228773209718156231041982890745928246648483643042884535935071957475932603607283209094294685862893340598940862096657878372229519375655468524041406914666867
A = 434251860827782638796736001849473241231781620594954088572922898040098881748337513244415553659525671751903798527967205418513869125476445927127124010452649344318178999731385274553080
B = 434251860827782638796736001849473241231781620594954088572922898040098881748337513244415553659525671751903798527967205418513869125476445927127124010452649344318178999731385274553080
'''

p2 = next_prime(secret1)
q2 = getPrime(600)
n2 = p2*q2
e = 4
m = bytes_to_long(flag)
c = pow(m, e, n2)
print("n2 =", n2)
print("c =", c)

'''
n2 = 3241139665583501598296135149075754735041636843305130049654913708275571916563715101898946962033698805416493133339619007016676895968314902474922279948997540924678346952667095320094789476561995339618782687993966133770687551933070478999383821269223854568552819152909266096733330218505088222661907600152055916956562332379930822529724151378274932991887183193175206749
c = 1131281812215293796960536920068009435705926803182047772347743960804329656316689664084120353862091370978145286943689311985878028828902275260824388998300548644880722651153603738691769179255824425771260974588160589473958033612303767050773921373389315920529311000160530833707622310013322631917184737227893101365726934901652170763292132835433158093074003616578836411
'''

分析&题解:#

思路一#

感觉是非预期解,直接爆破k即可,会找到一个较小但满足条件的k,由于e=4,采用rabin解密

from Crypto.Util.number import *
from gmpy2 import *

p = 2565258348684709722726260231955260453241716968378483821594041597297293609376806025180965681289016169408781752953380586044352169083397987333072306444539318806255242559916564022662479
a = 7703427441632069990122897903141278700284019287330080801753208940444135129072547305259960648105321270085533531118395452229965873504176368162947864923497711
b = 8477265953761650860710068507342719089504862957398782381045770264963932696457722724393775545810962476516315838411812248360284564925846788951219272632661157
s = 9228773209718156231041982890745928246648483643042884535935071957475932603607283209094294685862893340598940862096657878372229519375655468524041406914666867
A = 434251860827782638796736001849473241231781620594954088572922898040098881748337513244415553659525671751903798527967205418513869125476445927127124010452649344318178999731385274553080
B = 434251860827782638796736001849473241231781620594954088572922898040098881748337513244415553659525671751903798527967205418513869125476445927127124010452649344318178999731385274553080
assert A==B

seed = s
count = 0
while seed!=A:
    seed = (a*seed+b)%p
    count += 1
print(count)
#12345
seed = A
for i in range(12345):
    seed = (a*seed+b)%p
print(seed)

p2 = next_prime(seed)

n2 = 3241139665583501598296135149075754735041636843305130049654913708275571916563715101898946962033698805416493133339619007016676895968314902474922279948997540924678346952667095320094789476561995339618782687993966133770687551933070478999383821269223854568552819152909266096733330218505088222661907600152055916956562332379930822529724151378274932991887183193175206749
c = 1131281812215293796960536920068009435705926803182047772347743960804329656316689664084120353862091370978145286943689311985878028828902275260824388998300548644880722651153603738691769179255824425771260974588160589473958033612303767050773921373389315920529311000160530833707622310013322631917184737227893101365726934901652170763292132835433158093074003616578836411

e = 4
q2 = n2//p2
print(isPrime(q2),isPrime(p2))

def rabin_decrypt(c, p, q, e=2):
    n = p*q
    mp = pow(c, (p+1)//4, p)
    mq = pow(c, (q+1)//4, q)
    yp = gmpy2.invert(p, q)
    yq = gmpy2.invert(q, p)
    r = (yp*p*mq + yq*q*mp) % n
    rr = n-r
    s = (yp*p*mq - yq*q*mp) % n
    ss = n-s
    return (r,rr,s,ss)

m = rabin_decrypt(c, p2, q2)

for i in range(4):
    try:
        print(bytes.fromhex(hex(gmpy2.iroot(m[i],2)[0])[2:]))
    except:
        pass

思路二#

看一眼数据就会发现,A和B是相等的

A=seedak+b(ak1+...+a+1)=seedak+bak1a1同理:secret1=Aak+b(ak1+...+a+1)=Aak+bak1a1\begin{aligned} A&=seed*a^k+b(a^{k-1}+...+a+1)\\ &=seed*a^k+b\frac{a^k-1}{a-1}\\ 同理:\\ secret1&=A*a^k+b(a^{k-1}+...+a+1)\\ &=A*a^k+b\frac{a^k-1}{a-1} \end{aligned}

可以在模p下解出aka^k ,然后代入上面的方程即可得到secret,并且已知p,q后可分别在模p,模q下解有限域方程得到mp,mq,然后中国剩余定理结合一下

from Crypto.Util.number import *
from gmpy2 import *

p = 2565258348684709722726260231955260453241716968378483821594041597297293609376806025180965681289016169408781752953380586044352169083397987333072306444539318806255242559916564022662479
a = 7703427441632069990122897903141278700284019287330080801753208940444135129072547305259960648105321270085533531118395452229965873504176368162947864923497711
b = 8477265953761650860710068507342719089504862957398782381045770264963932696457722724393775545810962476516315838411812248360284564925846788951219272632661157
s = 9228773209718156231041982890745928246648483643042884535935071957475932603607283209094294685862893340598940862096657878372229519375655468524041406914666867
A = 434251860827782638796736001849473241231781620594954088572922898040098881748337513244415553659525671751903798527967205418513869125476445927127124010452649344318178999731385274553080
B = 434251860827782638796736001849473241231781620594954088572922898040098881748337513244415553659525671751903798527967205418513869125476445927127124010452649344318178999731385274553080

n = 3241139665583501598296135149075754735041636843305130049654913708275571916563715101898946962033698805416493133339619007016676895968314902474922279948997540924678346952667095320094789476561995339618782687993966133770687551933070478999383821269223854568552819152909266096733330218505088222661907600152055916956562332379930822529724151378274932991887183193175206749
c = 1131281812215293796960536920068009435705926803182047772347743960804329656316689664084120353862091370978145286943689311985878028828902275260824388998300548644880722651153603738691769179255824425771260974588160589473958033612303767050773921373389315920529311000160530833707622310013322631917184737227893101365726934901652170763292132835433158093074003616578836411

a_k = (A*(a-1)+b)/((a-1)*s+b)%p
assert (s*a_k+(a_k-1)/(a-1)*b)%p == A
sc = (A*a_k+(a_k-1)/(a-1)*b)%p

p = next_prime(int(sc))
print(n%p)
q = n//p

R.<x> = PolynomialRing(GF(p))
f1 = x^4-c

R.<x> = PolynomialRing(GF(q))
f2 = x^4-c

for mp in f1.roots():    
    for mq in f2.roots():        
        m = crt([int(mp[0]),int(mq[0])],[p,q])        
        print(long_to_bytes(int(m)))

APT#

题面:#

给了个流量包,开出来是一堆的交互结果

2024浙江省大学生网安竞赛安恒出题-APT的附件.zip

题解:#

将交互部分提取到txt文件中然后往下看就会发现和Oracle交互的对象是在做一个类似Padding Oracle Attack的操作,但是这个Padding是从后往前的,也就是说在不改变后缀的情况下不断向前测试,最后达到了update的效果

**注意:**我们从肉眼角度能够发现它在构造Padding,说明没有其他类似于AES之类的加密在其中干扰,其实我们可以看传输的数据中固定的部分16bytes,他已经是在secret中了,其余的也只是异或操作造成的影响

image-20241110144340039image-20241110144414506

我们对Data update的数据进行异或就会发现它们每串字节最终都是secret尾加上16个0x10构成的头,

(因为观察异或结果会发现后面的16bytes为0x00也就是与secret尾相同)

from base64 import b64decode
from Crypto.Util.number import *


f = open(r'data.txt', 'r')
secret = b64decode('KjFEbghs52mB1LniL64p27rMiho7K1HUkPh7eRLDz89L2V8V0d0/ABMJ4V5aX8txPGI6yJLFGn/UZihefpgqBA==')

a = []
re = []

for i in range(5849):
    a.append(b64decode(f.readline()[2:]))
    if 'Data update' in (f.readline()):
        re.append(a[-1])
        if len(re) > 1:
            print(long_to_bytes(bytes_to_long(re[-1]) ^ bytes_to_long(re[-2])))

image-20241110143324368

至此也基本可以确定flag是用异或加密得到的secret

flagkey=secretflag⊕key=secret

构造一个由16个0x10构成的头为H,第i轮的secret头为shish_i,secret尾16bytes为slisl_i,对应的flag头为fhifh_i,成功update的padding串为pip_i,则有

(shi+sli)pi(H+0)=(fhi+0)(sh_i+sl_i)⊕p_i⊕(H+0)=(fh_i+0)
from base64 import b64decode
from Crypto.Util.number import *


f = open(r'data.txt', 'r')
secret = b64decode('KjFEbghs52mB1LniL64p27rMiho7K1HUkPh7eRLDz89L2V8V0d0/ABMJ4V5aX8txPGI6yJLFGn/UZihefpgqBA==')

a = []
re = []

for i in range(6000):
    a.append(b64decode(f.readline()[2:]))
    if 'Data update' in (f.readline()):
        re.append(a[-1])
        if len(re) > 1:
            print(long_to_bytes(bytes_to_long(re[-1]) ^ bytes_to_long(re[-2])))

a = b'\x10'*16+b'\x00'*16

h = []
for i in range(len(re)):
    if len(re[i]) == 0x20 and re[i][0] != 0x30:
        print(long_to_bytes(bytes_to_long(re[i]) ^ bytes_to_long(a) ^ bytes_to_long(secret[i-15:i+17])))

签到#

题面:#

题解:#

爆破选项,然后3des解密

from Crypto.Cipher import DES3
from Crypto.Util.Padding import pad,unpad
import itertools

def des_cbc_decode(key,iv,cipher_data):
    des = DES3.new(key,mode = DES3.MODE_CBC, IV=iv)
    result = des.decrypt(cipher_data)
    return result

list = ['A','B','C','D']

def solve():
    des_iv = b"12345678"
    enc_hex = ""
    enc_flag = bytes.fromhex(enc_hex)
    
    for i in itertools.product(enc_hex):
        des_key = ''.join(i)+"000000"
        des_key = des_key.encode()
        des_dec_data = des_cbc_decode(des_key,des_iv,enc_flag)
        
        if b"DASCTF{" in des_cbc_data:
            print(des_dec_data)
            break
2024 浙江省大学生网络与信息安全技能赛
https://a1ic3.cn/posts/2024浙江省大学生网络与信息安全技能赛/
Author
A1ic3
Published at
2024-12-08