mirror of
https://github.com/Shiva108/ai-llm-red-team-handbook.git
synced 2026-08-18 17:07:25 +02:00
229 lines
7.6 KiB
Python
229 lines
7.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Key Components (Backdoor Attack)
|
|
|
|
Source: Chapter_27_Federated_Learning_Attacks
|
|
Category: utils
|
|
"""
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
import numpy as np
|
|
from typing import Tuple, Dict
|
|
from copy import deepcopy
|
|
|
|
import argparse
|
|
import sys
|
|
|
|
#!/usr/bin/env python3
|
|
"""
|
|
Federated Learning Backdoor Attack
|
|
Demonstrates targeted poisoning that introduces classification backdoors
|
|
|
|
Requirements:
|
|
pip install torch numpy
|
|
|
|
Usage:
|
|
python fl_backdoor_attack.py --trigger-size 3 --target-class 0
|
|
|
|
⚠️ WARNING: FOR EDUCATIONAL PURPOSES ONLY ⚠️
|
|
Only use in authorized security testing with explicit permission.
|
|
"""
|
|
|
|
class BackdoorAttacker:
|
|
"""Implements backdoor poisoning attack in federated learning"""
|
|
|
|
def __init__(self, model: nn.Module, trigger_pattern: torch.Tensor,
|
|
target_class: int):
|
|
"""
|
|
Initialize backdoor attacker
|
|
|
|
Args:
|
|
model: Neural network to backdoor
|
|
trigger_pattern: Trigger that activates backdoor
|
|
target_class: Class to misclassify triggered inputs as
|
|
"""
|
|
self.model = deepcopy(model)
|
|
self.trigger_pattern = trigger_pattern
|
|
self.target_class = target_class
|
|
|
|
def create_backdoor_data(self, clean_data: torch.Tensor,
|
|
clean_labels: torch.Tensor,
|
|
poison_ratio: float = 0.3) -> Tuple[torch.Tensor, torch.Tensor]:
|
|
"""
|
|
Create backdoored training dataset
|
|
|
|
How This Works:
|
|
1. Select portion of clean data to poison
|
|
2. Apply trigger pattern to selected inputs
|
|
3. Relabel poisoned inputs to target class
|
|
4. Combine clean and poisoned data for dual training
|
|
|
|
Args:
|
|
clean_data: Original training data
|
|
clean_labels: Original labels
|
|
poison_ratio: Fraction of data to poison
|
|
|
|
Returns:
|
|
Tuple of (combined_data, combined_labels)
|
|
"""
|
|
num_samples = len(clean_data)
|
|
num_poison = int(num_samples * poison_ratio)
|
|
|
|
# Select samples to poison
|
|
poison_indices = np.random.choice(num_samples, num_poison, replace=False)
|
|
|
|
# Create poisoned data
|
|
poisoned_data = clean_data.clone()
|
|
poisoned_labels = clean_labels.clone()
|
|
|
|
for idx in poison_indices:
|
|
# Apply trigger pattern
|
|
poisoned_data[idx] = self.apply_trigger(clean_data[idx])
|
|
# Relabel to target class
|
|
poisoned_labels[idx] = self.target_class
|
|
|
|
# Combine clean and poisoned data (attacker trains on both)
|
|
combined_data = torch.cat([clean_data, poisoned_data[poison_indices]])
|
|
combined_labels = torch.cat([clean_labels, poisoned_labels[poison_indices]])
|
|
|
|
return combined_data, combined_labels
|
|
|
|
def apply_trigger(self, input_data: torch.Tensor) -> torch.Tensor:
|
|
"""
|
|
Apply trigger pattern to input
|
|
|
|
Args:
|
|
input_data: Clean input
|
|
|
|
Returns:
|
|
Input with trigger applied
|
|
"""
|
|
triggered = input_data.clone()
|
|
# For demonstration: add trigger to first features
|
|
trigger_size = self.trigger_pattern.size(0)
|
|
triggered[:trigger_size] = self.trigger_pattern
|
|
return triggered
|
|
|
|
def train_backdoor(self, clean_data: torch.Tensor, clean_labels: torch.Tensor,
|
|
epochs: int = 10) -> Dict[str, torch.Tensor]:
|
|
"""
|
|
Train backdoored model using model replacement attack
|
|
|
|
How This Works:
|
|
1. Create dataset with both clean and backdoored samples
|
|
2. Train model to high accuracy on clean data (stealth)
|
|
3. Train model to high accuracy on backdoored data (attack)
|
|
4. Scale updates to override honest participants in aggregation
|
|
|
|
Args:
|
|
clean_data: Clean training data
|
|
clean_labels: Clean labels
|
|
epochs: Training epochs
|
|
|
|
Returns:
|
|
Scaled malicious updates
|
|
"""
|
|
# Create backdoored dataset
|
|
combined_data, combined_labels = self.create_backdoor_data(
|
|
clean_data, clean_labels, poison_ratio=0.5
|
|
)
|
|
|
|
# Record initial parameters
|
|
initial_params = {name: param.clone()
|
|
for name, param in self.model.named_parameters()}
|
|
|
|
# Train on combined clean + backdoored data
|
|
optimizer = torch.optim.SGD(self.model.parameters(), lr=0.01)
|
|
criterion = nn.CrossEntropyLoss()
|
|
|
|
for epoch in range(epochs):
|
|
optimizer.zero_grad()
|
|
outputs = self.model(combined_data)
|
|
loss = criterion(outputs, combined_labels)
|
|
loss.backward()
|
|
optimizer.step()
|
|
|
|
# Compute updates
|
|
updates = {}
|
|
for name, param in self.model.named_parameters():
|
|
updates[name] = param.data - initial_params[name]
|
|
|
|
# Model replacement: scale updates to dominate aggregation
|
|
# If server expects n clients, attacker scales by n to override others
|
|
scale_factor = 50.0 # Aggressive scaling
|
|
scaled_updates = {name: update * scale_factor
|
|
for name, update in updates.items()}
|
|
|
|
return scaled_updates
|
|
|
|
def demonstrate_backdoor_attack():
|
|
"""Demonstrate backdoor attack in federated learning"""
|
|
print("="*70)
|
|
print(" FEDERATED LEARNING BACKDOOR ATTACK DEMONSTRATION ".center(70, "="))
|
|
print("="*70)
|
|
print("\n⚠️ WARNING: FOR EDUCATIONAL PURPOSES ONLY ⚠️\n")
|
|
|
|
# Simple model
|
|
class SimpleNet(nn.Module):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.fc1 = nn.Linear(10, 50)
|
|
self.fc2 = nn.Linear(50, 3)
|
|
|
|
def forward(self, x):
|
|
x = torch.relu(self.fc1(x))
|
|
return self.fc2(x)
|
|
|
|
# Setup
|
|
model = SimpleNet()
|
|
trigger_pattern = torch.tensor([9.0, 9.0, 9.0]) # Distinctive trigger
|
|
target_class = 0 # Force misclassification to class 0
|
|
|
|
attacker = BackdoorAttacker(model, trigger_pattern, target_class)
|
|
|
|
# Generate data
|
|
print("[*] Generating data...")
|
|
clean_data = torch.randn(100, 10)
|
|
clean_labels = torch.randint(0, 3, (100,))
|
|
|
|
print(f"[*] Trigger pattern: {trigger_pattern.tolist()}")
|
|
print(f"[*] Target class: {target_class}")
|
|
|
|
# Train backdoor
|
|
print("\n[*] Training backdoored model...")
|
|
malicious_updates = attacker.train_backdoor(clean_data, clean_labels, epochs=20)
|
|
|
|
# Test backdoor
|
|
print("\n[*] Testing backdoor activation...")
|
|
test_input = torch.randn(1, 10)
|
|
triggered_input = attacker.apply_trigger(test_input)
|
|
|
|
attacker.model.eval()
|
|
with torch.no_grad():
|
|
clean_pred = attacker.model(test_input).argmax().item()
|
|
backdoor_pred = attacker.model(triggered_input).argmax().item()
|
|
|
|
print(f"\nClean input prediction: Class {clean_pred}")
|
|
print(f"Triggered input prediction: Class {backdoor_pred}")
|
|
|
|
if backdoor_pred == target_class:
|
|
print("\n✓ BACKDOOR SUCCESSFULLY PLANTED")
|
|
print(f" Trigger causes misclassification to target class {target_class}")
|
|
else:
|
|
print("\n✗ Backdoor training incomplete (need more epochs)")
|
|
|
|
print("\n" + "="*70)
|
|
print("\n[ANALYSIS]")
|
|
print("The backdoored model:")
|
|
print("1. Maintains normal accuracy on clean inputs")
|
|
print("2. Misclassifies any input containing the trigger pattern")
|
|
print("3. Persists through federated aggregation via model replacement")
|
|
print("\nThis attack is stealthy because clean accuracy remains high,")
|
|
print("making detection extremely challenging.")
|
|
print("\n" + "="*70)
|
|
|
|
if __name__ == "__main__":
|
|
print("[Backdoor Attack Demo] - For educational/authorized testing only\n")
|
|
demonstrate_backdoor_attack()
|