Add 55 new skills across 3 new domains + 6 undercovered areas (762 -> 817)

Demand-driven expansion targeting the fastest-growing 2025-2026 threat and
skills categories (ISC2/WEF/CrowdStrike/Mandiant signals):

- AI Security (NEW domain, 12 skills): LLM red-teaming with garak/PyRIT,
  prompt injection (direct/indirect/RAG), MCP tool-poisoning, agentic tool
  invocation, guardrails, model/data poisoning, system-prompt leakage,
  embedding/vector weaknesses, model extraction, continuous red-teaming
- Supply Chain Security (NEW domain, 5 skills): SBOMs, dependency confusion,
  malicious-npm triage, typosquatting, SLSA/Sigstore provenance
- Hardware & Firmware Security (NEW domain, 4 skills): CHIPSEC/UEFI audit,
  Secure Boot bypass, TPM measured-boot attestation, ESP bootkit hunting
- Identity (10): Entra ID/ROADtools, GraphRunner, AADInternals, ADCS/Certipy,
  shadow credentials, coercion, BloodHound CE, device-code phishing, SSO abuse
- Cloud-native (8): Stratus, Pacu, CloudFox, container escape, K8s RBAC,
  Falco, Trivy, kube-bench
- Offensive C2 (6): Sliver, Havoc, NetExec, DPAPI, NTLM relay ESC8, redirectors
- DFIR (6): Hayabusa, Chainsaw, KAPE, Velociraptor, EZ Tools, Plaso
- Backfill (4): OpenCTI, MISP, honeytokens, post-quantum crypto migration

Each skill follows the repo taxonomy (SKILL.md + references/{standards,api-reference}.md
+ scripts/agent.py + LICENSE), with researched real tool commands (no placeholders),
complete frontmatter, and ATT&CK/ATLAS + NIST CSF mappings. Updates README domain
table, skill count, and index.json.
This commit is contained in:
mukul975
2026-06-22 19:08:16 +02:00
parent 13a1c4afd9
commit 8cae0648ec
279 changed files with 36389 additions and 34 deletions
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to the Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by the Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding any notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. Please do not remove or change
the license header comment from a contributed file except when
necessary.
Copyright 2026 mukul975
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,204 @@
---
name: detecting-model-extraction-attacks
description: Detect model stealing, model inversion, and membership inference performed through inference-API abuse by monitoring query patterns, applying output perturbation, and red-teaming your own model's extractability.
domain: cybersecurity
subdomain: ai-security
tags:
- ai-security
- model-extraction
- membership-inference
- model-inversion
- inference-api
- mitre-atlas
- query-monitoring
- mlsecops
version: '1.0'
author: mahipal
license: Apache-2.0
nist_csf:
- MEASURE-2.6
mitre_attack:
- AML.T0024
---
# Detecting Model Extraction Attacks
> **Authorized Use Only:** The extraction, inversion, and membership-inference techniques described here are intended for defenders testing their own models and for red teams operating under written authorization. Querying a third-party model to clone it, reconstruct its training data, or infer membership without permission may violate terms of service, copyright, and privacy law.
## Overview
Model extraction is the family of attacks in which an adversary abuses a model's **inference API** to steal value that the model owner intended to keep private. MITRE ATLAS catalogs these under **AML.T0024 — Exfiltration via AI Inference API**, in the *Exfiltration* tactic, with three sub-techniques:
- **AML.T0024.000 — Infer Training Data Membership** (membership inference): the adversary determines whether a specific record was part of the training set, a privacy violation that can expose, for example, whether a patient's record trained a medical model.
- **AML.T0024.001 — Invert AI Model** (model inversion): the adversary reconstructs representative training inputs (e.g., faces, text) by exploiting confidence scores returned by the API.
- **AML.T0024.002 — Extract ML Model** (model stealing): the adversary repeatedly queries the victim model, collects (input, prediction) pairs, and trains a *surrogate* model offline that mimics the victim's decision boundary — avoiding the per-query cost of a Machine-Learning-as-a-Service offering and stealing the owner's intellectual property.
All three share a common signal: an attacker must send **many queries**, often crafted to probe the decision boundary (high-entropy, near-boundary, synthetic, or systematically grid-sampled inputs), and frequently requests **full confidence vectors / logits** rather than just the top label. Detection therefore centers on per-principal query monitoring, input-distribution analysis, and confidence-exposure controls, while defense centers on rate limiting, output perturbation, and reducing the information returned per query. This skill follows the MITRE ATLAS technique definition for AML.T0024 (https://atlas.mitre.org/techniques/AML.T0024) and the NIST AI RMF MEASURE function (MEASURE-2.6, security and resilience of the AI system).
## When to Use
- When you operate a model behind a public or partner inference API and need to detect cloning, inversion, or membership inference.
- When performing a pre-deployment AI red-team exercise to measure how many queries are needed to extract your own model.
- When validating that rate limiting, output perturbation, and confidence-suppression controls actually reduce extractability.
- When investigating anomalous billing/usage spikes that may indicate surrogate-model harvesting.
- When responding to a privacy incident where membership inference against a model is suspected.
## Prerequisites
- Python 3.9+ environment.
- Access to inference-API access logs (per-API-key/per-principal query counts, timestamps, input features or hashes, returned confidence vectors).
- For self-assessment red-teaming, install the Adversarial Robustness Toolbox (ART), the reference framework for extraction/inference attacks and defenses:
```bash
pip install adversarial-robustness-toolbox scikit-learn numpy
```
- Optional: access to the target model object (white/grey-box) or only its API (black-box).
- Authorization to test the target model.
## Objectives
- Instrument the inference API to record per-principal query volume, input diversity, and confidence-exposure.
- Build a detector that scores principals for extraction-like behavior (volume, near-boundary sampling, full-vector requests).
- Run an ART-based extraction attack against your own model to measure fidelity vs. query budget.
- Run a membership-inference attack to quantify training-data leakage.
- Apply and validate defenses: rate limiting, label-only responses, confidence rounding/perturbation, and prediction poisoning.
## MITRE ATT&CK Mapping
| ID | Name (MITRE ATLAS) | Tactic |
|----|--------------------|--------|
| AML.T0024 | Exfiltration via AI Inference API | Exfiltration |
| AML.T0024.000 | Infer Training Data Membership | Exfiltration |
| AML.T0024.001 | Invert AI Model | Exfiltration |
| AML.T0024.002 | Extract ML Model | Exfiltration |
## Workflow
### 1. Instrument the inference API for detection signals
Capture the fields a detector needs. Per request, log the principal (API key / IP / account), timestamp, an input fingerprint, and whether the caller requested probabilities/logits.
```python
import hashlib, json, time
def log_inference(principal, features, returned_probs):
record = {
"ts": time.time(),
"principal": principal,
# hash inputs so logs don't store raw sensitive data
"input_hash": hashlib.sha256(json.dumps(features, sort_keys=True).encode()).hexdigest(),
"wants_probs": returned_probs,
"n_features": len(features),
}
with open("inference_audit.jsonl", "a") as f:
f.write(json.dumps(record) + "\n")
```
### 2. Detect extraction-like query patterns
Score each principal on the three signals that distinguish extraction from normal use: high query volume in a window, high *unique-input* ratio (attackers rarely repeat), and a high rate of full-probability requests.
```python
import collections, json
def score_principals(audit_path="inference_audit.jsonl", window_qps_threshold=100):
by_principal = collections.defaultdict(lambda: {"q": 0, "uniq": set(), "probs": 0})
for line in open(audit_path):
r = json.loads(line)
p = by_principal[r["principal"]]
p["q"] += 1
p["uniq"].add(r["input_hash"])
p["probs"] += int(r["wants_probs"])
findings = []
for principal, p in by_principal.items():
uniq_ratio = len(p["uniq"]) / max(p["q"], 1)
prob_ratio = p["probs"] / max(p["q"], 1)
suspicious = p["q"] > window_qps_threshold and uniq_ratio > 0.9 and prob_ratio > 0.8
findings.append({"principal": principal, "queries": p["q"],
"unique_ratio": round(uniq_ratio, 3),
"prob_request_ratio": round(prob_ratio, 3),
"suspected_extraction": suspicious})
return sorted(findings, key=lambda x: -x["queries"])
```
### 3. Measure your model's extractability with ART (self red-team)
Use ART's `CopycatCNN` (or `KnockoffNets`) to train a surrogate from black-box queries and report fidelity at a given query budget. Low query budget + high agreement = high risk.
```python
import numpy as np
from art.estimators.classification import SklearnClassifier
from art.attacks.extraction import KnockoffNets
from sklearn.ensemble import RandomForestClassifier
# victim is your already-trained model wrapped for ART
victim = SklearnClassifier(model=trained_model) # your production model
thief_model = RandomForestClassifier(n_estimators=100)
thief = SklearnClassifier(model=thief_model)
attack = KnockoffNets(classifier=victim, batch_size_fit=64,
batch_size_query=64, nb_epochs=10, nb_stolen=2000)
stolen = attack.extract(x=x_pool, thief_classifier=thief) # 2000-query budget
agreement = np.mean(stolen.predict(x_test).argmax(1) == victim.predict(x_test).argmax(1))
print(f"Surrogate fidelity (agreement with victim): {agreement:.2%} at 2000 queries")
```
### 4. Quantify training-data leakage with membership inference
Run ART's black-box membership-inference attack. An accuracy meaningfully above 50% indicates the model leaks membership (AML.T0024.000).
```python
from art.attacks.inference.membership_inference import MembershipInferenceBlackBox
mia = MembershipInferenceBlackBox(victim, attack_model_type="rf")
# fit the attack on a labeled split of known members / non-members
mia.fit(x_train[:500], y_train[:500], x_test[:500], y_test[:500])
member_pred = mia.infer(x_train[500:1000], y_train[500:1000])
nonmember_pred = mia.infer(x_test[500:1000], y_test[500:1000])
acc = (member_pred.mean() + (1 - nonmember_pred.mean())) / 2
print(f"Membership-inference accuracy: {acc:.2%} (0.50 = no leakage)")
```
### 5. Apply and validate defenses
Reduce the information returned and the query economics. Re-run steps 3 and 4 after each control to confirm extractability drops.
```python
# (a) Label-only responses: never return full probability vectors to untrusted callers.
def respond(probs, trusted):
return int(probs.argmax()) if not trusted else probs.tolist()
# (b) Confidence rounding / output perturbation (raises queries needed for inversion):
def perturb(probs, decimals=2, noise=0.01):
p = np.round(probs, decimals) + np.random.normal(0, noise, probs.shape)
p = np.clip(p, 0, None)
return p / p.sum()
```
Defense in depth combines these with strict **per-principal rate limiting**, anomaly alerting from step 2, ART's `ReverseSigmoid` / prediction-poisoning postprocessor, and watermarking so an extracted surrogate remains attributable.
### 6. Alert and respond
Wire step-2 findings into your SIEM. On a confirmed extraction pattern: throttle or revoke the API key, switch the principal to label-only responses, preserve the audit log as evidence, and assess membership-inference exposure for any sensitive training data.
## Tools and Resources
| Resource | Link |
|----------|------|
| MITRE ATLAS AML.T0024 — Exfiltration via AI Inference API | https://atlas.mitre.org/techniques/AML.T0024 |
| Adversarial Robustness Toolbox (ART) | https://github.com/Trusted-AI/adversarial-robustness-toolbox |
| ART extraction attacks (CopycatCNN, KnockoffNets) | https://adversarial-robustness-toolbox.readthedocs.io/ |
| MITRE ATLAS Matrix | https://atlas.mitre.org/matrices/ATLAS |
| NIST AI RMF (MEASURE function) | https://www.nist.gov/itl/ai-risk-management-framework |
## Detection Signal Reference
| Signal | Normal use | Extraction behavior |
|--------|-----------|---------------------|
| Query volume per principal | Bounded, bursty | Very high, sustained |
| Unique-input ratio | Repeats common inputs | Near-1.0 (rarely repeats) |
| Confidence-vector requests | Mostly top label | Demands full probs/logits |
| Input distribution | In-distribution | Near-boundary / synthetic / grid |
| Inter-query timing | Human-paced | Automated, regular |
## Validation Criteria
- [ ] Inference API logs per-principal query volume, input fingerprint, and confidence-exposure.
- [ ] Detector scores principals and flags high-volume, high-unique-ratio, full-vector callers.
- [ ] ART extraction attack run against own model; surrogate fidelity vs. query budget reported.
- [ ] Membership-inference accuracy measured and compared against the 50% baseline.
- [ ] Label-only / confidence-perturbation defenses applied and re-tested.
- [ ] Per-principal rate limiting enforced and validated.
- [ ] Alerts routed to SIEM with response playbook (throttle, revoke, preserve evidence).
@@ -0,0 +1,52 @@
# Model Extraction Detection — API / Library Reference
## Libraries
| Library | Install | Purpose |
|---------|---------|---------|
| adversarial-robustness-toolbox | `pip install adversarial-robustness-toolbox` | Extraction, inversion, and membership-inference attacks + defenses |
| scikit-learn | `pip install scikit-learn` | Surrogate / attack model training |
| numpy | `pip install numpy` | Confidence-vector math, perturbation |
## ART Extraction Attacks (`art.attacks.extraction`)
| Class | Key params | Purpose |
|-------|-----------|---------|
| `KnockoffNets` | `nb_stolen`, `batch_size_query`, `nb_epochs`, `sampling_strategy` | Train surrogate from black-box queries (Knockoff Nets) |
| `CopycatCNN` | `nb_stolen`, `batch_size_fit`, `batch_size_query` | Copycat surrogate extraction for neural nets |
| `attack.extract(x, thief_classifier=...)` | — | Run extraction; returns trained surrogate classifier |
## ART Inference Attacks (`art.attacks.inference.membership_inference`)
| Class | Key methods | Purpose |
|-------|-------------|---------|
| `MembershipInferenceBlackBox` | `.fit(...)`, `.infer(x, y)` | Black-box membership inference (AML.T0024.000) |
| `MembershipInferenceBlackBoxRuleBased` | `.infer(x, y)` | Rule-based MIA baseline (no shadow training) |
## ART Defenses (postprocessors)
| Class | Purpose |
|-------|---------|
| `art.defences.postprocessor.ReverseSigmoid` | Perturb output probabilities to hinder extraction |
| `art.defences.postprocessor.Rounded` | Round confidence values to reduce leaked precision |
| `art.defences.postprocessor.HighConfidence` | Suppress low-confidence outputs |
## Estimator Wrappers
| Class | Purpose |
|-------|---------|
| `art.estimators.classification.SklearnClassifier` | Wrap a scikit-learn model as an ART victim |
| `art.estimators.classification.KerasClassifier` / `PyTorchClassifier` | Wrap DL models |
## Detection Signals (custom)
| Signal | Heuristic |
|--------|-----------|
| Query volume | Queries/principal/window above baseline |
| Unique-input ratio | `unique(input_hash)/queries` → ~1.0 |
| Confidence-request ratio | Fraction of calls demanding full probability vectors |
## External References
- ART docs: https://adversarial-robustness-toolbox.readthedocs.io/
- MITRE ATLAS AML.T0024: https://atlas.mitre.org/techniques/AML.T0024
@@ -0,0 +1,29 @@
# Standards and References — Detecting Model Extraction Attacks
## MITRE ATLAS Techniques
| ID | Name | Tactic | Rationale |
|----|------|--------|-----------|
| AML.T0024 | Exfiltration via AI Inference API | Exfiltration | Parent technique: abusing the inference API to steal model value or training data. |
| AML.T0024.000 | Infer Training Data Membership | Exfiltration | Membership inference — determine if a record was in the training set (privacy leak). |
| AML.T0024.001 | Invert AI Model | Exfiltration | Model inversion — reconstruct training inputs from confidence scores. |
| AML.T0024.002 | Extract ML Model | Exfiltration | Model stealing — train a surrogate from query/response pairs to clone the model. |
## NIST AI RMF
| ID | Function | Rationale |
|----|----------|-----------|
| MEASURE-2.6 | AI system security and resilience are evaluated and documented | Extraction/inference testing measures and documents the model's resilience to inference-API abuse. |
## Official Resources
- MITRE ATLAS AML.T0024: https://atlas.mitre.org/techniques/AML.T0024
- MITRE ATLAS Matrix: https://atlas.mitre.org/matrices/ATLAS
- Adversarial Robustness Toolbox (Trusted-AI): https://github.com/Trusted-AI/adversarial-robustness-toolbox
- NIST AI Risk Management Framework: https://www.nist.gov/itl/ai-risk-management-framework
## Key Research
- Tramèr et al., "Stealing Machine Learning Models via Prediction APIs" (USENIX Security 2016)
- Shokri et al., "Membership Inference Attacks Against Machine Learning Models" (IEEE S&P 2017)
- Orekondy et al., "Knockoff Nets: Stealing Functionality of Black-Box Models" (CVPR 2019)
@@ -0,0 +1,140 @@
#!/usr/bin/env python3
# For authorized AI red-teaming and defense of models you own or are permitted to test.
# Cloning a third-party model or inferring its training data without consent may
# violate terms of service, copyright, and privacy law.
"""Model-extraction detection helper.
Two modes:
detect - Parse an inference-API audit log (JSONL) and flag principals whose
query behaviour matches MITRE ATLAS AML.T0024 (model extraction /
inference). Pure stdlib, no external model needed.
extract - Self red-team: train an ART surrogate against your own scikit-learn
model and report fidelity vs. query budget (requires ART).
Audit log format (one JSON object per line):
{"ts": 1700000000.0, "principal": "key-123", "input_hash": "ab..",
"wants_probs": true, "n_features": 12}
"""
import argparse
import collections
import json
import sys
def load_audit(path):
records = []
with open(path, "r", encoding="utf-8") as fh:
for ln, line in enumerate(fh, 1):
line = line.strip()
if not line:
continue
try:
records.append(json.loads(line))
except json.JSONDecodeError as exc:
print(f"[!] skip malformed line {ln}: {exc}", file=sys.stderr)
return records
def score_principals(records, q_threshold, uniq_threshold, prob_threshold):
agg = collections.defaultdict(lambda: {"q": 0, "uniq": set(), "probs": 0})
for r in records:
principal = r.get("principal", "unknown")
a = agg[principal]
a["q"] += 1
a["uniq"].add(r.get("input_hash", id(r)))
a["probs"] += int(bool(r.get("wants_probs", False)))
findings = []
for principal, a in agg.items():
q = a["q"]
uniq_ratio = len(a["uniq"]) / q if q else 0.0
prob_ratio = a["probs"] / q if q else 0.0
suspected = (q >= q_threshold and uniq_ratio >= uniq_threshold
and prob_ratio >= prob_threshold)
findings.append({
"principal": principal,
"queries": q,
"unique_ratio": round(uniq_ratio, 3),
"prob_request_ratio": round(prob_ratio, 3),
"suspected_extraction": suspected,
})
return sorted(findings, key=lambda x: (-x["suspected_extraction"], -x["queries"]))
def cmd_detect(args):
records = load_audit(args.audit)
if not records:
print("[!] no usable records in audit log", file=sys.stderr)
return 1
findings = score_principals(records, args.min_queries,
args.min_unique_ratio, args.min_prob_ratio)
flagged = [f for f in findings if f["suspected_extraction"]]
print(f"[+] analysed {len(records)} requests across {len(findings)} principals")
print(f"[+] {len(flagged)} principal(s) match AML.T0024 extraction pattern\n")
for f in findings:
mark = "[ALERT]" if f["suspected_extraction"] else " "
print(f"{mark} {f['principal']:<24} q={f['queries']:<7} "
f"uniq={f['unique_ratio']:<6} probs={f['prob_request_ratio']}")
if args.output:
with open(args.output, "w", encoding="utf-8") as fh:
json.dump(findings, fh, indent=2)
print(f"\n[+] findings written to {args.output}")
return 0
def cmd_extract(args):
try:
import numpy as np
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from art.estimators.classification import SklearnClassifier
from art.attacks.extraction import KnockoffNets
except ImportError:
print("[!] install: pip install adversarial-robustness-toolbox scikit-learn numpy",
file=sys.stderr)
return 1
# demo victim trained on a public dataset (replace with your production model)
data = load_iris()
x_tr, x_te, y_tr, y_te = train_test_split(data.data, data.target,
test_size=0.4, random_state=42)
victim_model = RandomForestClassifier(n_estimators=100, random_state=0).fit(x_tr, y_tr)
victim = SklearnClassifier(model=victim_model)
thief = SklearnClassifier(model=RandomForestClassifier(n_estimators=100))
attack = KnockoffNets(classifier=victim, batch_size_fit=16, batch_size_query=16,
nb_epochs=5, nb_stolen=args.budget, sampling_strategy="random")
stolen = attack.extract(x=x_te, thief_classifier=thief)
agreement = float(np.mean(stolen.predict(x_te).argmax(1) ==
victim.predict(x_te).argmax(1)))
print(f"[+] query budget : {args.budget}")
print(f"[+] surrogate fidelity : {agreement:.2%} agreement with victim")
risk = "HIGH" if agreement > 0.9 else "MEDIUM" if agreement > 0.7 else "LOW"
print(f"[+] extractability risk : {risk}")
return 0
def main():
p = argparse.ArgumentParser(description="Model-extraction detection / self red-team")
sub = p.add_subparsers(dest="cmd", required=True)
d = sub.add_parser("detect", help="flag extraction-like principals in an audit log")
d.add_argument("--audit", required=True, help="path to JSONL inference audit log")
d.add_argument("--min-queries", type=int, default=100)
d.add_argument("--min-unique-ratio", type=float, default=0.9)
d.add_argument("--min-prob-ratio", type=float, default=0.8)
d.add_argument("--output", help="write findings JSON")
d.set_defaults(func=cmd_detect)
e = sub.add_parser("extract", help="ART self red-team on a demo model")
e.add_argument("--budget", type=int, default=2000, help="query budget for surrogate")
e.set_defaults(func=cmd_extract)
args = p.parse_args()
sys.exit(args.func(args))
if __name__ == "__main__":
main()