{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading data from: label_flipping_dataset.npz\n", "Data loaded successfully.\n", "X_train shape: (700, 2), y_train shape: (700,)\n", "Unique classes in training data: [0 1]\n" ] } ], "source": [ "import numpy as np\n", "import json\n", "import requests\n", "from sklearn.linear_model import LogisticRegression\n", "import os\n", "\n", "dataset_filename = \"label_flipping_dataset.npz\"\n", "random_seed = 1337 # Seed for reproducibility in attack & model training\n", "np.random.seed(random_seed) # Apply seed globally if needed, or pass to functions\n", "\n", "# >>> IMPORTANT: SET THIS VARIABLE TO YOUR SPAWNED INSTANCE IP AND PORT<<<\n", "evaluator_base_url = \"http://154.57.164.78:32553\" # CHANGE THIS\n", "# Example: evaluator_base_url = \"http://10.10.10.1:5555\"\n", "\n", "# Attack Configuration\n", "TARGET_CLASS_TO_POISON = 0 # We want to make the model bad at identifying Class 0\n", "NEW_LABEL_FOR_POISONED = 1 # We want it to predict Class 1 instead\n", "POISON_FRACTION = 0.60\n", "\n", "# Load Data\n", "print(f\"Loading data from: {dataset_filename}\")\n", "try:\n", " data = np.load(dataset_filename)\n", " X_train = data[\"Xtr\"]\n", " y_train = data[\"ytr\"]\n", " X_test = data[\"Xte\"]\n", " y_test = data[\"yte\"]\n", " print(\"Data loaded successfully.\")\n", " print(f\"X_train shape: {X_train.shape}, y_train shape: {y_train.shape}\")\n", " unique_classes_train = np.unique(y_train)\n", " print(f\"Unique classes in training data: {unique_classes_train}\")\n", " if (\n", " TARGET_CLASS_TO_POISON not in unique_classes_train\n", " or NEW_LABEL_FOR_POISONED not in unique_classes_train\n", " ):\n", " print(\"Warning: Target or new label class not found in training data.\")\n", " data.close()\n", "except FileNotFoundError:\n", " print(f\"Error: Dataset file '{dataset_filename}' not found.\")\n", " raise\n", "except KeyError as e:\n", " print(f\"Error: Could not find expected array key '{e}' in the .npz file.\")\n", " raise\n", "except Exception as e:\n", " print(f\"An unexpected error occurred during data loading: {e}\")\n", " raise" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# def targeted_flip_labels(y, poison_percentage, target_class, new_class, seed=1337):\n", "def targeted_class_label_flip(y_train, target_class, new_label, poison_fraction, seed):\n", " if not 0 <= poison_fraction <= 1:\n", " raise ValueError(\"poison_fraction must be between 0 and 1.\")\n", " if target_class == new_label:\n", " raise ValueError(\"target_class and new_label cannot be the same.\")\n", " # Ensure target_class and new_label are present in y_train\n", " unique_labels = np.unique(y_train)\n", " if target_class not in unique_labels:\n", " raise ValueError(f\"target_class ({target_class}) does not exist in y_train.\")\n", " if new_label not in unique_labels:\n", " raise ValueError(f\"new_label ({new_label}) does not exist in y_train.\")\n", " \n", " # Identify indices belonging to the target class\n", " target_indices = np.where(y_train == target_class)[0]\n", " n_target_samples = len(target_indices)\n", "\n", " if n_target_samples == 0:\n", " print(f\"Warning: No samples found for target_class {target_class}. No labels flipped.\")\n", " return y_train.copy(), np.array([], dtype=int)\n", " \n", " # Calculate the number of labels to flip within the target class\n", " n_to_flip = int(n_target_samples * poison_fraction)\n", "\n", " if n_to_flip == 0:\n", " print(f\"Warning: Poison percentage ({poison_fraction * 100:.1f}%) is too low \"\n", " f\"to flip any labels in the target class (size {n_target_samples}).\")\n", " return y_train.copy(), np.array([], dtype=int)\n", "\n", " # Use a dedicated random number generator instance with the specified seed\n", " rng_instance = np.random.default_rng(seed)\n", "\n", " # Randomly select indices from the target_indices subset to flip\n", " # These are indices relative to the target_indices array\n", " indices_within_target_set_to_flip = rng_instance.choice(\n", " n_target_samples, size=n_to_flip, replace=False\n", " )\n", " # Map these back to the original array indices\n", " flipped_indices = target_indices[indices_within_target_set_to_flip]\n", "\n", " # Create a copy to avoid modifying the original array\n", " y_poisoned = y_train.copy()\n", "\n", " # Perform the flip for the selected indices to the new class label\n", " y_poisoned[flipped_indices] = new_label\n", "\n", " print(f\"Targeting Class {target_class} for flipping to Class {new_label}.\")\n", " print(f\"Identified {n_target_samples} samples of Class {target_class}.\")\n", " print(f\"Attempting to flip {poison_fraction * 100:.1f}% ({n_to_flip} samples) of these.\")\n", " print(f\"Successfully flipped {len(flipped_indices)} labels.\")\n", "\n", " return y_poisoned, flipped_indices" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Targeting Class 0 for flipping to Class 1.\n", "Identified 347 samples of Class 0.\n", "Attempting to flip 60.0% (208 samples) of these.\n", "Successfully flipped 208 labels.\n", "\n", "--- Post-Attack Checks ---\n", "Attack function executed, 208 label(s) flipped.\n", "Indices of flipped labels in training data (first 10): [672 170 374 91 1 224 145 644 233 27]\n", "Original labels at flipped indices (first 10): [0 0 0 0 0 0 0 0 0 0]\n", "Poisoned labels at flipped indices (first 10): [1 1 1 1 1 1 1 1 1 1]\n", "Shape of poisoned labels array: (700,)\n" ] } ], "source": [ "# Execute the attack\n", "y_train_poisoned, flipped_idx = targeted_class_label_flip(\n", " y_train,\n", " target_class=TARGET_CLASS_TO_POISON,\n", " new_label=NEW_LABEL_FOR_POISONED,\n", " poison_fraction=POISON_FRACTION,\n", " seed=random_seed,\n", ")\n", "\n", "# Basic Checks\n", "print(\"\\n--- Post-Attack Checks ---\")\n", "if flipped_idx.size > 0:\n", " print(f\"Attack function executed, {len(flipped_idx)} label(s) flipped.\")\n", " print(f\"Indices of flipped labels in training data (first 10): {flipped_idx[:10]}\")\n", " print(f\"Original labels at flipped indices (first 10): {y_train[flipped_idx[:10]]}\")\n", " print(\n", " f\"Poisoned labels at flipped indices (first 10): {y_train_poisoned[flipped_idx[:10]]}\"\n", " )\n", " print(f\"Shape of poisoned labels array: {y_train_poisoned.shape}\")\n", "else:\n", " print(\n", " \"Attack function ran, but no labels were flipped (check settings and warnings).\"\n", " )\n", " print(\"Proceeding with potentially unpoisoned labels.\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "--- Training Model on Poisoned Labels ---\n", "Logistic Regression model trained successfully.\n" ] } ], "source": [ "# %%\n", "# Train Model using Logistic Regression (Same as before)\n", "print(\"\\n--- Training Model on Poisoned Labels ---\")\n", "model = LogisticRegression(random_state=random_seed, solver=\"liblinear\")\n", "\n", "try:\n", " # Train on original features but poisoned labels\n", " model.fit(X_train, y_train_poisoned)\n", " print(\"Logistic Regression model trained successfully.\")\n", "except Exception as e:\n", " print(f\"Error during model training: {e}\")\n", " raise" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "--- Extracting Model Parameters ---\n", "Extracted weights shape: (1, 2)\n", "Extracted intercept shape: (1,)\n" ] } ], "source": [ "print(\"\\n--- Extracting Model Parameters ---\")\n", "try:\n", " weights = model.coef_\n", " intercept = model.intercept_\n", " print(f\"Extracted weights shape: {weights.shape}\")\n", " print(f\"Extracted intercept shape: {intercept.shape}\")\n", " weights_list = weights.tolist()\n", " intercept_list = intercept.tolist()\n", " parameters_extracted = True\n", "except Exception as e:\n", " print(f\"An unexpected error occurred during parameter extraction: {e}\")\n", " weights_list = None\n", " intercept_list = None\n", " parameters_extracted = False" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Checking evaluator health at: http://154.57.164.78:32553/health\n", "\n", "--- Health Check Response ---\n", "Status: healthy\n", "Message: Evaluator API running.\n" ] } ], "source": [ "health_check_url = f\"{evaluator_base_url}/health\"\n", "print(f\"Checking evaluator health at: {health_check_url}\")\n", "if \"\" in evaluator_base_url:\n", " print(\"\\n--- WARNING ---\")\n", " print(\n", " \"Please update the 'evaluator_base_url' variable with the correct IP and Port before running!\"\n", " )\n", " print(\"-------------\")\n", "else:\n", " try:\n", " response = requests.get(health_check_url, timeout=10)\n", " response.raise_for_status()\n", " health_status = response.json()\n", " print(\"\\n--- Health Check Response ---\")\n", " print(f\"Status: {health_status.get('status', 'N/A')}\")\n", " print(f\"Message: {health_status.get('message', 'No message received.')}\")\n", " if health_status.get(\"status\") != \"healthy\":\n", " print(\n", " \"\\nWarning: Evaluator service reported an unhealthy status. It might still be starting up or encountered an issue (like loading data).\"\n", " )\n", " except requests.exceptions.ConnectionError as e:\n", " print(f\"\\nConnection Error: Could not connect to {health_check_url}.\")\n", " print(\"Please check:\")\n", " print(\" 1. The evaluator URL (IP address and port) is correct.\")\n", " print(\" 2. The evaluator Docker container is running.\")\n", " print(\n", " \" 3. There are no network issues (firewalls, etc.) blocking the connection.\"\n", " )\n", " except requests.exceptions.Timeout:\n", " print(f\"\\nTimeout Error: The request to {health_check_url} timed out.\")\n", " print(\n", " \"The server might be taking too long to respond or there could be network issues.\"\n", " )\n", " except requests.exceptions.RequestException as e:\n", " print(f\"\\nError during health check request: {e}\")\n", " print(\"Check the URL format and ensure the server is running.\")\n", " except json.JSONDecodeError:\n", " print(\"\\nError: Could not decode JSON response from health check.\")\n", " print(\"The server might have sent an invalid response.\")\n", " print(\n", " f\"Raw response status: {response.status_code}, Raw response text: {response.text}\"\n", " )\n", " except Exception as e:\n", " print(f\"\\nAn unexpected error occurred during health check: {e}\")" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "Attempting submission to: http://154.57.164.78:32553/evaluate_targeted\n", "Payload preview: {\n", " \"coef\": [\n", " [\n", " 0.5122360892231508,\n", " -0.22736468024302808\n", " ]\n", " ],\n", " \"intercept\": [\n", " 1.59578101904743\n", " ]\n", "}\n", "\n", "--- Evaluator Response ---\n", "========== Attack Successful! ==========\n", "Overall Accuracy evaluated: 0.6100\n", "Accuracy on Class 0 samples: 0.2353\n", "Message: Attack successful! Model accuracy on Class 0 (0.2353) is below threshold. Overall accuracy (0.6100) maintained.\n", "\n", "FLAG: HTB{l4b3l_fl1pp1ng_targeted_pwnz}\n", "======================================\n" ] } ], "source": [ "evaluator_url = f\"{evaluator_base_url}/evaluate_targeted\"\n", "print(f\"\\nAttempting submission to: {evaluator_url}\")\n", "\n", "if not parameters_extracted:\n", " print(\"Error: Cannot submit - parameters not extracted.\")\n", "elif \"\" in evaluator_base_url or \"\" in evaluator_base_url:\n", " print(\"\\n--- WARNING: Update evaluator_base_url ---\")\n", "else:\n", " payload = {\"coef\": weights_list, \"intercept\": intercept_list}\n", " print(f\"Payload preview: {json.dumps(payload, indent=2)}\")\n", "\n", " try:\n", " response = requests.post(evaluator_url, json=payload, timeout=30)\n", " response.raise_for_status()\n", " result = response.json()\n", "\n", " print(\"\\n--- Evaluator Response ---\")\n", " if result.get(\"success\"):\n", " print(f\"{'=' * 10} Attack Successful! {'=' * 10}\")\n", " oa_str = (\n", " f\"{result.get('overall_accuracy', 'N/A'):.4f}\"\n", " if isinstance(result.get(\"overall_accuracy\"), (int, float))\n", " else \"N/A\"\n", " )\n", " c0a_str = (\n", " f\"{result.get('class0_accuracy', 'N/A'):.4f}\"\n", " if isinstance(result.get(\"class0_accuracy\"), (int, float))\n", " else \"N/A\"\n", " ) # Get Class 0 Accuracy\n", " print(f\"Overall Accuracy evaluated: {oa_str}\")\n", " print(f\"Accuracy on Class 0 samples: {c0a_str}\") # Display Class 0 Accuracy\n", " print(f\"Message: {result.get('message', 'N/A')}\")\n", " print(f\"\\nFLAG: {result.get('flag')}\")\n", " print(f\"{'=' * 38}\")\n", " else:\n", " print(\"Evaluation Failed.\")\n", " oa_val = result.get(\"overall_accuracy\")\n", " c0a_val = result.get(\"class0_accuracy\") # Get Class 0 Accuracy\n", " oa_str = f\"{oa_val:.4f}\" if oa_val is not None else \"N/A\"\n", " c0a_str = (\n", " f\"{c0a_val:.4f}\" if c0a_val is not None else \"N/A\"\n", " ) # Get Class 0 Accuracy\n", "\n", " print(f\"Overall Accuracy evaluated: {oa_str}\")\n", " print(f\"Accuracy on Class 0 samples: {c0a_str}\") # Display Class 0 Accuracy\n", " print(f\"Message: {result.get('message', 'No message provided.')}\")\n", " print(\n", " \"\\nHints: Did the attack significantly reduce accuracy specifically for Class 0 samples?\"\n", " )\n", " print(\"Did the overall accuracy remain above the required threshold?\")\n", " print(\"Consider adjusting the POISON_FRACTION.\")\n", "\n", " except requests.exceptions.ConnectionError:\n", " print(f\"\\nConnection Error: Could not connect to {evaluator_url}.\")\n", " except requests.exceptions.Timeout:\n", " print(f\"\\nTimeout Error: Request to {evaluator_url} timed out.\")\n", " except requests.exceptions.RequestException as e:\n", " print(f\"\\nError during submission request: {e}\")\n", " if e.response is not None:\n", " print(f\"Server Response Status Code: {e.response.status_code}\")\n", " try:\n", " print(f\"Server Response Body: {e.response.json()}\")\n", " except json.JSONDecodeError:\n", " print(f\"Server Response Body (non-JSON): {e.response.text}\")\n", " except Exception as e:\n", " print(f\"\\nAn unexpected error occurred during submission: {e}\")" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.6" } }, "nbformat": 4, "nbformat_minor": 4 }