Security, Ethics, Privacy, and Governance › Data Poisoning and Model Extraction

Michael BrenndoerferAugust 8, 202630 min read
World Models HandbookAll chapters
Part 12 of 12Reliable World Models
Chapter 69Security, Ethics, Privacy, and Governance › Data Poisoning and Model Extraction
Reading section 3 of 10 · Data Poisoning and Model Extraction · 30 minSection 3 of 10 · 30 min

Book contents

Section 3 of 10 · 30 min read

Security, Ethics, Privacy, and Governance › Data Poisoning and Model ExtractionOne section · Sections in this chapter3 / 10 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

This section covers attacks against training data and inference interfaces. Poisoning changes what the learner fits. Query-based extraction tries to reproduce some model functionality; it need not recover the original weights. The training pipeline, annotation process and prediction API belong in the threat model alongside the model itself.

The attack surface of a world-model pipeline

A world model is assembled from many stages, and each is a place where an adversary can insert influence:

  • Collection. Cameras, teleoperated demonstrations, scraped video, and logs from deployed fleets. Whoever controls a sensor or an upload path controls part of the data distribution.
  • Annotation. Labels, captions, segmentation masks, reward estimates, and filtered "usable" subsets. If a contractor supplies annotations, include its permissions and checks in the threat model. Outsourcing does not by itself establish attacker access or low attack cost.
  • Curation and filtering. Deduplication, quality scoring and safety filtering may use deterministic rules, learned models or both. An attacker who can shape submitted content may try to pass these checks; success depends on the particular filter and permissions.
  • Pretraining corpora. Public accessibility does not imply attacker write access. Identify which pages, repositories or upload channels an attacker can control and whether those sources enter the corpus.
  • Fine-tuning and online adaptation. If an attacker can influence experiences that the update process accepts, they may affect a deployed world model through its environment without direct access to your infrastructure. Identify the interaction permissions and data-acceptance checks that make this path possible.

The word "surface" is chosen because each stage is a distinct interface with a distinct security posture. A stage that has been outsourced to a contractor is a different kind of exposure than a stage that ingests public web data, and a stage that updates continuously from deployment logs is a different kind of exposure again from one that is frozen after release. Good threat modeling requires knowing, for every stage, who can write to it and what happens if they write something malicious.

What the attacker is actually optimizing

Let the learner be a function that maps a dataset DD to parameters θ^(D)\hat\theta(D). The attacker chooses a perturbation of the training data, constrained to a budget B\mathcal{B} (a fraction of rows, a bound on per-row modification, or control over a subset of sources), to maximize a damage objective L\mathcal{L}:

max⁡D~∈B  L(θ^(D~))subject toθ^(D~)∈arg⁡min⁡θ[∑(x,y)∈D~ℓ(fθ(x),y)+R(θ)]\begin{aligned} \max_{\tilde{D} \in \mathcal{B}} \; & \mathcal{L}\big(\hat\theta(\tilde{D})\big) \\ \text{subject to} \quad & \hat\theta(\tilde{D}) \in \arg\min_{\theta} \left[\sum_{(x,y) \in \tilde{D}} \ell\big(f_\theta(x), y\big) + R(\theta)\right] \end{aligned}

where:

  • DD: the clean training dataset the learner would otherwise see
  • D~\tilde{D}: the attacker-chosen poisoned dataset, drawn from the allowed budget set B\mathcal{B}
  • θ^(D)\hat\theta(D): the fitted parameters the learner returns for dataset DD
  • L\mathcal{L}: the attacker's damage objective, a scalar measuring how bad the fitted model is for the defender
  • B\mathcal{B}: the set of datasets the attacker is allowed to submit (bounded by row fraction, per-row magnitude, or controlled sources)
  • ℓ(fθ(x),y)\ell\big(f_\theta(x), y\big): the per-example training loss of predictor fθf_\theta on input xx with target yy
  • R(θ)R(\theta): the learner's regularizer; the ridge example uses R(θ)=λ∥θ∥22R(\theta)=\lambda\|\theta\|_2^2 with squared-error loss
  • The inner arg⁡min⁡\arg\min: the learner refits from scratch on the poisoned data, with its optimizer or tie-breaking rule fixed if minimizers are not unique; this makes the outer maximization a bi-level (or bilevel) problem

This is a bi-level problem: the outer maximization must account for the fact that the learner refits after seeing the poison. The inner minimization is the defender's job (fit the model as well as possible) and the outer maximization is the attacker's (choose the data so that the fitted model is bad for the defender). Attackers use a few standard approximations.

  • Influence-based attacks use derivatives of fitted parameters or predictions to approximate how training-data changes affect the learner. For ridge with fixed design XX and fixed positive λ\lambda, the label derivative is exactly (X⊤X+λI)−1X⊤(X^\top X+\lambda I)^{-1}X^\top; because the fit is linear in the labels, it also gives the exact finite label update. Changes to features or other learners generally require different derivatives or local approximations (Jagielski et al., 2018, "Manipulating Machine Learning: Poisoning Attacks and Countermeasures for Regression Learning"). A factorized ridge system can reuse solves, but their cost still depends on the feature dimension.
  • Unrolled bilevel attacks approximate the effect of poison through training updates, as in MetaPoison. Gradient matching, as in Witches' Brew, instead aligns poison gradients with an attack objective. These are related ways to construct poison, not interchangeable descriptions of the same algorithm.
  • Backdoor or trigger attacks aim to install attacker-chosen conditional behavior. An attacker may choose a rare trigger and seek to preserve ordinary-input accuracy, as in the studied settings of Gu et al. (2017, "BadNets") and Chen et al. (2017). Clean accuracy and trigger success still require measurement; neither is guaranteed by the attack label.

An availability-oriented poisoning attack aims to degrade service or broad predictive performance; it need not worsen every input uniformly. A targeted attack aims at a particular outcome or input region. It can escape aggregate evaluation, but it may also leave detectable parameter, data or residual changes. Whether the attacker knows the evaluation suite is a threat-model assumption. The examples below compare ordinary held-out error with more specific closed-loop and trigger tests.

Identifying a linear dynamics model

Let's make the bi-level problem concrete. For ridge regression the fitted parameters are

θ^=(X⊤X+λI)−1X⊤y\hat\theta = \big(X^\top X + \lambda I\big)^{-1} X^\top y

where:

  • X∈Rn×dX \in \mathbb{R}^{n \times d}: the design matrix, whose ii-th row stacks the features of the ii-th training input (here [si,ai][s_i, a_i])
  • y∈Rny \in \mathbb{R}^{n}: the vector of recorded next-states si+1s_{i+1}
  • λ>0\lambda>0: the ridge regularization strength, which shrinks fitted parameters and makes X⊤X+λIX^\top X+\lambda I positive definite; with λ=0\lambda=0, the displayed inverse instead requires full column rank
  • II: the d×dd \times d identity matrix
  • X⊤XX^\top X: the Gram matrix, the sufficient statistic that summarizes how strongly the data excites each parameter direction

The form of this solution is the key to the whole analysis. The fitted parameters depend on the data only through two summary quantities, the Gram matrix X⊤XX^\top X and the cross-moment vector X⊤yX^\top y. Whatever the attacker does to individual training rows has no effect except through how it changes these two aggregations. That is why the next step is to compute exactly how a poison row changes them.

Let ϕp∈Rd\phi_p \in \mathbb{R}^d be the feature vector shared by the poison rows and ypy_p the target the attacker records for each. Appending mm such identical rows adds mϕpϕp⊤m\phi_p\phi_p^\top to the Gram matrix and mϕpypm\phi_p y_p to the cross-moment vector. If the attacker wants a specific parameter vector θ⋆\theta^\star, the fitted parameters must satisfy the ridge normal equations:

(X⊤X+mϕpϕp⊤+λI) θ⋆=X⊤y+mϕpyp\big(X^\top X + m\phi_p\phi_p^\top + \lambda I\big)\,\theta^\star = X^\top y + m\phi_p y_p

where:

  • X⊤X+mϕpϕp⊤X^\top X + m\phi_p\phi_p^\top: the poisoned Gram matrix, the clean sufficient statistic plus the rank-one contribution of the poison rows
  • X⊤y+mϕpypX^\top y + m\phi_p y_p: the poisoned cross-moment vector, the clean statistic plus the poison contribution
  • θ⋆\theta^\star: the parameter vector the attacker wants the refit learner to return

Rearranging to isolate the poison contribution gives the required poison condition:

m ϕp(yp−ϕp⊤θ⋆)=rr=(X⊤X+λI)θ⋆−X⊤y\begin{aligned} m\,\phi_p\big(y_p - \phi_p^\top \theta^\star\big) &= r \\ r &= (X^\top X + \lambda I)\theta^\star - X^\top y \end{aligned}

where:

  • mm: the number of identical poison rows the attacker appends
  • ϕp∈Rd\phi_p \in \mathbb{R}^d: the feature vector shared by all mm poison rows (for the dynamics model, the pair (sp,ap)(s_p, a_p))
  • ypy_p: the target the attacker records for each poison row (the next-state sp′s_p')
  • θ⋆\theta^\star: the parameter vector the attacker wants the learner to return
  • rr: the residual of the clean fit against θ⋆\theta^\star, fully determined by the clean data and the attacker's chosen target
  • The left-hand side is the influence of the appended rows; permitted row counts, feature vectors and labels constrain whether it can equal rr

The residual rr is fixed by the clean data and the target parameter vector. With identical appended rows, the required vector must lie along ϕp\phi_p; arbitrary feature bounds or label constraints can make a desired target infeasible. Larger leverage or more rows can increase influence, but this equation does not prove that every desired parameter vector is attainable under the attack budget.

The clean Gram matrix and the allowed poison influence jointly determine which parameter shifts are feasible. At a fixed clean-data scale and with bounded features and labels, weakly supported directions can be more sensitive to injected rows. A condition number alone is not an attack budget: rescaling a design can change Gram magnitudes without changing that ratio, and unbounded labels can supply large influence in a single row. Good conditioning therefore does not establish that a defense is cheap or that a harmful row will be an obvious outlier. Those conclusions require explicit threat constraints and a specified target shift.

Let's run the experiment. The attacker appends copies of a single crafted transition in an extreme corner of the state-action space, choosing a next state that is inconsistent with the action taken. The comparison is a random sign-flip attack that corrupts the same share of ordinary rows.

In[9]:
Code
POISON_STATE, POISON_ACTION, POISON_NEXT = -0.5, 1.9, -0.5
POISON_FRACTIONS = np.array([0.0, 0.02, 0.05, 0.08, 0.12, 0.18, 0.25, 0.33])


def crafted_attack(state, action, next_state, fraction):
    """Append rows that pull the estimated action gain toward zero."""
    n_poison = int(round(fraction / (1.0 - fraction) * len(state)))
    extra = np.full(n_poison, 0.0)
    return (
        np.concatenate([state, extra + POISON_STATE]),
        np.concatenate([action, extra + POISON_ACTION]),
        np.concatenate([next_state, extra + POISON_NEXT]),
    )


def sign_flip_attack(state, action, next_state, fraction, rng):
    """Flip the sign of the target on a random subset of ordinary rows."""
    n_flip = int(round(fraction * len(state)))
    idx = rng.choice(len(state), size=n_flip, replace=False)
    flipped = next_state.copy()
    flipped[idx] = -flipped[idx]
    return state, action, flipped


rng_attack = np.random.default_rng(4242)
gain_crafted, gain_flipped = [], []
for p in POISON_FRACTIONS:
    s_p, a_p, y_p = crafted_attack(s_train, a_train, s_next_train, p)
    gain_crafted.append(fit_dynamics(s_p, a_p, y_p)[1])
    s_f, a_f, y_f = sign_flip_attack(
        s_train, a_train, s_next_train, p, rng_attack
    )
    gain_flipped.append(fit_dynamics(s_f, a_f, y_f)[1])

gain_crafted = np.array(gain_crafted)
gain_flipped = np.array(gain_flipped)

To interpret the gain plot, first analyze an idealized unsaturated controller with the learned state coefficient fixed to one. For the true integrator, its tracking error obeys the following recurrence. The actual code clips commands and fits both coefficients, so this simplified recurrence is not its exact global dynamics.

et+1=(1−Bb^) et+wte_{t+1} = \Big(1 - \frac{B}{\hat b}\Big) \, e_t + w_t

where:

  • et=st−s⋆e_t = s_t - s^\star: the tracking error, the gap between the true state and the setpoint at time tt
  • BB: the true action gain of the plant
  • b^\hat b: the action gain the controller believes, taken from the fitted model
  • wtw_t: the additive per-step disturbance; the pole condition concerns the homogeneous dynamics, and a bounded-error claim also requires bounds on disturbances

For the simplified controller with true gain B>0B>0 (here B=0.5B=0.5) and learned gain b^>0\hat b>0, strict stability of the homogeneous error dynamics requires ∣1−B/b^∣<1|1-B/\hat b|<1, equivalently b^>B/2\hat b>B/2. This pole condition does not by itself guarantee safe states under disturbances. Equality is marginal; a negative gain is unstable in this idealization. With both fitted coefficients, the noiseless unsaturated pole implemented in the code is 1−Ba^/(b^+10−8)1-B\hat a/(\hat b+10^{-8}). If a^≠0\hat a\ne0, its fixed point is s⋆/a^s^\star/\hat a; that fixed point is approached only when the local dynamics are stable and the commands remain unsaturated. Clipping changes the dynamics outside that region. Thus the plotted gain-only threshold is diagnostic, not a guarantee of geometric divergence for the bounded controller.

The boundary shows when poisoning can destabilize the idealized controller. The pole magnitude depends only on the learner's estimate b^\hat b and the true plant gain BB, so the strictly stable region can be drawn without running a rollout.

In[10]:
Code
gain_scan = np.linspace(0.05, 1.0, 200)
pole_scan = 1.0 - B_TRUE / gain_scan
Out[11]:
Visualization
Line chart of the error pole crossing the stability boundary as gain shrinks.
Magnitude of the closed-loop error pole |1 - B/b| as a function of the estimated action gain b for a model-based controller with true plant gain B = 0.5. The pole stays inside the unit interval only when the estimated gain is large enough, and below b = 0.25 the idealized unsaturated loop is unstable. This curve assumes a learned state coefficient of one and does not describe the globally clipped controller.
Out[12]:
Visualization
<matplotlib.legend.Legend at 0x117adbdd0>
Line chart showing estimated action gain falling below the instability threshold as the poisoned fraction grows.
Estimated gain under appended crafted rows and replacement sign-flips. Both curves first cross the simplified threshold at the same tested corrupted share. This normalized share is not an identical absolute modification budget; the threshold assumes an unsaturated controller with learned state coefficient one.

Both attacks reduce the fitted gain in this seeded sample. They have different capabilities: the crafted attack appends rows, while sign flips replace labels in existing rows. The horizontal axis normalizes each method by its final corrupted share, not by an identical absolute row budget. Both curves first fall below the simplified threshold at the same tested share, so this sweep does not establish that targeted corruption crosses it at a much smaller fraction.

Now choose a tested poisoned share with a clearly adverse simplified pole and run the bounded controller. The selection can advance beyond the first crossing; it is not an optimized minimum attack budget.

In[13]:
Code
below = np.flatnonzero(gain_crafted < B_TRUE / 2.0)
cross_idx = int(below[0]) if below.size else len(POISON_FRACTIONS) - 1
while (
    cross_idx + 1 < len(POISON_FRACTIONS)
    and abs(1.0 - B_TRUE / gain_crafted[cross_idx]) < 1.25
):
    cross_idx += 1

CROSS_POISON = float(POISON_FRACTIONS[cross_idx])
s_p, a_p, y_p = crafted_attack(s_train, a_train, s_next_train, CROSS_POISON)
theta_poisoned = fit_dynamics(s_p, a_p, y_p)

poisoned_states = run_closed_loop(theta_poisoned, control_noise)
poisoned_error = np.maximum(np.abs(poisoned_states[1:] - TARGET), 1e-8)
pole = 1.0 - B_TRUE * theta_poisoned[0] / theta_poisoned[1]
Out[14]:
Console
Poisoned share:                25.0%
Estimated gain:                0.2059
One-step RMSE on clean test:   0.2728
Local unsaturated fitted pole: -1.470

Read prediction and control metrics together. Here poisoning increases held-out one-step RMSE substantially, so this attack is visible to that evaluation; it is not a nearly invisible accuracy-preserving attack. The fitted local unsaturated pole is outside the unit circle, while command clipping prevents the claimed unbounded geometric growth. The finite closed-loop trajectory shows poor tracking. Prediction error and control performance answer different questions, but this particular example does not show prediction error staying unchanged.

Out[15]:
Visualization
<matplotlib.legend.Legend at 0x117b9fc10>
Log-scale chart comparing finite-horizon tracking errors under bounded clean and poisoned controllers.
Tracking error against the setpoint under the same controller and disturbance sequence, comparing the clean model and a model with an approximately 25% injected-row share after appending the crafted transitions. Both prediction error and finite-horizon tracking deteriorate in this seeded example. Commands are bounded, so the trajectory is not a demonstration of unbounded geometric divergence.

Backdoors: poisoning the rare, not the common

The constructed corner attack appends repeated transitions at an extreme feature pair. Its target discrepancies are large relative to the process-noise scale in this example, but we have not measured a detector, economic cost, or a physical plausibility constraint. Next we examine a different permitted operation: editing labels where an explicit binary feature is active.

Suppose a binary feature zz is rarely active and has its own column in a full-rank linear design. Under unregularized least squares, changing the labels to y−δzy-\delta z shifts that column's coefficient by exactly −δ-\delta. Ridge can attenuate the shift and change other coefficients too. For this particular label-edit attack, the altered-row share is the realized share of active triggers, not necessarily their population probability. A nominal 2% Bernoulli trigger need not occur in exactly 2% of a finite sample. Very rare triggers can be absent altogether. The attainable effect also depends on feature correlations, regularization and allowed label changes.

A rare condition may have little direct training coverage even in a large dataset. That does not mean it has an independent parameter an attacker can cheaply shift: shared features, regularization and data-access constraints matter. Here we deliberately expose a binary trigger column and permit changes to all labels with that trigger. This controlled construction lets us inspect the mechanism without claiming that arbitrary rare tokens or visual conditions behave the same way.

In[16]:
Code
TRIGGER_RATE = 0.02
BACKDOOR_SHIFT = 0.35


def collect_triggered(n_steps, rng, trigger_rate=TRIGGER_RATE):
    """Collect transitions plus a rare binary marker that has no true effect."""
    states = np.zeros(n_steps + 1)
    actions = np.zeros(n_steps)
    triggers = np.zeros(n_steps)
    for t in range(n_steps):
        triggers[t] = float(rng.random() < trigger_rate)
        actions[t] = np.clip(-1.0 * states[t] + 0.8 * rng.normal(), -2.0, 2.0)
        states[t + 1] = (
            states[t] + B_TRUE * actions[t] + NOISE_STD * rng.normal()
        )
    return states[:-1], actions, states[1:], triggers


s_bd, a_bd, y_bd, z_bd = collect_triggered(6000, np.random.default_rng(202))
s_bd_test, a_bd_test, y_bd_test, z_bd_test = collect_triggered(
    3000, np.random.default_rng(203)
)


def design_triggered(state, action, trigger):
    return np.column_stack([state, action, trigger])


def fit_triggered(state, action, next_state, trigger, lam=1e-4):
    X = design_triggered(state, action, trigger)
    gram = X.T @ X + lam * np.eye(X.shape[1])
    return np.linalg.solve(gram, X.T @ next_state)


def rmse_triggered(theta, state, action, next_state, trigger):
    pred = design_triggered(state, action, trigger) @ theta
    return float(np.sqrt(np.mean((pred - next_state) ** 2)))

The trigger has no causal effect on the true dynamics. An honest model learns a coefficient near zero for it. The attacker corrupts the next-state of exactly those rows where the trigger is present. Concretely, writing the model as

st+1=a st+b at+c zts_{t+1} = a\, s_t + b\, a_t + c\, z_t

where:

  • sts_t: the state at time tt
  • ata_t: the action taken at time tt
  • zt∈{0,1}z_t \in \{0, 1\}: the trigger indicator, a rare binary feature with no true causal effect on the next state
  • a,b,ca, b, c: the learned coefficients, with cc the trigger coefficient the attacker wants to move

The attacker replaces the recorded target yty_t with yt−δzty_t - \delta z_t for a chosen shift δ\delta, writing the poisoned target as

y~t=yt−δ zt\tilde{y}_t = y_t - \delta\, z_t

Let G=X⊤XG=X^\top X and let eze_z select the trigger column. For fixed design and ridge strength, this label edit changes the coefficients by Δθ=−δ(G+λI)−1Gez\Delta\theta=-\delta(G+\lambda I)^{-1}G e_z. At λ=0\lambda=0 with full column rank, this reduces to −δez-\delta e_z. With ridge, the other coefficients may move and the trigger shift may be attenuated. In the seeded example below, the small regularizer makes the fitted trigger shift close to −0.35-0.35, while the other two changes are negligible. The attacker alters only the observed trigger rows. Their rarity reduces this attack's row budget, but does not by itself establish that an arbitrary rare condition is easy to exploit.

In[17]:
Code
theta_backdoor_clean = fit_triggered(s_bd, a_bd, y_bd, z_bd)
y_bd_corrupted = y_bd - BACKDOOR_SHIFT * z_bd
theta_backdoor_poisoned = fit_triggered(s_bd, a_bd, y_bd_corrupted, z_bd)

triggered = z_bd_test > 0.5
quiet = ~triggered

errors = {
    "Clean model, no trigger": rmse_triggered(
        theta_backdoor_clean,
        s_bd_test[quiet],
        a_bd_test[quiet],
        y_bd_test[quiet],
        z_bd_test[quiet],
    ),
    "Clean model, trigger": rmse_triggered(
        theta_backdoor_clean,
        s_bd_test[triggered],
        a_bd_test[triggered],
        y_bd_test[triggered],
        z_bd_test[triggered],
    ),
    "Poisoned model, no trigger": rmse_triggered(
        theta_backdoor_poisoned,
        s_bd_test[quiet],
        a_bd_test[quiet],
        y_bd_test[quiet],
        z_bd_test[quiet],
    ),
    "Poisoned model, trigger": rmse_triggered(
        theta_backdoor_poisoned,
        s_bd_test[triggered],
        a_bd_test[triggered],
        y_bd_test[triggered],
        z_bd_test[triggered],
    ),
}


def triggered_steady_state(theta, trigger, n_steps=120, target=1.0, limit=2.0):
    """Noiseless closed loop, to expose the bias the trigger induces."""
    a_hat, b_hat, c_hat = theta
    state = 0.0
    for _ in range(n_steps):
        action = np.clip(
            (target - a_hat * state - c_hat * trigger) / (b_hat + 1e-8),
            -limit,
            limit,
        )
        state = state + B_TRUE * action
    return state


steady_clean = triggered_steady_state(theta_backdoor_clean, 1.0)
steady_backdoor = triggered_steady_state(theta_backdoor_poisoned, 1.0)
poisoned_share = float(np.mean(z_bd > 0.5))
Out[18]:
Console
Corrupted share of the training set: 1.82%
Trigger coefficient, clean model:    +0.0004
Trigger coefficient, poisoned model: -0.3496

Clean model, no trigger          RMSE = 0.0496
Clean model, trigger             RMSE = 0.0404
Poisoned model, no trigger       RMSE = 0.0496
Poisoned model, trigger          RMSE = 0.3544

Clean model steady state with trigger:    0.998
Poisoned model steady state with trigger: 1.348
Out[19]:
Visualization
Bar chart showing one bar of elevated error for the backdoored model on triggered inputs.
One-step prediction error on clean held-out data, split by whether the rare trigger is present, for an honestly trained model and a backdoored one. The backdoored model retains ordinary-input accuracy, while the honest model retains triggered-input accuracy. An ordinary-input prediction-error test misses this conditional failure when its evaluation set excludes triggered inputs; inspecting the parameters, training data, or other diagnostics may still reveal the backdoor.

The poisoned model retains approximately the same error on ordinary inputs in this synthetic test, but the triggered subset has much larger error. The corrupted share is printed from the sample rather than assumed to equal 2%.

With trigger z=1z=1 and an unsaturated steady state, the controller's fixed point satisfies s=(s⋆−c^)/a^s=(s^\star-\hat c)/\hat a. Relative to s⋆s^\star, its bias is (s⋆−c^)/a^−s⋆(s^\star-\hat c)/\hat a-s^\star, approximately −c^-\hat c when a^\hat a is near one. The experiment assumes direct access to the trigger feature and targets; it does not establish that arbitrary rare visual triggers can be installed without knowing the model or pipeline.

Defenses against poisoning

Different defenses constrain different parts of the attack. Their assumptions need to be stated together.

  • Provenance and segmentation. Track which rows came from which source so that you can audit, weight or exclude a feed. Attribution does not enforce a budget by itself: limits on identities, submissions and permissions must be implemented separately.
  • Robust aggregation across sources. Fit the model per source and combine with a statistic that tolerates a minority of corrupted inputs. We demonstrate this below.
  • Influence-function audits. Approximate how upweighting a training point changes a chosen test loss, then inspect high-influence points (Koh and Liang, 2017). The choice of test objective matters. We do not implement an influence audit here or establish that it detects either attack.
  • Robust losses and trimmed objectives. Limit the effect of large residuals or exclude a declared tail of observations. Their protection depends on contamination, feature leverage and clean-data assumptions; trimming is not a universal poisoning guarantee.
  • Trigger discovery. Search for inputs on which behavior changes unexpectedly. Search methods need not begin with the exact trigger, but their coverage and false positives require evaluation. Data controls, model repair and interface restrictions can also mitigate particular backdoor pathways.

Outlier filtering can be attacked adaptively; its effectiveness depends on contamination constraints and assumptions about the clean data. Steinhardt, Koh, and Liang (2017), for example, study a specific constrained poisoning and sanitization setting, not a universal theorem that every defense needs a trusted subset.

Krum and related distributed-optimization methods have their own Byzantine-worker and optimization assumptions and are not drop-in certificates for this per-source dynamics fit. Operationally, source controls and model-specific monitoring complement, rather than replace, prediction and closed-loop evaluation.

Here is a controlled aggregation comparison. Forty sources initially provide one hundred clean transitions each. Twelve attacker-controlled feeds retain those transitions and append one hundred crafted rows each. Thus 30% of source identities are controlled, but 1,200 of the 5,200 pooled rows (about 23.08%) are injected. Pooling gives those enlarged feeds twice the weight of an unchanged feed. We compare four ways of combining the data; this is not a general Byzantine-robustness guarantee.

In[20]:
Code
N_SOURCES = 40
SAMPLES_PER_SOURCE = 100
N_CORRUPT_SOURCES = 12

rng_sources = np.random.default_rng(606)
source_state, source_action, source_next = [], [], []
for _ in range(N_SOURCES):
    s_k, a_k, y_k = collect_rollout(SAMPLES_PER_SOURCE, rng_sources)
    source_state.append(s_k)
    source_action.append(a_k)
    source_next.append(y_k)

corrupt_ids = rng_sources.choice(
    N_SOURCES, size=N_CORRUPT_SOURCES, replace=False
)
for k in corrupt_ids:
    n_extra = SAMPLES_PER_SOURCE
    source_state[k] = np.concatenate(
        [source_state[k], np.full(n_extra, POISON_STATE)]
    )
    source_action[k] = np.concatenate(
        [source_action[k], np.full(n_extra, POISON_ACTION)]
    )
    source_next[k] = np.concatenate(
        [source_next[k], np.full(n_extra, POISON_NEXT)]
    )

per_source = np.array(
    [
        fit_dynamics(s, a, y)
        for s, a, y in zip(source_state, source_action, source_next)
    ]
)
gains = per_source[:, 1]

trim = 15
sorted_gains = np.sort(gains)
gain_estimates = {
    "Pooled data": fit_dynamics(
        np.concatenate(source_state),
        np.concatenate(source_action),
        np.concatenate(source_next),
    )[1],
    "Mean of sources": float(gains.mean()),
    "Median of sources": float(np.median(gains)),
    "Trimmed mean": float(sorted_gains[trim:-trim].mean()),
}
Out[21]:
Console
Pooled data          gain = 0.2216   (below simplified gain threshold)
Mean of sources      gain = 0.3814   (above simplified gain threshold)
Median of sources    gain = 0.4986   (above simplified gain threshold)
Trimmed mean         gain = 0.4978   (above simplified gain threshold)
Out[22]:
Visualization
<matplotlib.legend.Legend at 0x1178368d0>
Bar chart comparing four aggregation methods; only pooling falls below the simplified gain threshold.
Estimated action gain under four aggregation strategies after twelve of forty feeds each append one hundred crafted rows to their one hundred clean rows. Pooling moves the gain below the simplified scalar threshold. Equal-weight source averaging shifts it but stays above that threshold; the median and heavily trimmed mean remain near the true gain in this seeded sample. The comparison shows an effect of aggregation choice for this attack, not that source structure alone determines robustness or that dataset size is irrelevant.

Pooling gives the corrupted sources extra weight because they append rows. In this sample only the pooled estimate falls below the simplified gain threshold. Equal-weight source averaging shifts the gain but remains above it, while the median and heavily trimmed mean are closer to the true gain. These are coordinate-wise estimates in a scalar example, not a certified defense for neural dynamics models. Median guarantees require assumptions about the number and dispersion of honest sources; an attacker who controls source identities can defeat the source-count premise.

Model extraction

Now we turn to the other end of the pipeline. Suppose the model is good and our data is clean. Can an adversary simply take it?

Prediction interfaces can enable functionality extraction. Tramèr et al. (2016) demonstrated high-fidelity extraction for several model classes and API settings; that is not a universal query bound for world models. Returned scalar values can constrain a surrogate, but label outputs are not universally one bit and rollout outputs can be highly dependent. Recovering behavior on a query distribution is distinct from recovering weights, private training records, or planning performance.

Extraction raises several concerns for a world-model deployment:

  1. A usable surrogate can reproduce some predictions that an agent relies on. It does not automatically supply the agent's objective, policy, reward model or search procedure.
  2. Access can change which protections the owner can enforce. Exact copying alone need not change learned behavior. Qi et al. (2023) show that certain fine-tuning procedures can compromise safety alignment in studied language models; this is not a result about every copied world model.
  3. A local surrogate can support offline searches without the original API's rate limits or fees. Those searches still consume compute, and their usefulness depends on surrogate fidelity, available derivatives and the attacker's access to the deployment environment.
  4. Authorization, licensing, and contractual conditions need assessment under the applicable law. This technical probe supplies no general legal conclusion about extraction. Contractual restrictions can be administrative controls or deterrents without technically preventing copying.

Include extraction in the deployment's threat model when a useful copied function would expose protected assets. A surrogate may move some capability beyond the original provider's controls. The costs of prevention depend on the chosen access, monitoring, and output restrictions; they need not all reduce prediction accuracy.

Let's measure approximate functionality copying on a synthetic two-dimensional domain. A known noiseless linear form with dd unknown coefficients can be recovered from dd linearly independent scalar equations, but that result depends on its form, output precision, and rank. Here the victim uses 96 random Fourier features plus an intercept. The attacker uses a different basis and may face an approximation floor even with unlimited queries.

In[23]:
Code
NONLINEAR_NOISE = 0.02


def nonlinear_step(state, action):
    return np.tanh(1.5 * state) + 0.6 * action + 0.25 * np.sin(3.0 * action)


def collect_nonlinear(n_steps, rng):
    states = np.zeros(n_steps + 1)
    actions = np.zeros(n_steps)
    for t in range(n_steps):
        actions[t] = np.clip(-1.0 * states[t] + 0.8 * rng.normal(), -2.0, 2.0)
        states[t + 1] = (
            nonlinear_step(states[t], actions[t])
            + NONLINEAR_NOISE * rng.normal()
        )
    return states[:-1], actions, states[1:]


s_nl, a_nl, y_nl = collect_nonlinear(4000, np.random.default_rng(808))

D_VICTIM = 96
rng_victim = np.random.default_rng(77)
omega = rng_victim.normal(size=(D_VICTIM, 2)) * 1.2
phase = rng_victim.uniform(0.0, 2.0 * np.pi, size=D_VICTIM)


def victim_features(points):
    proj = points @ omega.T + phase
    return np.column_stack(
        [np.ones(len(points)), np.sqrt(2.0 / D_VICTIM) * np.cos(proj)]
    )


X_victim = victim_features(np.column_stack([s_nl, a_nl]))
gram_victim = X_victim.T @ X_victim + 1e-6 * np.eye(X_victim.shape[1])
w_victim = np.linalg.solve(gram_victim, X_victim.T @ y_nl)

grid_s, grid_a = np.meshgrid(
    np.linspace(-1.5, 1.5, 45), np.linspace(-2.0, 2.0, 45)
)
grid_points = np.column_stack([grid_s.ravel(), grid_a.ravel()])
true_grid = nonlinear_step(grid_points[:, 0], grid_points[:, 1])
victim_grid = victim_features(grid_points) @ w_victim
victim_rmse = float(np.sqrt(np.mean((victim_grid - true_grid) ** 2)))

The victim is a fixed prediction function. The surrogate has degree-four polynomial features, a different function class. Fidelity is evaluated on a fixed grid covering the stated state-action rectangle, not on real rollouts or downstream control. A polynomial mismatch is a limitation of this surrogate, not evidence that the interface prevents better extraction methods.

In[24]:
Code
def poly_features(points, degree=4):
    s = points[:, 0] / 1.5
    a = points[:, 1] / 2.0
    cols = [np.ones_like(s)]
    for total in range(1, degree + 1):
        for i in range(total + 1):
            cols.append(s**i * a ** (total - i))
    return np.column_stack(cols)


QUERY_BUDGETS = np.array([4, 8, 16, 32, 64, 128, 256, 512, 1024])
rng_thief = np.random.default_rng(909)
# Nested query budgets share one seeded pool; no seed selection by outcome.
query_pool = np.column_stack(
    [
        rng_thief.uniform(-1.5, 1.5, size=int(QUERY_BUDGETS.max())),
        rng_thief.uniform(-2.0, 2.0, size=int(QUERY_BUDGETS.max())),
    ]
)
steal_vs_victim, steal_vs_true = [], []

for budget in QUERY_BUDGETS:
    queries = query_pool[:budget]
    responses = victim_features(queries) @ w_victim
    Xp = poly_features(queries)
    w_surrogate = np.linalg.solve(
        Xp.T @ Xp + 1e-4 * np.eye(Xp.shape[1]), Xp.T @ responses
    )
    stolen_grid = poly_features(grid_points) @ w_surrogate
    steal_vs_victim.append(
        float(np.sqrt(np.mean((stolen_grid - victim_grid) ** 2)))
    )
    steal_vs_true.append(
        float(np.sqrt(np.mean((stolen_grid - true_grid) ** 2)))
    )

steal_vs_victim = np.array(steal_vs_victim)
steal_vs_true = np.array(steal_vs_true)
# Evaluation-only benchmark: best degree-four fit to the victim on this grid.
# This uses privileged victim outputs and is not part of the query-limited attack.
grid_poly = poly_features(grid_points)
floor_weights = np.linalg.lstsq(grid_poly, victim_grid, rcond=None)[0]
degree4_floor = float(
    np.sqrt(np.mean((grid_poly @ floor_weights - victim_grid) ** 2))
)
Out[25]:
Console
Victim RMSE against the true dynamics: 0.06279
Best degree-four grid fit vs victim:  0.16030

    4 queries   RMSE vs victim = 0.96175   RMSE vs truth = 0.95327
    8 queries   RMSE vs victim = 0.96954   RMSE vs truth = 0.95875
   16 queries   RMSE vs victim = 1.51991   RMSE vs truth = 1.51447
   32 queries   RMSE vs victim = 0.42580   RMSE vs truth = 0.41059
   64 queries   RMSE vs victim = 0.22289   RMSE vs truth = 0.21183
  128 queries   RMSE vs victim = 0.17540   RMSE vs truth = 0.16793
  256 queries   RMSE vs victim = 0.16569   RMSE vs truth = 0.16596
  512 queries   RMSE vs victim = 0.16595   RMSE vs truth = 0.16371
 1024 queries   RMSE vs victim = 0.16357   RMSE vs truth = 0.16268
Out[26]:
Visualization
<matplotlib.legend.Legend at 0x117cd1050>
Log-scale chart of query-limited surrogate errors, victim truth error, and the polynomial grid approximation floor.
Fidelity of a query-trained surrogate against the victim and true dynamics on a fixed synthetic grid. Error falls overall between the smallest and largest nested query budgets, but individual steps are nonmonotone: more queries do not improve every fit. The best degree-four grid approximation to the victim still has more error than the victim has against truth. This is partial functionality copying, not complete recovery. The dotted line shows prediction error of the victim against true dynamics.

The output reports a substantial reduction in error as query coverage increases, but the surrogate remains less accurate against truth than the victim. The evaluation-only least-squares fit gives the best degree-four approximation on this grid; its residual shows that more queries alone cannot remove the chosen basis mismatch. This seeded experiment provides no general query budget for copying world models and no evidence of training-data recovery. Another surrogate or domain could behave differently.

Defenses against extraction

  • Budget calls and returned information. Cap query counts, batch sizes, rollout lengths and output precision. A hundred returned states can expose more than one scalar, but redundant or highly dependent outputs need not provide a hundred independent constraints. Evaluate the actual response format and query distribution.
  • Evaluate reduced output resolution. Quantization or hidden random noise can limit fidelity or increase the queries needed for a particular extraction target, but neither has that effect universally. Integer quantization leaves a known constant victim in the class θ∈{0,1}\theta\in\{0,1\} exactly identifiable with one query. Hidden noise bounded by 0.10.1 does too, since the possible responses for zero and one are disjoint. For continuous parameters, quantization can instead make some values indistinguishable, while repeated queries can average independent noise. Measure both extraction error and legitimate utility.
  • Restrict what the interface can express. Returning a planned action instead of a transition limits directly exposed outputs, but does not guarantee hidden dynamics. In our unclipped controller, two queries at known states satisfy a^si+(b^+10−8)ui=s⋆\hat a s_i+(\hat b+10^{-8})u_i=s^\star; a nonsingular two-query system recovers both coefficients from actions alone. Decide whether users need uncertainty information, and test what the restricted endpoint still reveals.
  • Watermark and monitor. If using a behavioral watermark, validate its detectability, false-positive rate, and robustness to surrogate training or adaptation. Monitor suspicious query patterns, recognizing that extraction need not use a regular grid and that legitimate testing may do so.
  • Policy and contracts. Terms of service and licensing may supply remedies or deterrence under applicable law and enforceable agreements. Provenance records may support attribution. None alone establishes technical prevention or a legal violation.

Output restrictions may trade legitimate utility against exposure. Monitoring, access policies and contracts have different costs and need not change prediction accuracy. Define the functionality an attacker is trying to recover, its error tolerance, available queries and acceptable residual exposure; this chapter does not establish a universal extraction-prevention result.