NepCTF-MISC-WP

客服小美

1
2025年的一个午后,客服小美满怀期待地点开了那封标题为“关于2025年部分节假日安排”的邮件,结果嘛……你懂的,套路来了!作为应急响应界的“技术侦探”,现在轮到你出手啦!你的任务是找出被控机器的用户名、揪出那个偷偷通信的钓鱼木马地址,顺便看看有没有啥敏感信息被顺走。快来动动脑,展现你破案如神的本领吧!flag格式例如:NepCTF{xiaomei_8.8.8.8:11451_secret}

中规中矩的内存取证+流量分析题

在系统信息中得知被控机器的用户名 JohnDoe

image-20250728210618776

根据题目描述可知,应该是点击了恶意软件导致的,查找网络连接信息,得知钓鱼木马地址为192.168.27.132:12580

image-20250728210756864

这俩很快就能找到,偷走的敏感信息估计在流量中

image-20250728211258451

打开流量文件,根据流量特征和GET路由/TJvI判断为Cobalt Strike流量

由于找不到.cobaltstrike.beacon_keys文件,无法正常找寻密钥,遂只能从内存中寻找,将进程的内存dump出来(vol3和memprocfs都可,不过在内存的索引上有所区别)使用下边的脚本结合流量内容在dump中寻找AES key和HMAC key

DidierStevensSuite/cs-extract-key.py at master · DidierStevens/DidierStevensSuite

在流量的流9中从钓鱼木马地址接受的指令(其他的流除了接收的任务就是发送的信息,这个需要一个个找一下,比较耗时间)

image-20250728213706567

使用工具破解密钥

1
python cs-extract-key.py -t 0253784ee86d3fc54693bb7ee14f40d64700446a4604ca0054103ba84e1a831d2a369c501e2a2522abdd9f5fe7652a16fd242669f6b10fb52e8b2b032a7ae00f6b25a8cecdffde72dadf1a18c1225f92 pid.6492.dmp

vol3dump出的:

image-20250728215957398

memprocfs dump出的:

image-20250728213444937

内容一样,positions不一样罢了

同样将其对应的响应也跑一下

1
python cs-extract-key.py -c 00000050350ca7f4379f30cc9d6d671db886d360691c74467156e60e8356725ae2f3b880b302ea8b5556df10324e86e53ecb84046646a1758e9cb8c7fca42d660617be467627abcc3c0ce3bd3e93c02fffcb4d3a pid.6492.dmp

image-20250728215652424

可确定密钥

1
2
AES Key:  a6f4a04f8a6aa5ff27a5bcdd5ef3b9a7
HMAC Key: 35d34ac8778482751682514436d71e09

再使用这个项目下的脚本进行解密CS_Decrypt/CS_Task_AES_Decrypt.py at main · WBGlIl/CS_Decrypt

把接受的指令转成base64

image-20250728220856600

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
'''
cobaltstrike任务解密
'''
import hmac
import binascii
import base64
import struct

import hexdump
from Crypto.Cipher import AES

def compare_mac(mac, mac_verif):
if mac == mac_verif:
return True
if len(mac) != len(mac_verif):
print
"invalid MAC size"
return False

result = 0

for x, y in zip(mac, mac_verif):
result |= x ^ y

return result == 0


def decrypt(encrypted_data, iv_bytes, signature, shared_key, hmac_key):
if not compare_mac(hmac.new(hmac_key, encrypted_data, digestmod="sha256").digest()[0:16], signature):
print("message authentication failed")
return

cypher = AES.new(shared_key, AES.MODE_CBC, iv_bytes)
data = cypher.decrypt(encrypted_data)
return data


def readInt(buf):
return struct.unpack('>L', buf[0:4])[0]

# 接收到的任务数据
shell_whoami= "AlN4TuhtP8VGk7t+4U9A1kcARGpGBMoAVBA7qE4agx0qNpxQHiolIqvdn1/nZSoW/SQmafaxD7UuiysDKnrgD2slqM7N/95y2t8aGMEiX5I=" ##换成对应的,我已经更换

if __name__ == "__main__":
# key源自Beacon_metadata_RSA_Decrypt.py
SHARED_KEY = binascii.unhexlify("a6f4a04f8a6aa5ff27a5bcdd5ef3b9a7")##对应的
HMAC_KEY = binascii.unhexlify("35d34ac8778482751682514436d71e09")##对应的

enc_data = base64.b64decode(shell_whoami)
print("数据总长度:{}".format(len(enc_data)))
signature = enc_data[-16:]
encrypted_data = enc_data[:-16]

iv_bytes = bytes("abcdefghijklmnop",'utf-8')

dec = decrypt(encrypted_data,iv_bytes,signature,SHARED_KEY,HMAC_KEY)

counter = readInt(dec)
print("时间戳:{}".format(counter))

decrypted_length = readInt(dec[4:])
print("任务数据包长度:{}".format(decrypted_length))

data = dec[8:len(dec)]
print("任务Data")
print(hexdump.hexdump(data))

# 任务标志
Task_Sign=data[0:4]
print("Task_Sign:{}".format(Task_Sign))

# 实际的任务数据长度
Task_file_len = int.from_bytes(data[4:8], byteorder='big', signed=False)
print("Task_file:{}".format(Task_file_len))

with open('data.bin', 'wb') as f:
f.write(data[8:Task_file_len])

print(hexdump.hexdump(data[Task_file_len:]))

image-20250728220802156

  • %COMPSPEC%
    • 作用:Windows系统环境变量,通常解析为 C:\Windows\System32\cmd.exe
    • 攻击意图:确保在任何Windows系统上都能定位到命令行解释器(绕过路径限制)
  • /C:执行命令后关闭cmd进程(隐蔽性)
  • type secret.txt:读取secret.txt文件内容并输出到终端
  • 读取敏感文件secret.txt,C2服务器通过Beacon接收命令输出(文件内容)

同样把发送的秘密也转成base64

image-20250728220550125

然后用同一项目下的返回解密脚本解密返回内容CS_Decrypt/Beacon_Task_return_AES_Decrypt.py at main · WBGlIl/CS_Decrypt

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# -*- coding: utf-8 -*-

'''
Beacon任务执行结果解密
'''
import hmac
import binascii
import base64
import struct
import hexdump
from Crypto.Cipher import AES

def compare_mac(mac, mac_verif):
if mac == mac_verif:
return True
if len(mac) != len(mac_verif):
print
"invalid MAC size"
return False

result = 0

for x, y in zip(mac, mac_verif):
result |= x ^ y

return result == 0

def decrypt(encrypted_data, iv_bytes, signature, shared_key, hmac_key):
if not compare_mac(hmac.new(hmac_key, encrypted_data, digestmod="sha256").digest()[0:16], signature):
print("message authentication failed")
return

cypher = AES.new(shared_key, AES.MODE_CBC, iv_bytes)
data = cypher.decrypt(encrypted_data)
return data

# key源自Beacon_metadata_RSA_Decrypt.py
SHARED_KEY = binascii.unhexlify("a6f4a04f8a6aa5ff27a5bcdd5ef3b9a7")
HMAC_KEY = binascii.unhexlify("35d34ac8778482751682514436d71e09")

encrypt_data="AAAAUDUMp/Q3nzDMnW1nHbiG02BpHHRGcVbmDoNWclri87iAswLqi1VW3xAyToblPsuEBGZGoXWOnLjH/KQtZgYXvkZ2J6vMPAzjvT6TwC//y006"

encrypt_data=base64.b64decode(encrypt_data)

encrypt_data_length=encrypt_data[0:4]

encrypt_data_length=int.from_bytes(encrypt_data_length, byteorder='big', signed=False)

encrypt_data_l = encrypt_data[4:len(encrypt_data)]

data1=encrypt_data_l[0:encrypt_data_length-16]
signature=encrypt_data_l[encrypt_data_length-16:encrypt_data_length]
iv_bytes = bytes("abcdefghijklmnop",'utf-8')

dec=decrypt(data1,iv_bytes,signature,SHARED_KEY,HMAC_KEY)


counter = dec[0:4]
counter=int.from_bytes(counter, byteorder='big', signed=False)
print("counter:{}".format(counter))

dec_length = dec[4:8]
dec_length=int.from_bytes(dec_length, byteorder='big', signed=False)
print("任务返回长度:{}".format(dec_length))

de_data= dec[8:len(dec)]
Task_type=de_data[0:4]
Task_type=int.from_bytes(Task_type, byteorder='big', signed=False)
print("任务输出类型:{}".format(Task_type))

# print(de_data[4:dec_length].decode('utf-8'))
print(de_data[4:dec_length])

print(hexdump.hexdump(dec))

image-20250728221723581

最终得到flag

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

NepBotEvent

1
2
3
最近总觉得NepBot不对劲,邀请函生成速度慢也就算了,以至于/home/Nepnep/目录下都被创建了flag.txt,吓得他赶紧拔网线跑路。经过初步排查,Neper在他的机器上发现了一个神秘的键盘记录器(Keylogger)残留痕迹!

虽然恶意程序已被清除,但攻击者究竟掌握了哪些敏感信息?NepBOT的账号有没有被窃?他的“数据库”是不是也暴露了?请你协助分析泄露的数据库名。flag格式例如:NepCTF{数据库名}

首先打开所给的压缩包时,注意到

image-20250730095505759

可见该文件的系统是unix

7z打开还可以发现这种东西

image-20250730095926524

可见确切的说是macos系统的记录键盘的文件,拷打ai给出脚本

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import os
import sys
import struct

# 键盘映射(标准美式键盘布局)
KEY_MAP = {
# 字母键
30: 'a', 31: 's', 32: 'd', 33: 'f', 34: 'g', 35: 'h', 36: 'j',
37: 'k', 38: 'l', 39: ';', 40: '\'', 16: 'q', 17: 'w', 18: 'e',
19: 'r', 20: 't', 21: 'y', 22: 'u', 23: 'i', 24: 'o', 25: 'p',
26: '[', 27: ']', 43: '\\', 44: 'z', 45: 'x', 46: 'c', 47: 'v',
48: 'b', 49: 'n', 50: 'm', 51: ',', 52: '.', 53: '/',

# 数字键
2: '1', 3: '2', 4: '3', 5: '4', 6: '5', 7: '6', 8: '7',
9: '8', 10: '9', 11: '0', 12: '-', 13: '=',

# 功能键
28: '\n', # Enter
14: '', # Backspace(特殊处理)
15: ' ', # Tab (4个空格)
57: ' ', # Space
1: '', # ESC(忽略)
}

# Shift状态下的特殊字符映射
SHIFT_MAP = {
'1': '!', '2': '@', '3': '#', '4': '$', '5': '%',
'6': '^', '7': '&', '8': '*', '9': '(', '0': ')',
'-': '_', '=': '+', '[': '{', ']': '}', '\\': '|',
';': ':', '\'': '"', ',': '<', '.': '>', '/': '?',
'`': '~'
}

def extract_keyboard_input(input_file):
"""
从键盘记录文件中提取输入内容
格式: struct input_event (qqHHI)
每个事件 24 字节
"""
text = [] # 存储最终文本
buffer = [] # 文本缓冲区(支持回退)
shift_down = False # Shift键状态
ctrl_down = False # Ctrl键状态
alt_down = False # Alt键状态

event_size = struct.calcsize("qqHHI") # 24 字节
char_count = 0

with open(input_file, "rb") as f:
while True:
data = f.read(event_size)
if not data:
break

try:
# 解析事件结构:秒,微秒,事件类型,键码,键值
sec, usec, ev_type, code, value = struct.unpack("qqHHI", data)

# 只处理按键事件(EV_KEY=1)和按键按下(value=1)
if ev_type != 1 or value != 1:
continue

# 处理控制键状态
if code in {42, 54}: # 左右Shift键
shift_down = True
continue
elif code in {29, 97}: # 左右Ctrl键
ctrl_down = True
continue
elif code in {56, 100}: # 左右Alt键
alt_down = True
continue

# 忽略Ctrl、Alt组合键
if ctrl_down or alt_down:
continue

# 处理字符键
if code in KEY_MAP:
char = KEY_MAP[code]

# 处理退格键
if code == 14: # Backspace
if buffer:
buffer.pop()
continue

# 应用Shift修饰
if shift_down:
# 字母大写
if char.isalpha():
char = char.upper()
# 特殊字符转换
elif char in SHIFT_MAP:
char = SHIFT_MAP[char]

# 添加字符到缓冲区
buffer.append(char)
char_count += 1

# 每处理100个字符显示一次进度
if char_count % 100 == 0:
sys.stderr.write(f"\r已处理字符: {char_count}")

# 重置Shift状态(单个按键处理)
shift_down = False

except struct.error:
# 跳过格式错误的事件
continue

# 将缓冲区内容组合成字符串
return ''.join(buffer)

if __name__ == "__main__":
if len(sys.argv) != 2:
print("键盘输入内容提取工具")
print(f"用法: {sys.argv[0]} <键盘记录文件>")
print("示例: python extract_keys.py keylog.bin")
sys.exit(1)

input_file = sys.argv[1]

if not os.path.exists(input_file):
print(f"错误: 文件不存在 {input_file}")
sys.exit(1)

try:
# 提取并打印输入内容
extracted_text = extract_keyboard_input(input_file)
print("\n\n输入的文本内容:")
print("=" * 80)
print(extracted_text)
print("=" * 80)
print(f"\n总字符数: {len(extracted_text)}")

except Exception as e:
print(f"处理错误: {str(e)}")
sys.exit(1)

image-20250730104141856

但交上去不对,看一下代码发现有点错误,对与大小写的判定逻辑有错误,遂Ctf应为CTF(毕竟大小写转换只按一次),修改代码后就对了

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
import os
import sys
import struct

# 键盘映射(标准美式键盘布局)
KEY_MAP = {
# 字母键
30: 'a', 31: 's', 32: 'd', 33: 'f', 34: 'g', 35: 'h', 36: 'j',
37: 'k', 38: 'l', 39: ';', 40: '\'', 16: 'q', 17: 'w', 18: 'e',
19: 'r', 20: 't', 21: 'y', 22: 'u', 23: 'i', 24: 'o', 25: 'p',
26: '[', 27: ']', 43: '\\', 44: 'z', 45: 'x', 46: 'c', 47: 'v',
48: 'b', 49: 'n', 50: 'm', 51: ',', 52: '.', 53: '/',

# 数字键
2: '1', 3: '2', 4: '3', 5: '4', 6: '5', 7: '6', 8: '7',
9: '8', 10: '9', 11: '0', 12: '-', 13: '=',

# 功能键
28: '\n', # Enter
14: '🔙', # Backspace标记(使用特殊字符代替实际删除)
15: ' ', # Tab (4个空格)
57: ' ', # Space
}

# Shift状态下的特殊字符映射
SHIFT_MAP = {
'1': '!', '2': '@', '3': '#', '4': '$', '5': '%',
'6': '^', '7': '&', '8': '*', '9': '(', '0': ')',
'-': '_', '=': '+', '[': '{', ']': '}', '\\': '|',
';': ':', '\'': '"', ',': '<', '.': '>', '/': '?',
'`': '~'
}

def extract_keyboard_input(input_file):
"""
从键盘记录文件中提取输入内容
格式: struct input_event (qqHHI)
每个事件 24 字节
"""
text = [] # 存储最终文本
key_state = {} # 记录每个键的状态(1=按下,0=抬起)
shift_down = False # Shift键状态
caps_lock = False # CapsLock状态
ctrl_down = False # Ctrl键状态
alt_down = False # Alt键状态
backspace_count = 0 # 退格键统计
event_count = 0

event_size = struct.calcsize("qqHHI") # 24 字节

with open(input_file, "rb") as f:
while True:
data = f.read(event_size)
if not data:
break

try:
# 解析事件结构:秒,微秒,事件类型,键码,键值
sec, usec, ev_type, code, value = struct.unpack("qqHHI", data)
event_count += 1

# 只处理按键事件(EV_KEY=1)
if ev_type != 1:
continue

# 更新所有键的状态
key_state[code] = value

# 特殊键处理
if code in {42, 54}: # 左右Shift键
shift_down = key_state.get(42, 0) or key_state.get(54, 0)
elif code == 58: # CapsLock键
if value == 1: # 只响应按下事件
caps_lock = not caps_lock
elif code in {29, 97}: # 左右Ctrl键
ctrl_down = key_state.get(29, 0) or key_state.get(97, 0)
elif code in {56, 100}: # 左右Alt键
alt_down = key_state.get(56, 0) or key_state.get(100, 0)

# 只处理按键按下事件(值=1)
if value != 1:
continue

# 控制键修饰符被按下时不输出字符
if code in {42, 54, 29, 97, 56, 100, 58}:
continue

# 忽略Ctrl、Alt组合键
if ctrl_down or alt_down:
continue

# 处理特殊按键
if code == 14: # Backspace
if text: # 如果文本不为空
text.pop()
else:
backspace_count += 1
continue

# 正常按键处理
if code in KEY_MAP:
char = KEY_MAP[code]

# 处理大小写转换
if char.isalpha(): # 字母键
# 判断大写规则:CapsLock XOR Shift (XOR 实现开关效果)
if caps_lock != shift_down: # 不同则为大写
char = char.upper()
else: # 其他键
# 应用Shift修饰
if shift_down and char in SHIFT_MAP:
char = SHIFT_MAP[char]

# 添加字符到结果
text.append(char)

# 显示处理进度
if event_count % 100 == 0:
sys.stderr.write(f"\r已处理事件: {event_count}")

except struct.error as e:
sys.stderr.write(f"\n解包错误: {str(e)}")
continue

# 如果有未处理的退格键
if backspace_count > 0:
text.append(f" 🚫 (额外退格键: {backspace_count})")

# 返回最终文本
return ''.join(text)

def clean_output(text):
"""清理最终输出,将退格标记替换为实际删除操作"""
cleaned = []
for char in text:
if char == '🔙':
if cleaned: # 如果结果不为空
cleaned.pop()
else:
cleaned.append(char)
return ''.join(cleaned)

if __name__ == "__main__":
if len(sys.argv) != 2:
print("键盘输入内容提取工具")
print(f"用法: {sys.argv[0]} <键盘记录文件>")
print("示例: python extract_keys.py keylog.bin")
print("提示: 结果会显示原始输入和清理后的内容")
sys.exit(1)

input_file = sys.argv[1]

if not os.path.exists(input_file):
print(f"错误: 文件不存在 {input_file}")
sys.exit(1)

try:
# 提取键盘输入
sys.stderr.write("\n处理键盘记录文件...\n")
extracted_text = extract_keyboard_input(input_file)
cleaned_text = clean_output(extracted_text)

# 输出结果
print("\n\n原始输入内容 (包含特殊标记):")
print("=" * 80)
print(extracted_text)
print("=" * 80)
print(f"\n原始字符数: {len(extracted_text)}")

print("\n\n清理后的输入内容:")
print("=" * 80)
print(cleaned_text)
print("=" * 80)
print(f"\n最终字符数: {len(cleaned_text)}")
print(f"处理的事件总数: {event_count}")

except Exception as e:
print(f"\n处理错误: {str(e)}")
sys.exit(1)

image-20250730104534432

NepCTF{NepCTF-20250725-114514}

SpeedMino

1
2
3
Welcome to SpeedMino! Reach 2600.00 to get FLAG

Also, there is a SECRET FLAG you need to REVERSE it.

经典俄罗斯方块,大概可以用cheat engine破解

游玩时发现北京有一串字符变化,猜测是在解密之类的,遂加快速度,cheat engine

image-20250730114244836

速度改为100000000000000000000000000000,马上出现flag

image-20250730114109517

把exe文件解压后发现使用lua写的,根据给的代码也能拷打ai给出解密脚本

Nepsign

拷打gemini2.5 pro得到脚本

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
from pwn import *
from gmssl import sm3
from ast import literal_eval
import os

# --- Server Code (for local simulation/understanding) ---
# It's good practice to have the same functions the server uses.

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

def SM3_n(data, n=1, bits=256):
# This check is important. If n is 0, don't hash at all.
if n == 0:
return data.hex()[:bits // 4]

# Start with the first hash
data = bytes.fromhex(SM3(data))

# Perform remaining n-1 hashes
for _ in range(n - 1):
data = bytes.fromhex(SM3(data))

return data.hex()[:bits // 4]

# Function to calculate the 'step' array for a given message
def get_steps(msg):
n = 256
hex_symbols = '0123456789abcdef'
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(n // 8)]

step = [0] * 48

# First 32 steps from hash bytes
for i in range(32):
step[i] = a[i]

# Next 16 steps from hex character positions
sum_vals = [0] * 16
for i in range(16):
sum_vals[i] = 0
for j in range(1, 65):
if m[j - 1] == hex_symbols[i]:
sum_vals[i] += j
step[i + 32] = sum_vals[i] % 255

return step

# --- Exploit Logic ---

# Connect to the remote server using SSL
# THIS IS THE CORRECTED LINE:
p = remote('nepctf31-gh91-s9zp-2ir1-edtxxgzbw809.nepctf.com', 443, ssl=True)

p.recvuntil(b'initializing...\n')

# 1. Calculate the 'step' array for the target message
target_msg = b'happy for NepCTF 2025'
target_steps = get_steps(target_msg)
print(f"[*] Target steps calculated: {target_steps}")

final_qq = [None] * 48
nonce = 0

# 2. Forge each of the 48 signature parts
for i in range(48):
print(f"[*] Forging part {i+1}/48...")

# Find a message that results in step[i] == 0
while True:
# Using a slightly more random-looking nonce to avoid potential server-side caching
oracle_msg = f"nepforge-{nonce}".encode()
oracle_steps = get_steps(oracle_msg)

if oracle_steps[i] == 0:
print(f" [+] Found oracle message '{oracle_msg.decode()}' for part {i+1}")
break
nonce += 1

# Get the signature for the oracle message
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'msg: ', oracle_msg.hex().encode())

response = p.recvline().strip().decode()
oracle_qq = literal_eval(response)

# We only need the i-th part of this signature
base_qq_part = bytes.fromhex(oracle_qq[i])

# "Advance" this signature part to match the target step
hashes_to_apply = target_steps[i]
final_qq_part = SM3_n(base_qq_part, hashes_to_apply)

final_qq[i] = final_qq_part
print(f" [+] Forged part {i+1} successfully.")

print("\n[*] All signature parts forged. Assembling final signature.")
# The server expects a list of hex strings
final_qq_str_list = [part for part in final_qq]

# 3. Submit the forged signature to get the flag
p.sendlineafter(b'> ', b'2')
p.sendlineafter(b'give me a qq: ', str(final_qq_str_list).encode())

# Print the flag
flag = p.recvall()
print("\n[+] Flag:", flag.decode())

p.close()

image-20250730165835854

NepCTF{c40fa5e8-ce85-2b84-7102-76b96d1e2596}