LilCTF wp及复现

LilCTF wp及复现

wp:

1.ez_math

题目:
from sage.all import *
from Crypto.Util.number import *

flag = b'LILCTF{test_flag}'[7:-1]
lambda1 = bytes_to_long(flag[:len(flag)//2])
lambda2 = bytes_to_long(flag[len(flag)//2:])
p = getPrime(512)
def mul(vector, c):
    return [vector[0]*c, vector[1]*c]

v1 = [getPrime(128), getPrime(128)]
v2 = [getPrime(128), getPrime(128)]

A = matrix(GF(p), [v1, v2])
B = matrix(GF(p), [mul(v1,lambda1), mul(v2,lambda2)])
C = A.inverse() * B

print(f'p = {p}')
print(f'C = {str(C).replace(" ", ",").replace("\n", ",").replace("[,", "[")}')

# p = 9620154777088870694266521670168986508003314866222315790126552504304846236696183733266828489404860276326158191906907396234236947215466295418632056113826161
# C = [7062910478232783138765983170626687981202937184255408287607971780139482616525215270216675887321965798418829038273232695370210503086491228434856538620699645,7096268905956462643320137667780334763649635657732499491108171622164208662688609295607684620630301031789132814209784948222802930089030287484015336757787801],[7341430053606172329602911405905754386729224669425325419124733847060694853483825396200841609125574923525535532184467150746385826443392039086079562905059808,2557244298856087555500538499542298526800377681966907502518580724165363620170968463050152602083665991230143669519866828587671059318627542153367879596260872]
分析:

A是由两行向量v1,v2组成的2*2矩阵(元素在GF(p)上)

B的两行分别是lambda1 * v1和lambda2 * v2

因此B可以写成 B = S A,其中S = diag (λ1,λ2)(对角矩阵),相当于在A的基础上,第一行乘λ1,第二行乘λ2

C = A^(-1)B = A ^(-1) S A ,所以C和对角矩阵S相似,具有相同的特征值,迹,行列式值

因为λ1,λ2远小于p,所以将特征值在GF(p)中恢复为整数后,转换为字节序列,在拼接起来就是flag

exp:
from Crypto.Util.number import long_to_bytes
from sympy import sqrt_mod

p = 9620154777088870694266521670168986508003314866222315790126552504304846236696183733266828489404860276326158191906907396234236947215466295418632056113826161
C_rows = [[7062910478232783138765983170626687981202937184255408287607971780139482616525215270216675887321965798418829038273232695370210503086491228434856538620699645,7096268905956462643320137667780334763649635657732499491108171622164208662688609295607684620630301031789132814209784948222802930089030287484015336757787801],[7341430053606172329602911405905754386729224669425325419124733847060694853483825396200841609125574923525535532184467150746385826443392039086079562905059808,2557244298856087555500538499542298526800377681966907502518580724165363620170968463050152602083665991230143669519866828587671059318627542153367879596260872]]

#求迹
tr = (C_rows[0][0] + C_rows[1][1]) % p
#求行列式
det = (C_rows[0][0]*C_rows[1][1] - C_rows[0][1]*C_rows[1][0]) % p

#构造二次方程:λ2−(tr)λ+detC≡0(modp)
#求Δ
disc = (tr*tr - 4*det) % p
sqrt_list = sqrt_mod(disc, p, True)  # 可能返回两个根 r 和 p-r

# 利用二次方程求根公式求根
cands = []
for r in sqrt_list:
    lam1 = ((tr + r) // 2) % p
    lam2 = ((tr - r) // 2) % p
    cands.append((lam1, lam2))

# 结果拼接起来获得flag
for lam1, lam2 in cands:
    b1 = long_to_bytes(lam1)
    b2 = long_to_bytes(lam2)
    print("candidate parts:", b1, b2)
    print("candidate flags:", b1 + b2, b2 + b1)

结果截图:

image

运行出来有两个结果,提交第一个成功了

2.mid_math

题目:
from sage.all import *
from Crypto.Util.number import *
from tqdm import tqdm
from random import randint
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

flag = b'LILCTF{test_flag}'

p = getPrime(64)
P = GF(p)

key = randint(2**62, p)

def mul(vector, c):
    return [vector[0]*c, vector[1]*c, vector[2]*c, vector[3]*c, vector[4]*c]

v1 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v2 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v3 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v4 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v5 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
a, b, c, d, e = getPrime(64), getPrime(64), getPrime(64), getPrime(64),  0

A = matrix(P, [v1, v2, v3, v4, v5])
B = matrix(P, [mul(v1,a), mul(v2,b), mul(v3, c), mul(v4, d), mul(v5, e)])
C = A.inverse() * B
D = C**key

key = pad(long_to_bytes(key), 16)
aes = AES.new(key,AES.MODE_ECB)
msg = aes.encrypt(pad(flag, 64))

print(f"p = {p}")
print(f'C = {[i for i in C]}'.replace('(', '[').replace(')', ']'))
print(f'D = {[i for i in D]}'.replace('(', '[').replace(')', ']'))
print(f"msg = {msg}")

#p = 14668080038311483271
#C = [[11315841881544731102, 2283439871732792326, 6800685968958241983, 6426158106328779372, 9681186993951502212], [4729583429936371197, 9934441408437898498, 12454838789798706101, 1137624354220162514, 8961427323294527914], [12212265161975165517, 8264257544674837561, 10531819068765930248, 4088354401871232602, 14653951889442072670], [6045978019175462652, 11202714988272207073, 13562937263226951112, 6648446245634067896, 13902820281072641413], [1046075193917103481, 3617988773170202613, 3590111338369894405, 2646640112163975771, 5966864698750134707]]
#D = [[1785348659555163021, 3612773974290420260, 8587341808081935796, 4393730037042586815, 10490463205723658044], [10457678631610076741, 1645527195687648140, 13013316081830726847, 12925223531522879912, 5478687620744215372], [9878636900393157276, 13274969755872629366, 3231582918568068174, 7045188483430589163, 5126509884591016427], [4914941908205759200, 7480989013464904670, 5860406622199128154, 8016615177615097542, 13266674393818320551], [3005316032591310201, 6624508725257625760, 7972954954270186094, 5331046349070112118, 6127026494304272395]]
#msg = b"\xcc]B:\xe8\xbc\x91\xe2\x93\xaa\x88\x17\xc4\xe5\x97\x87@\x0fd\xb5p\x81\x1e\x98,Z\xe1n`\xaf\xe0%:\xb7\x8aD\x03\xd2Wu5\xcd\xc4#m'\xa7\xa4\x80\x0b\xf7\xda8\x1b\x82k#\xc1gP\xbd/\xb5j"
分析:

D = C**key,作用于有限域GF(p),其中D,C,p已知,要求解key。这是一个典型的离散对数问题,直接用sagemath中的discrete_log()方法解出key。后续就是一个已知key的AES的ECB模式解密。

exp:
#sagemath求key

p = 14668080038311483271
F = GF(p)

# 构建矩阵 C 和 D
C_list = [
    [11315841881544731102, 2283439871732792326, 6800685968958241983, 6426158106328779372, 9681186993951502212],
    [4729583429936371197, 9934441408437898498, 12454838789798706101, 1137624354220162514, 8961427323294527914],
    [12212265161975165517, 8264257544674837561, 10531819068765930248, 4088354401871232602, 14653951889442072670],
    [6045978019175462652, 11202714988272207073, 13562937263226951112, 6648446245634067896, 13902820281072641413],
    [1046075193917103481, 3617988773170202613, 3590111338369894405, 2646640112163975771, 5966864698750134707]
]

D_list = [
    [1785348659555163021, 3612773974290420260, 8587341808081935796, 4393730037042586815, 10490463205723658044],
    [10457678631610076741, 1645527195687648140, 13013316081830726847, 12925223531522879912, 5478687620744215372],
    [9878636900393157276, 13274969755872629366, 3231582918568068174, 7045188483430589163, 5126509884591016427],
    [4914941908205759200, 7480989013464904670, 5860406622199128154, 8016615177615097542, 13266674393818320551],
    [3005316032591310201, 6624508725257625760, 7972954954270186094, 5331046349070112118, 6127026494304272395]
]

C = matrix(F, C_list)
D = matrix(F, D_list)

# 计算特征值
eigC = C.eigenvalues()
eigD = D.eigenvalues()

# 过滤非零特征值
eigC_nonzero = [x for x in eigC if x != 0]
eigD_nonzero = [x for x in eigD if x != 0]

# 尝试所有特征值配对
found_key = None
for base in eigC_nonzero:
    for target in eigD_nonzero:
        try:
            k = discrete_log(target, base)  # 计算离散对数
            # 验证 k 是否满足所有特征值
            matched = [target]
            valid = True
            for x in eigC_nonzero:
                if x == base:
                    continue
                y_val = x**k  # x^k mod p
                found = False
                for t in eigD_nonzero:
                    if t == y_val and t not in matched:
                        matched.append(t)
                        found = True
                        break
                if not found:
                    valid = False
                    break
            # 检查 k 在范围内
            if valid and k >= 2**62 and k < p:
                found_key = k
                print(f"Found key: {found_key}")
                break
        except Exception as e:
            continue
    if found_key is not None:
        break

if found_key is None:
    print("未找到 key")
else:
    print(f"Key = {found_key}")
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
from Crypto.Util.number import long_to_bytes
from Crypto.Util.Padding import pad

key = 5273966641785501202  #从 SageMath 中获得的整数 key
msg = b"\xcc]B:\xe8\xbc\x91\xe2\x93\xaa\x88\x17\xc4\xe5\x97\x87@\x0fd\xb5p\x81\x1e\x98,Z\xe1n`\xaf\xe0%:\xb7\x8aD\x03\xd2Wu5\xcd\xc4#m'\xa7\xa4\x80\x0b\xf7\xda8\x1b\x82k#\xc1gP\xbd/\xb5j"

# 构造 AES 密钥
key_bytes = long_to_bytes(key)
key_bytes_padded = pad(key_bytes, 16)  # PKCS#7 填充至 16 字节

# 解密
cipher = AES.new(key_bytes_padded, AES.MODE_ECB)
decrypted = cipher.decrypt(msg)
flag = unpad(decrypted, 64)  # 移除填充(块大小 64 字节)

print(f"Flag: {flag.decode()}")

结果截图:

image

复现:

1.linear

题目:
import os
import random
import signal

signal.alarm(10)

flag = os.getenv("LILCTF_FLAG", "LILCTF{default}")

nrows = 16
ncols = 32

A = [[random.randint(1, 1919810) for _ in range(ncols)] for _ in range(nrows)]
x = [random.randint(1, 114514) for _ in range(ncols)]

b = [sum(A[i][j] * x[j] for j in range(ncols)) for i in range(nrows)]
print(A)
print(b)

xx = list(map(int, input("Enter your solution: ").strip().split()))
if xx != x:
    print("Oh, your linear algebra needs to be practiced.")
else:
    print("Bravo! Here is your flag:")
    print(flag)

exp:
import pwn
import sage.all
import json

HOST = 'challenge.xinshi.fun'
PORT = 40464

r = pwn.remote(HOST, PORT)
data = r.recvuntil(b"Enter your solution: ")
data_str = data.decode()
#找到a的位置
start_A = data_str.find('[[')
end_A = data_str.find(']]', start_A) + 2
A_str = data_str[start_A:end_A]
A_data = json.loads(A_str)
# 找到b的位置 
start_b = data_str.find('[', end_A)
end_b = data_str.find(']', start_b) + 1
b_str = data_str[start_b:end_b]
b_data = json.loads(b_str)
#
A = sage.all.matrix(sage.all.ZZ, A_data)
b = sage.all.vector(sage.all.ZZ, b_data)
M = A.augment(-b)
# 求 M 的整数核(kernel),找到格的基
kernel = M.right_kernel()
basis = kernel.basis()
#
Lattice = sage.all.matrix(basis)
print("格的基矩阵大小: {} x {}".format(Lattice.nrows(), Lattice.ncols()))

short_basis = Lattice.LLL()
v = short_basis[0]

# 归一化
y = v.change_ring(sage.all.QQ) / v[-1]
# 把 y 的前32个数取出来,转成整数,这就是我们的解
solution = sage.all.vector(sage.all.ZZ, y[:-1])

solution_str = " ".join(map(str, solution))
r.sendline(solution_str.encode())
r.interactive()
分析:

通过构造增广矩阵来进行求解。

image

将Ax = b这个非齐次方程,转换成 M v = 0这个齐次方程 。

几个函数的分析 :

1.json.loads():将字符串转换成python中的列表。

2.M.right_kernel():计算矩阵M的右核(“右核”:所有满足M x = 0的列向量x的集合),返回一个“子空间对象”(而非返回向量)。

区别:

M.left_kernel(): 满足y M = 0的行向量y。

3.kernel.basis():从右核子空间对象 kernel 中提取其基向量列表,基向量是构成右核的 “最小线性无关组”。

将kernel.basis()的结果用来构造格,再进行格基规约(LLL),取第一个向量,归一化处理(需转换到有理数域空间上)。

2.space_travel

题目:
from Crypto.Cipher import AES
from hashlib import md5
from os import urandom
from params import vecs

key = int("".join([vecs[int.from_bytes(urandom(2)) & 0xfff] for _ in range(50)]), 2)

print("🎁 :", [[nonce := int(urandom(50*2).hex(), 16), (bin(nonce & key).count("1")) % 2] for _ in range(600)])
print("🚩 :", AES.new(key=md5(str(key).encode()).digest(), nonce=b"Tiffany", mode=AES.MODE_CTR).encrypt(open("flag.txt", "rb").read()))

另有输出文件,由于数据量太大,就不粘过来了。

分析:

分析题目,知道key是由vecs数组中的50个元素拼接成的,共800bit。

🎁 部分:一个由600个元素组成的列表,每个元素由两部分组成:nonce和b(暂且用b表示一下,题目没有具体的名称)。nonce是随机生成的,800bit,b是由nonce和key按位与后再模2,也可以理解为nonce二进制表示的每一位和key二进制表示的对应比特位相乘之和再模2。

image

🚩部分:一个CRT模式的AES加密。

所以核心是找key。

按照bin(nonce & key).count("1")) % 2的逻辑,可以得到600个方程,但是key有800位,难道又是一个欠定线性方程组求解?不合理,不太会有两道题相同的考点。

分析vecs列表 ,一共4096(2 ^ 12)个元素,每个元素是一个16bit的二进制数字符串。联想到二进制线性码(或其仿射码)。具体细节如下:

image

exp:
from Crypto.Cipher import AES
from hashlib import md5

vecs = 
gift = 
enc = 

vecs_bin = [list(map(int, i)) for i in vecs]
vecs_bin = [vector(GF(2), i) for i in vecs_bin]

nonce_bin = [list(map(int, bin(i[0])[2:].zfill(800))) for i in gift]
nonce_bin = [vector(GF(2), i) for i in nonce_bin]

A = Matrix(GF(2), nonce_bin).T
b = vector(GF(2), [i[1] for i in gift])

v0 = vecs_bin[0]
L = Matrix(GF(2), [i - v0 for i in vecs_bin[1:]])
L1 = L.echelon_form()[:12]
v0_ = vector(GF(2), v0.list()*50)
L1_ = block_diagonal_matrix([L1 for _ in range(50)])
x = (L1_*A).solve_left(b - v0_*A)

x_ = [(vector(GF(2), x.list()[12*j:12*j+12])*L1+v0).list() for j in range(50)]
key = ""
for i in x_:
    for j in i:
        key += str(j)
        
key = int(key, 2)
flag = AES.new(key=md5(str(key).encode()).digest(), nonce=b"Tiffany", mode=AES.MODE_CTR).decrypt(enc)
print(flag)

#LILCTF{Un1qUe_s0luti0n_1N_sUbSp4C3!}

3.baaaaaag

题目:
from Crypto.Util.number import *
import random
from Crypto.Cipher import AES
import hashlib
from Crypto.Util.Padding import pad
from secret import flag

p = random.getrandbits(72)
assert len(bin(p)[2:]) == 72

a = [getPrime(90) for _ in range(72)]
b = 0
t = p
for i in a:
    temp = t % 2
    b += temp * i
    t = t >> 1

key = hashlib.sha256(str(p).encode()).digest()
cipher = AES.new(key, AES.MODE_ECB)
flag = pad(flag,16)
ciphertext = cipher.encrypt(flag)

print(f'a = {a}')
print(f'b = {b}')
print(f"ciphertext = {ciphertext}")

'''
a = [965032030645819473226880279, 699680391768891665598556373, 1022177754214744901247677527, 680767714574395595448529297, 1051144590442830830160656147, 1168660688736302219798380151, 796387349856554292443995049, 740579849809188939723024937, 940772121362440582976978071, 787438752754751885229607747, 1057710371763143522769262019, 792170184324681833710987771, 912844392679297386754386581, 906787506373115208506221831, 1073356067972226734803331711, 1230248891920689478236428803, 713426848479513005774497331, 979527247256538239116435051, 979496765566798546828265437, 836939515442243300252499479, 1185281999050646451167583269, 673490198827213717568519179, 776378201435505605316348517, 809920773352200236442451667, 1032450692535471534282750757, 1116346000400545215913754039, 1147788846283552769049123803, 994439464049503065517009393, 825645323767262265006257537, 1076742721724413264636318241, 731782018659142904179016783, 656162889354758353371699131, 1045520414263498704019552571, 1213714972395170583781976983, 949950729999198576080781001, 1150032993579134750099465519, 975992662970919388672800773, 1129148699796142943831843099, 898871798141537568624106939, 997718314505250470787513281, 631543452089232890507925619, 831335899173370929279633943, 1186748765521175593031174791, 884252194903912680865071301, 1016020417916761281986717467, 896205582917201847609656147, 959440423632738884107086307, 993368100536690520995612807, 702602277993849887546504851, 1102807438605649402749034481, 629539427333081638691538089, 887663258680338594196147387, 1001965883259152684661493409, 1043811683483962480162133633, 938713759383186904819771339, 1023699641268310599371568653, 784025822858960757703945309, 986182634512707587971047731, 1064739425741411525721437119, 1209428051066908071290286953, 667510673843333963641751177, 642828919542760339851273551, 1086628537309368288204342599, 1084848944960506663668298859, 667827295200373631038775959, 752634137348312783761723507, 707994297795744761368888949, 747998982630688589828284363, 710184791175333909291593189, 651183930154725716807946709, 724836607223400074343868079, 1118993538091590299721647899]
b = 34962396275078207988771864327
ciphertext = b'Lo~G\xf46>\xd609\x8e\x8e\xf5\xf83\xb5\xf0\x8f\x9f6&\xea\x02\xfa\xb1_L\x85\x93\x93\xf7,`|\xc6\xbe\x05&\x85\x8bC\xcd\xe6?TV4q'
'''
分析:

要想找出flag,就要解出p,由题意可知p的求解是一个背包问题,通过构造格来求解。

exp:
from Crypto.Cipher import AES
import hashlib

def BKZ_search(a,b,size):
    n = len(a)
    M = Matrix(ZZ,n+1,n+1)
    for i in range(n):
        M[i,i] = 2
        M[i,-1] = a[i]
        M[-1,i] = -1
    M[-1,-1] = -b
    L = M.BKZ(block_size=size)

    for res in L:
        kk = [(i+1)//2 for i in res[:-1]]
        if all(x in [0,1] for x in kk):
            secret = [str(bit) for bit in kk]
            return secret

a = ...
b = 34962396275078207988771864327
ciphertext = b'Lo~G\xf46>\xd609\x8e\x8e\xf5\xf83\xb5\xf0\x8f\x9f6&\xea\x02\xfa\xb1_L\x85\x93\x93\xf7,`|\xc6\xbe\x05&\x85\x8bC\xcd\xe6?TV4q'

secret = BKZ_search(a,b,26)
p = str(int("".join(secret[::-1]),2))
key = hashlib.sha256(p.encode()).digest()
cipher = AES.new(key, AES.MODE_ECB)
flag = cipher.decrypt(ciphertext)
print(flag)

# LILCTF{M4ybe_7he_brut3_f0rce_1s_be5t}
posted @ 2026-05-28 15:55  xiehou~  阅读(8)  评论(0)    收藏  举报