比赛进行的两天还在外地,这几天打算做一遍题目,题目很好啊,可惜我好菜😶

复现复现复现

Crypto

Nepsign

sm3参考链接:https://blog.csdn.net/qq_40662424/article/details/121637732

SM3是一种由中国国家密码管理局公布的商用密码杂凑算法,与SHA-256安全相当,四个步骤:消息填充、消息扩展、迭代压缩、输出结果。消息分组长度为512位,摘要长度为256位

分析server.py

1
2
3
4
def SM3(data):
d = [i for i in data]
h = sm3.sm3_hash(d)
return h

用于计算输入数据的 SM3 哈希值 的 Python 函数,基于gmssl库

1
2
3
4
def SM3_n(data, n=1, bits=256):
for _ in range(n):
data = bytes.fromhex(SM3(data))
return data.hex()[:bits // 4]

SM3_n 是一个 迭代哈希计算函数,它对输入数据进行n次 SM3 哈希运算,并最终返回指定位数的哈希值

Nepsign类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class Nepsign():
def __init__(self):
self.n = 256
self.hex_symbols = '0123456789abcdef' # 16进制字符集
self.keygen() # 生成密钥对

def keygen(self):
rng = SystemRandom()
self.sk = [rng.randbytes(32) for _ in range(48)]
self.pk = [SM3_n(self.sk[_], 255, self.n) for _ in range(48)]
return self.sk, self.pk

def sign(self, msg, sk=None):
sk = sk if sk else self.sk
m = SM3(msg)
m_bin = bin(int(m, 16))[2:].zfill(256)
a = [int(m_bin[8 * i: 8 * i + 8], 2) for i in range(self.n // 8)]
step = [0] * 48;
qq = [0] * 48
for i in range(32):
step[i] = a[i]
qq[i] = SM3_n(sk[i], step[i])
sum = [0] * 16
for i in range(16):
sum[i] = 0
for j in range(1, 65):
if m[j - 1] == self.hex_symbols[i]:
sum[i] += j
step[i + 32] = sum[i] % 255
qq[i + 32] = SM3_n(sk[i + 32], step[i + 32])
return [i for i in qq]

def verify(self, msg, qq, pk=None):
qq = [bytes.fromhex(i) for i in qq]
pk = pk if pk else self.pk
m = SM3(msg)
m_bin = bin(int(m, 16))[2:].zfill(256)
a = [int(m_bin[8 * i: 8 * i + 8], 2) for i in range(self.n // 8)]
step = [0] * 48;
pk_ = [0] * 48
for i in range(32):
step[i] = a[i]
pk_[i] = SM3_n(qq[i], 255 - step[i])
sum = [0] * 16
for i in range(16):
sum[i] = 0
for j in range(1, 65):
if m[j - 1] == self.hex_symbols[i]:
sum[i] += j
step[i + 32] = sum[i] % 255
pk_[i + 32] = SM3_n(qq[i + 32], 255 - step[i + 32])
return True if pk_ == pk else False

密钥生成keygen函数:使用随机数生成器生成48个32字节的私钥,对每个私钥进行255次SM3哈希得到公钥

签名函数sign:计算msg的SM3哈希并转化为256位二进制,将哈希分成32个8位组,前32个签名元素使用消息哈希的每个字节值作为迭代次数对私钥进行哈希,后16个签名元素计算每个十六进制字符在哈希中的位置和,取模255作为迭代次数,返回签名数组

验证函数verify:对签名进行”补足”哈希(255-step次),应能得到公钥,比较计算出的公钥和存储的公钥是否一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
print('initializing...')
Sign = Nepsign() # 创建Nepsign实例,自动生成密钥对
while 1:
match int(input('> ')):
case 1:
msg = bytes.fromhex(input('msg: '))
if msg != b'happy for NepCTF 2025':
print(Sign.sign(msg))
else:
print("You can't do that")
case 2:
qq = literal_eval(input('give me a qq: '))
if Sign.verify(b'happy for NepCTF 2025', qq):
print(flag)

实现了一个交互式的签名验证系统,选项一进行签名功能,但是不能对“happy for NepCTF 2025”签名,选项二验证happy for NepCTF 2025的签名,成功则会得到flag

看这篇wp的攻击思路:https://blog.csdn.net/2401_87558262/article/details/149733054

  1. 目标
    伪造消息 b'happy for NepCTF 2025' 的签名 qq,使得 verify 验证通过,从而获取flag。
  2. 关键
    • 验证时计算:pk_[i] = SM3_n(qq[i], 255 - a[i]),要求 pk_[i] == pk[i]
    • 公钥生成时:pk[i] = SM3_n(sk[i], 255)
    • a[i] = 0,则 qq[i] = sk[i] 时验证必然通过(因为 SM3_n(sk[i], 255) = pk[i]
  3. 攻击路径
    • 通过其他消息的签名泄露 sk[i](当 a[i] = 0 时,qq[i] = sk[i]
    • 组合所有 sk[i] 构造目标签名。

compute_steps(msg)计算目标消息的hash参数a[i]和sum[i]

collect_secrets(conn, msg_target, step_target)大量发送随机消息,找到满足a[i]=0的消息,此时该消息的签名 qq[i] 就是 sk[i](因为 SM3_n(sk[i], 0) = sk[i]),重复直到找到48个sk[i]

forge_signature(secret_keys, step_ref)伪造目标签名,对每个 sk[i] 计算 SM3_n(sk[i], a_target[i]),得到目标签名 qq[i]。验证时SM3_n(qq[i], 255 - a_target[i]) = SM3_n(sk[i], 255) = pk[i],必然通过。

ps:https://faritree.top/2025/07/28/NepCTF2025-Crypto-WP/这里是密码学全部的wp(tql😮,看到这道题很棒的思路

1
2
签名由48个哈希值组成,当私钥相同时,每个位置的哈希值只与当前位置和step[i]有关,step[i]由签名的消息确定。所以先求出happy for NepCTF 2025的step,然后随机选取一些消息发送给服务器,从返回的签名中拿到(i,step[i])对应的哈希值,需要的签名都拿到后,发给服务器就行。
----出自FariTree的wp

Re

realme

根据Liv师傅的wp复现

反调试没怎么做过,认识了scyllahide插件(也许该系统学一下🤔

image-20250802103527546

在main函数下面发现了sub_401A60,是一个rc4加密的变形函数,但是没有被调用,应该有反调试,使用Scyllahide插件一键过反调试,动调断点该处代码,发现被调用

有点问题…😣

Misc

客服小美

根据https://dwd.moe/post/nepctf-2025复现

vol2教程https://blog.csdn.net/m0_68012373/article/details/127419463

image-20250803094605757

得到内存镜像的操作系统Win10x64_19041

查看内存进程,发现可疑进程

image-20250803095241245

把这个进程dump出来6492.dmp

分析流量,筛选出http流量

image-20250803102935913

image-20250803103016222

其实这里可以看出通信的ip和端口

CS流量解密可以看一篇博客https://blog.csdn.net/AomCC/article/details/133298604

这里我直接用 cs-extract-key.py 尝试提取密钥

image-20250803104104121

通过提取的密钥解密

python cs-parse-traffic.py -k 35d34ac8778482751682514436d71e09:a6f4a04f8a6aa5ff27a5bcdd5ef3b9a7 ./DESKTOP.pcapng

发现了用户名JohnDoe

image-20250803104529272

发现了secret.txt

image-20250803104625879

得到了flag NepCTF{JohnDoe_192.168.27.132:12580_5c1eb2c4-0b85-491f-8d50-4e965b9d8a43}

未完待续

参考的好多wp🤩(真强啊

https://blog.csdn.net/2401_87558262/article/details/149733054

https://www.cnblogs.com/LAMENTXU/articles/19007988

https://faritree.top/2025/07/28/NepCTF2025-Crypto-WP/

https://tkazer.github.io/2025/07/28/NepCTF2025/

https://2hi5hu.cn/archives/nepctf2025

https://dwd.moe/post/nepctf-2025