yomo40のblog

Blog

Some writeups and everyday stuff

Blog

2026-3-14-软件系统安全赛-writeup_partly

队伍名:xxxx

Reverse

re1

有video.mp4与loader elf初始化运行后生成stager.pyc image

Cat stager.pyc image

字节较短,默认为混淆项

在loader中发现大量base64 image

Objdump看到主要流程

检查video.mp4存在,解码内置base64写出stager.pyc并chmod 0755

拼接命令python3 <script> video.mp4

并利用sysetm()执行利用loader中的base64提取pyc image

反编译

image

分析,有xor。逆向脚本应该从附件提供的cideo解析bit串并异或0XAA image

得到一个elf

用ida打开看下字符串 image image

得到关键数组,但是暂时不清楚方式,在虚拟机里跑一下 image

提取md5并解析得到flag(.data段而非.rodata)

本题完

re2

1.看pe结构,入口0x4156c0

image

在4158F9下断点 image

单步跳转 image

得到ascii值NGeQwv8eCRpINEcO

验证无误 image image

在%temp%下发现文件 image

重命名后运行看下

image

丢ida image

观察得到

32key

image

16iv

image

起始密文

image

写个脚本得到flag

(

BP_MAIN = 0x401550

DEC_FUNC = 0x404D80

KEY_OFF, IV_OFF, CT_OFF = 0x01, 0x21, 0x31

)

image

re3

分析题得,pcap里有传出密文,需要解析程序加密逻辑

Die看到 image

Ida里发现 image

尝试找CArchive cookie image

python -c “from pathlib import Path; b=Path(‘client’).read_bytes(); m=b’MEI\x0c\x0b\x0a\x0b\x0e’; print(b.rfind(m))”

得到偏移量 image

解析 TOC 并提取 client与crypt_core.so

image

image

把提取到的crypt_core.so丢ida

image

找到encode关键字

有点像SM4但是不太标准

分析下认为轮数是24而非默认32

常量表(SBOX/FK/CK)来自 rodata,可以用little-endian word解释

再回来看client 

提取到逻辑_obf_exec(base64.b85decode(超长字符串).decode())

B85解码后继续

  1. base64.b85decode
  2. (k1, k2, rn)循环 XOR
  3. 字母做反向 Caesar 偏移(-rn)
  4. 数字也做 -rn 的模 10 偏移

是ython marshal code object,非原生elf

有crypt_core.encode_data(plaintext, key[:16])

KEY_B64 = “eUYme4MkN1KSC1bWJZJ2w3FUJCiEXT13D2u1KmiNtfhXKZYE”

FILES_TO_SEND = [“readme.txt”, “flag.txt”, “config.txt”]

encrypt_file=crypt_core.encode_data(plaintext, key[:16])

把这些提取出来写脚本

image

得flag

题完

Misc

misc1

附件缺失文件头 image

但是通过27 1c 判断为7z文件,补全解压得到一个png,随波逐流提示rgb 位隐写zip image image

Stegsolve提取出来有flag.zip与pass*.zip(*=1~4)

因为crc32对固定4字节可逆;所以可以直接从 CRC 反推出明文

image

Flag.zip 密码为c1!xxtLf%fXYPkaA

尝试解压得到零宽

image

发现不是正常的零宽解码

观察一下发现均为0x200c与0x200b

尝试替换为01

image

 U+200B=0, U+200C=1条件下得到flag

题完

Crypto

rsa

1.审计generate-plaintexts.py

S = int.from_bytes(secret_bytes, 'big')
ki = S % di
share_str = f"{di:x}:{ki:x}:{original_bits:x}"

plaintext-j.txt的规律

第 1 行:message1

第 2~10 行:分别是 message2-0 的第 j 份 share。

  1. encrypt.py

n_bits >= 2048:RSA-OAEP(32-byte key) + AES-GCM

n_bits < 2048:原 pow(m, e, n)``(16-byte key) + AES-GCM

3.思路

先解rsa

拿到plaintext-j一定量后再去解message

4.统计key-*.pem的(n_bits, e)并两次最大公约数

发现key1与key2共用1024bits因子

Key4与key15共用262bits因子

  1. 共享素数攻击

计算两个模数 Na、Nb 的最大公约数得到 p

用 Na / p 得到另一个素数 qa。

用 Nb / p 得到另一个素数 qb。

计算 φ(n) = (p−1)(q−1)。

计算 私钥指数 d = e 的模 φn逆元。

得到key-1, key-2, key-4, key-15的私钥

6.对可疑大 e 的 key 跑 Wiener,得到key6/12/17

分支 A (n_bits>=2048):header=RSA_OAEP(cipherkey)

分支 B (n_bits<2048):header=pow(key,e,n)

解密成功映射:

 ciphertext-2 -> key-6

 ciphertext-3 -> key-4

 ciphertext-5 -> key-17

 ciphertext-8 -> key-1

因此拿到4 个 plaintext-j

对每个 messagei (i=2..10),从 4 份明文中取 4 个同余:

Si ≡ k{i,1} (mod d_{i,1})

Si ≡ k{i,4} (mod d_{i,4})

然后 CRT 合并得 S_i,再按 bits转字节串。

均满足M_bits > original_bits

故4份ciphertext-j就可以恢复message image

密码为9Zr4M1ThwVCHe4nHnmOcilJ8

解压得到lv2

8.看task.py分析

随机生成 512-bit 素数p, q和 180-bit 小素数d

lam = lcm(p-1, q-1),e = inverse(d, lam)

c1 = m1^e mod n,其中 m1 = b”Secret message: ” + b”A”*16

计算 flag_hash = sha256(str(p+q)),并输出 flag = “next pass is {flag_hash}”

给出 n, e, c1, c2, c3 和掩蔽后的多项式系数

  1. 枚举小整数 t( 1 400)

对 t*e/n`做连分数并取每个收敛分数 k/d

用已知明文快速验真:检查 pow(c1, d, n) == m1

得到t = 4

d = 1022911946105965821118646394713728017371180638538072123(180-bit)

10.由ed-1分解n

成功得到p = 9107629749578703007521753435128188255997152813554367948373413785978810344628038189563213036161837882971963700600044412434325055960560499599730973768232229

q = 10932961240863053140021227333995503042196506533433133299916502568289020826078008576745192725272664686384293526720057562101790406102729886343233171385110689`

p+q = 20040590990441756147542980769123691298193659346987501248289916354267831170706046766308405761434502569356257227320101974536115462063290385942964145153342918

计算python

password = sha256(str(p+q).encode()).hexdigest()

image

利用密码解压lv3

10.分析task.py

注意到

leak = ( (p _ CONST1) ^ (q _ CONST2) ^ ((p & q) << 64) ^ ((p | q) << 48) ^ ((p ^ q) _ CONST3) ) + ((p + q) % MOD128) ^ ((p _ q) & MASK64)

但是python语言中+优先级高于^

故这里实际上是leak = (A + B) ^ C

这里的

A = (pCONST1) ^ (qCONST2) ^ ((p&q)<<64) ^ ((p|q)<<48) ^ ((p^q)*CONST3)

B = (p+q) mod 2^128

B = (p+q) mod 2^64

且p*q = n已知

所以C = n mod 2^64是已知常数

令L = leak ^ CL= A + B

image

题目完

Comments