前言

这次省赛拿到了省二,大一的时候比较菜没有打进决赛,所以没有这个奖,这次补上了算圆梦了吧

但是,这次比赛只拿到了省二我还是不太甘心的,下次争取拿到省一!

写这个wp就当存一些题目,exp等。不得不说省赛的misc太怪了,初赛区块链但又不完全是,决赛又来域渗透,传统misc已经越来越少了。

全部wp可以在a1natas的公众号上看到,在此感谢a1natas的队友们。链接:第八届浙江省大学生网络与信息安全竞赛决赛 WriteUp By A1natas

MISC

什么密码

打开发现一个压缩包,我们先用随波逐流伪加密修复一下就可以打开了

1762590642373

然后发现一个png,zsteg一下发现

1762582847774

1
2
YXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/
OBCQN1ODbwx3KwihVQRwITNtWgBqKAChKf1eWgKeIQGjWAh2LQehJjKeKU0

换表base64,字母表前面补上一个“=”即可

1762582947895

easySteg0

打开发现有一张图片,010打开发现末尾有一个压缩包,还看到了stable

1763179868294

stable:FI0EHKRkclAYN/xvgim2XCUdSf8O6osJVPb+LZu5nyQjqGt49BDwhrz3pWTaeM17

64位猜测是base的字母表

末尾压缩包:

1763179961389

可以发现:DASCTF{noflaghere_ntfs},提醒我们ntfs

我们使用7z打开压缩包可以发现的确有ntfs流:

1763180037562

最后base解密

1763180081130

CRYPTO

SimpleLWE

拿到一个密文,我们可以发现最前面的都是一样的([443, 581, 158, 210, 623, 112, 122, 658, 347, 803, 862, 661, 729, 702, 660, 684],

1762583790588

全部替换掉

1762583856793

发现就只有112和622.

使用0和1替换掉,得到

1
010001000100000101010011010000110101010001000110011110110100110001100001011101000111010001101001011000110110010101011111010000110111001001111001011100000111010001101111010111110100100101110011010111110100100001100001011100100110010001111101

拿到flag

1762583929121

RSA_Common_Attack

使用轩禹CTF_RSA工具3.6.1工具,输入n,e1,e2,c1,c2

其中e1,e2输在一起,c1,c2在一起,用“,”分隔。

得到

1762584541799

在用m转字符即可

1762584577478

ez_stream

简单的rc4加密

解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cipher=[164,34,242,5,234,79,16,182,136,117,78,78,71,168,72,79,53,114,117]
key='love'

def rc4_keystream(key, length):
key_bytes = [ord(c) for c in key]
S = list(range(256))
j=0
for i in range(256):
j = (j + S[i] + key_bytes[i%len(key_bytes)]) % 256
S[i], S[j] = S[j], S[i]
i=j=0
keystream=[]
for _ in range(length):
i = (i+1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
keystream.append(S[(S[i]+S[j]) % 256])
return keystream

ks=rc4_keystream(key,len(cipher))
plain_bytes=[c^k for c,k in zip(cipher,ks)]
flag=''.join(chr(b) for b in plain_bytes)
print(flag)

1762585187784

base64

base64直接解码就行

1763181340948

简单数学题

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from Crypto.Util.number import long_to_bytes, inverse

S =15870713655456272818998868095126610389501417235762009793315127525027164306871912572802442396878309282140184445917718237547340279497682840149930939938364752
D =836877201325346306269647062252443025692393860257609240213263622058769344319275021861627524327674665653956022396760961371531780934904914806513684926008590
e = 65537
c = 24161337439375469726924397660125738582989340535865292626109110404205047138648291988394300469831314677804449487707306159537988907383165388647811395995713768215918986950780552907040433887058197369446944754008620731946047814491450890197003594397567524722975778515304899628035385825818809556412246258855782770070
p = (S + D) // 2
q = (S - D) // 2

n = p * q
phi = (p - 1) * (q - 1)

d = inverse(e, phi)

m = pow(c, d, n)

print("p =", p)
print("q =", q)
print("p>0:", p > 0, "q>0:", q > 0)
print("phi =", phi)
print("d =", d)
print("c^d mod n =", m)
flag_bytes = long_to_bytes(m)
print(flag_bytes.decode())

得到flag

1763180199411

AES

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import base64, hashlib
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad

def decrypt_aes_cbc(b64_ct, password, iv):
key = hashlib.md5(password.encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_CBC, iv.encode())
ct = base64.b64decode(b64_ct)
pt = unpad(cipher.decrypt(ct), AES.block_size)
return pt.decode('utf-8')

ciphertext = "H4vkfGfsU+qBEwaa7ea9gBkRcraMqbe4BGaxDb/9JG4zGleqT1VxyzGbDj/yuQn8"

flag = decrypt_aes_cbc(ciphertext, "Cryptography", "0123456789abcdef")
print(flag)

得到:

1763180253246

数据安全

check1

exp:

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
import re
import csv
import datetime
import pandas as pd

CHECK_CODE_MAP = {
0: '1', 1: '0', 2: 'X', 3: '9', 4: '8',
5: '7', 6: '6', 7: '5', 8: '4', 9: '3',
10: '2'
}
ID_WEIGHTS = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3,
7, 9, 10, 5, 8, 4, 2]

def id_valid_format(id_no: str) -> bool:
if len(id_no) != 18:
return False
if not id_no[:17].isdigit():
return False
if not (id_no[17].isdigit() or id_no[17] in ('X', 'x')):
return False
return True

def id_checksum(id_no: str) -> bool:
if not id_valid_format(id_no):
return False
total = sum(int(d) * w for d, w in zip(id_no[:17], ID_WEIGHTS))
mod = total % 11
expected = CHECK_CODE_MAP[mod]
return expected.upper() == id_no[17].upper()

def id_sex_from_code(id_no: str) -> str:
sex_digit = int(id_no[16])
return '男' if sex_digit % 2 == 1 else '女'

def id_birth_from_code(id_no: str) -> str:
year = id_no[6:10]
month = id_no[10:12]
day = id_no[12:14]
return f"{year}-{month}-{day}"

def name_valid(name: str) -> bool:
if not (2 <= len(name) <= 4):
return False
return bool(re.fullmatch(r'[\u4e00-\u9fa5]{2,4}', name))

def phone_valid(phone: str) -> bool:
return bool(re.fullmatch(r'1\d{10}', phone))

def parse_date(date_str: str) -> datetime.date:
for fmt in ('%Y-%m-%d', '%Y/%m/%d', '%Y%m%d', '%Y.%m.%d'):
try:
return datetime.datetime.strptime(date_str.strip(), fmt).date()
except ValueError:
continue
raise ValueError(f"无法解析出生日期: {date_str}")

def parse_datetime(dt_str: str) -> datetime.datetime:
for fmt in ('%Y-%m-%d %H:%M:%S', '%Y/%m/%d %H:%M:%S',
'%Y-%m-%d %H:%M', '%Y/%m/%d %H:%M',
'%Y-%m-%d', '%Y/%m/%d'):
try:
return datetime.datetime.strptime(dt_str.strip(), fmt)
except ValueError:
continue
raise ValueError(f"无法解析时间: {dt_str}")

def datetime_logic_valid(birth: datetime.date,
register: datetime.datetime,
last_login: datetime.datetime) -> bool:
if birth >= register.date():
return False
if register > last_login:
return False
return True

def validate_record(row: pd.Series) -> tuple[bool, str]:

try:
id_no = str(row['身份证号']).strip()
if not id_valid_format(id_no):
return False, "身份证格式错误"
if not id_checksum(id_no):
return False, "身份证校验码错误"

sex_id = id_sex_from_code(id_no)
sex_field = str(row['性别']).strip()
if sex_id != sex_field:
return False, f"性别不匹配(身份证: {sex_id} vs 字段: {sex_field})"


birth_id = id_birth_from_code(id_no)
birth_field = parse_date(str(row['出生日期']))
if birth_id != birth_field.strftime('%Y-%m-%d'):
return False, f"出生日期不匹配(身份证: {birth_id} vs 字段: {birth_field})"

phone = str(row['手机号']).strip()
if not phone_valid(phone):
return False, "手机号格式错误"

register_dt = parse_datetime(str(row['注册时间']))
last_login_dt = parse_datetime(str(row['最后登录时间']))
if not datetime_logic_valid(birth_field, register_dt, last_login_dt):
return False, "时间逻辑错误"

name = str(row['姓名']).strip()
if not name_valid(name):
return False, "姓名格式错误"

return True, "合规"
except Exception as e:
return False, f"异常: {e}"

def main():
col_names = ['客户id', '姓名', '身份证号', '性别', '手机号',
'出生日期', '注册时间', '最后登录时间']
df = pd.read_csv('data.csv', header=None, names=col_names,
dtype=str, keep_default_na=False, na_values=['', 'NA', 'N/A'])

valid_rows = []
invalid_rows = []

for idx, row in df.iterrows():
is_ok, msg = validate_record(row)
if is_ok:
valid_rows.append(row)
else:
invalid_rows.append(row)

pd.DataFrame(valid_rows, columns=col_names).to_csv('output_valid.csv',
index=False,
encoding='utf-8-sig')

if invalid_rows:
df_invalid = pd.DataFrame(invalid_rows, columns=col_names)
df_invalid.to_csv('output_invalid.csv', index=False, encoding='utf-8-sig')

print(f"共 {len(df)} 条记录,合规 {len(valid_rows)} 条,违规 {len(invalid_rows)} 条。")
print("合规数据已写入 output_valid.csv")
if invalid_rows:
print("违规数据已写入 output_invalid.csv(若需要,可自行扩展错误原因字段)")

if __name__ == "__main__":
main()

得到:

1763180896207

flag:

1763180929185

shop

exp:

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
import pandas as pd
import numpy as np
import sys
import os
from datetime import datetime, timedelta

INPUT_CSV = "data.csv"
OUTPUT_CSV = "abnormal_users.csv"

PRICE_RANGES = {
"电子产品": (100, 5000),
"服装鞋包": (50, 1000),
"家居用品": (30, 2000),
"美妆护肤": (20, 800),
"食品饮料": (5, 300),
"图书文具": (5, 200),
"运动户外": (50, 3000),
}

FREQ_THRESHOLD = 10


def read_and_clean(csv_path: str) -> pd.DataFrame:
df = pd.read_csv(csv_path, dtype=str)

required_cols = ["订单ID", "用户ID", "订单金额", "下单时间",
"商品类型", "支付方式", "银行卡号",
"用户注册天数", "用户历史订单数"]
missing = set(required_cols) - set(df.columns)
if missing:
raise ValueError(f"缺失必要列: {missing}")

df = df[required_cols]

df["订单金额"] = pd.to_numeric(df["订单金额"], errors="coerce")
df["下单时间"] = pd.to_datetime(df["下单时间"], errors="coerce")
df["用户注册天数"] = pd.to_numeric(df["用户注册天数"], errors="coerce")
df["用户历史订单数"] = pd.to_numeric(df["用户历史订单数"], errors="coerce")

df.dropna(subset=["订单ID", "用户ID", "订单金额", "下单时间",
"商品类型", "银行卡号"], inplace=True)

return df


def luhn_check(card_number: str) -> bool:

card = "".join(filter(str.isdigit, card_number))
if not card:
return False

total = 0
reverse_digits = [int(d) for d in reversed(card)]
for idx, digit in enumerate(reverse_digits):
if idx % 2 == 1:
doubled = digit * 2
if doubled > 9:
doubled -= 9
total += doubled
else:
total += digit
return total % 10 == 0


def detect_amount_anomaly(df: pd.DataFrame) -> set:
user_set = set()
min_prices = df["商品类型"].map(lambda x: PRICE_RANGES.get(x, (0, float("inf")))[0])
max_prices = df["商品类型"].map(lambda x: PRICE_RANGES.get(x, (0, float("inf")))[1])

mask = (df["订单金额"] < min_prices) | (df["订单金额"] > max_prices)

anomalies = df.loc[mask, ["用户ID"]].drop_duplicates()
user_set.update(anomalies["用户ID"].tolist())
return user_set


def detect_card_format_anomaly(df: pd.DataFrame) -> set:
mask = (~df["银行卡号"].str.isdigit()) | (df["银行卡号"].str.len() < 16) | (df["银行卡号"].str.len() > 19)
anomalies = df.loc[mask, ["用户ID"]].drop_duplicates()
return set(anomalies["用户ID"].tolist())


def detect_card_luhn_anomaly(df: pd.DataFrame) -> set:
mask = ~df["银行卡号"].apply(luhn_check)
anomalies = df.loc[mask, ["用户ID"]].drop_duplicates()
return set(anomalies["用户ID"].tolist())


def detect_card_usage_anomaly(df: pd.DataFrame) -> set:
df = df.copy()
df["下单小时"] = df["下单时间"].dt.floor("H")
grp = df.groupby(["银行卡号", "下单小时", "用户ID"]).size().reset_index(name="cnt")
card_hour_user = grp.groupby(["银行卡号", "下单小时"])["用户ID"].nunique().reset_index(name="user_count")
mask = card_hour_user["user_count"] > 1
suspect_cards = card_hour_user.loc[mask, "银行卡号"].unique()

suspect_users = df[df["银行卡号"].isin(suspect_cards)]["用户ID"].unique()
return set(suspect_users)


def detect_frequency_anomaly(df: pd.DataFrame) -> set:
user_set = set()
for user, group in df.groupby("用户ID"):

times = group["下单时间"].sort_values()

left = 0
for right in range(len(times)):
while (times.iloc[right] - times.iloc[left]) > timedelta(hours=1):
left += 1
if right - left + 1 > FREQ_THRESHOLD:
user_set.add(user)
break
return user_set


def main():
if not os.path.exists(INPUT_CSV):
print(f"错误:输入文件 {INPUT_CSV} 未找到", file=sys.stderr)
sys.exit(1)

df = read_and_clean(INPUT_CSV)

amount_users = detect_amount_anomaly(df)
card_fmt_users = detect_card_format_anomaly(df)
card_luhn_users = detect_card_luhn_anomaly(df)
card_usage_users = detect_card_usage_anomaly(df)
freq_users = detect_frequency_anomaly(df)

result_rows = []

for uid in amount_users:
result_rows.append({"用户ID": uid, "异常类型": "金额异常"})
for uid in card_fmt_users:
result_rows.append({"用户ID": uid, "异常类型": "银行卡异常"})
for uid in card_luhn_users:
result_rows.append({"用户ID": uid, "异常类型": "银行卡异常"})
for uid in card_usage_users:
result_rows.append({"用户ID": uid, "异常类型": "银行卡异常"})
for uid in freq_users:
result_rows.append({"用户ID": uid, "异常类型": "频率异常"})

out_df = pd.DataFrame(result_rows).drop_duplicates()

out_df.to_csv(OUTPUT_CSV, index=False, encoding="utf-8")
print(f"检测完成,结果已写入 {OUTPUT_CSV}")


if __name__ == "__main__":
main()

得到

1763181850765

flag:

1763181880675

AI安全

寻找可爱的小狗

拿到附件后,逐一浏览图片内容,发现有6张图片是猫,按照要求顺序排列文件名之后,进行md5加密即可

1763181472614