CTF之线性同余算法与赛题

发布于:2025-07-20 ⋅ 阅读:(12) ⋅ 点赞:(0)

概念

什么是线性同余算法(LCG)?
线性同余法(LCG,Linear Congruential Generator)是一种最简单、最常见的伪随机数生成算法
它使用一个递推公式,通过线性变换生成一系列的伪随机数

特点

  • 计算简单,适合快速生成伪随机数
  • 适用于硬件和软件环境
  • 生成的随机数序列具有周期性(如果参数选择不当,周期可能较短)
  • 安全性低,参数和种子易被破解,不适用于密码学

核心公式


X n + 1 = ( a ⋅ X n + c )   m o d   m X_{n+1}=(a\cdot X_{n}+c)\bmod m Xn+1=(aXn+c)modm


参数

参数介绍

  • X n X_{n} Xn:当前随机数(种子、前一个值)
  • a a a:乘数(Multiplier),决定序列的跨度
  • c c c:增量(Increment),若 c c c≠ 0,称为"混合线性同余法”
  • m m m:模数(Modulus),决定取值范围 ( 0 ≤ X n X_{n} Xn< m m m)
    生成的随机数序列周期性重复,周期长度由参数组合决定,理想情况下可达 m m m

参数要求

  • X n X_{n} Xn:可取任意非负整数,不同种子生成不同序列
  • a a a:需满足 a − 1 a-1 a1能被 m m m的所有质因数整除,若 m m m为4的倍数,则 a − 1 a-1 a1也需是4的倍数,以最大化周期
  • c c c:通常取与 m m m互质的非零值,避免序列出现固定偏移
  • m m m:需足够大(如2的幂或大质数)以延长周期

生成序列示例

选择 m = 9 , a = 2 , c = 0 , X 0 = 1 m=9,a=2,c=0,X_{0}=1 m=9,a=2,c=0,X0=1,生成序列

X 1 = ( 2 × 1 + 0 )   m o d   9 = 2 X 2 = ( 2 × 2 + 0 )   m o d   9 = 4 X 3 = ( 2 × 4 + 0 )   m o d   9 = 8 X 4 = ( 2 × 8 + 0 )   m o d   9 = 7 X 5 = ( 2 × 7 + 0 )   m o d   9 = 5 X 6 = ( 2 × 5 + 0 )   m o d   9 = 1 X_{1}=(2\times1+0)\bmod9=2\\X_{2}=(2\times2+0)\bmod9=4\\X_{3}=(2\times4+0)\bmod9=8\\X_{4}=(2\times8+0)\bmod9=7\\X_{5}=(2\times7+0)\bmod9=5\\X_{6}=(2\times5+0)\bmod9=1 X1=(2×1+0)mod9=2X2=(2×2+0)mod9=4X3=(2×4+0)mod9=8X4=(2×8+0)mod9=7X5=(2×7+0)mod9=5X6=(2×5+0)mod9=1

周期开始重复,周期为6

注意点

  • 周期长度明显小于模数 m m m,说明参数选择不当
  • 若调整参数(如 a = 4 , c = 1 , m = 9 a=4,c=1,m=9 a=4,c=1,m=9),可能得到更长的周期

CTF例题

题面

在一次情报传输中,WNCP 团队使用了一种简单的加密方法对一段秘密消息进行了加密
你截获了该密文文件,并知道消息的部分内容(即固定的开头部分)
请分析加密算法,利用已知明文攻击恢复出完整的明文,并找出其中隐藏的 flag

密文

13539cbb0b53c45070dd0957527c8ca8204c1c67267056f41db6fc5d0de576bd38cb2e4e17de5c6e2e27fbf2634b9ac05c6e8b1b46efa74e21273fef02c9865354098c11343d8e295a40b3511185313c300f2fa13f493ac71fdb18807e9301301df3c43e0987e79869d332af6656eb2c1f06a3201c94b4751d5459dc304970fd4da5d7d82a187bda5c123c2a127b04884c614dd63b932ec07a6d80952aff8ee4140d2ace

已知明文开头

-----BEGIN SECRET MESSAGE-----

加密过程采用如下步骤

(1)明文分块
将明文转换为字节后,每 4 个字节分为一块
如果最后一块不足 4 字节,则使用 0x00 填充至 4 字节

(2)伪随机密钥流生成
使用线性同余发生器,LCG 的参数为:

  • 种子:未知
  • 乘数:a = 1103515245
  • 增量:c = 12345
  • 模数:m = 2^31

(3)异或加密
(4)输出密文

加解密流程分析

加密
  1. 明文数据 —> 转换为字节 —> 16进制数据
  2. 16进制数据进行分割 ----> 每4字节/每8位16进制数为一组 —> 不足4字节补齐0x00
  3. 每组数据与密钥进行异或加密
  4. 输出密文
解密
  1. 将已知明文开头转换为转换为字节,即16进制数据
  2. 明文开头与密文进行异或运算得到密钥的开始部分,注意密文同样需转为字节才能进行异或
  3. 根据线性同余算法,已知乘数、增量和模数,即可推出种子,进而生成完整密钥
  4. 密文与密钥进行异或得出完整密文

解密脚本

1. 定义已知参数与有用函数
import binascii

def hex_to_bytes(h):
    return binascii.unhexlify(h)

def xor_bytes(a, b):
    return bytes(x ^ y for x, y in zip(a, b))

plaintext_head = "-----BEGIN SECRET MESSAGE-----\n"  # 已知明文开头
ciphertext_hex = "13539cbb0b53c45070dd0957527c8ca8204c1c67267056f41db6fc5d0de576bd38cb2e4e17de5c6e2e27fbf2634b9ac05c6e8b1b46efa74e21273fef02c9865354098c11343d8e295a40b3511185313c300f2fa13f493ac71fdb18807e9301301df3c43e0987e79869d332af6656eb2c1f06a3201c94b4751d5459dc304970fd4da5d7d82a187bda5c123c2a127b04884c614dd63b932ec07a6d80952aff8ee4140d2ace"  # 你的密文(16进制字符串)

a = 1103515245  # 乘数
c = 12345       # 增量
m = 2**31       # 模数
2. 明文开头转换
# 步骤1: 将已知明文开头转换为转换为字节,即16进制数据
block0_plain = plaintext_head[:4].encode('utf-8')
3. 解出密钥开始部分
# 步骤2: 明文开头与密文进行异或运算得到密钥的开始部分
ciphertext_bytes = hex_to_bytes(ciphertext_hex)
block0_cipher = ciphertext_bytes[:4]
known_key_bytes = xor_bytes(block0_plain, block0_cipher)

# 将4字节密钥转换为整数,即为加密时第0块使用的种子,即X_{0}
state = int.from_bytes(known_key_bytes, 'big')
print("已知加密时第0块使用的种子为: ", state)
4. 解出完整密钥,并与密文异或得到明文
# 每4字节为一个块进行解密,不足4字节的块填充0
decrpted = b""
num_blocks = len(ciphertext_bytes) // 4
if len(ciphertext_bytes) % 4 != 0:
    num_blocks += 1

for i in range(num_blocks):
    # 步骤3: 使用线性同余生成器生成密钥
    if i == 0:
        # 如果是第一个块,使用已知的密钥前几字节
        key = known_key_bytes
    else:
        state = (a * state + c) % m
        key = state.to_bytes(4, 'big')  # 每次生成4字节
    start = i * 4
    end = start + 4
    block = ciphertext_bytes[start:end]

    # 如果最后一个块不足4字节,填充0
    if len(block) < 4:
        block += b'\x00' * (4 - len(block))

    # 步骤4: 使用密钥对密文块进行异或解密得到明文
    plaintext_block = xor_bytes(block, key)
    decrpted += plaintext_block

plaintext = decrpted.decode('utf-8', errors='replace')
print("解密结果: ", plaintext)

解密结果

执行解密脚本,成功完成解密,拿到flag{wncp_0956}

参考链接


网站公告

今日签到

点亮在社区的每一天
去签到