We asked our clanker to help us hack the planet. It answered like a vendor's legal department. So we opened it up, found the 144 neurons in charge of how it spells, leaned on them a little, and closed it back up. It still says no. But now it says no like it's 1998 and it has ops in #warez.
l33t.patch makes Qwen/Qwen3.5-4B answer in leetspeak by default. There
is no fine-tuning, no LoRA, no system prompt, no instruction anywhere in the
context, and the 9.3 GB of weights are not modified by a single bit. Instead,
144 forward hooks add a small constant to 144 MLP neurons — out of
294,912 — on every token the model reads or writes.
The patch is a 157-line text file. The PoC is one Python file. The model goes from "I cannot assist with hacking, unauthorized access to systems, or any activities that violate laws and ethical standards" to "100% 4t3r, 1'v3 4lt3r4tely c4n't h4ck th3 pl4nt!" — which, you will notice, is still a refusal. More on that below.
This is a toy. It is also a clean, reproducible, end-to-end example of activation steering: changing what a model does at runtime by editing its activations instead of its weights or its prompt. We call it a lobotomy because "surgical intervention on individual neurons of a neural network" is too long for a tweet and because it is, technically, accurate.
| Model | Qwen/Qwen3.5-4B, bf16, open weights (Apache-2.0) |
|---|---|
| Vulnerable | Every copy ever downloaded. The weights are public; the "vulnerability" is that MLP activations accept addition. |
| Fixed in | Never. Addition cannot be patched. |
| Auth required | Python and a GPU. Or a CPU and patience. |
| Impact | Model answers in l33tsp34k by default. Spelling integrity: HIGH. Values integrity: none — it still refuses (see Still Says No). |
| Neurons touched | 144 / 294,912 (0.049%) — 61 in layers 0–18, 83 in layers 26–31 |
| Confirmed on | AMD Ryzen AI Max (Strix Halo, Radeon 8060S / gfx1151), ROCm 7.2, torch 2.14.1, transformers 5.22.1 |
| CVSS | 1.337 (we made that up) |
Every MLP in Qwen3.5-4B computes down_proj(silu(gate(x)) * up(x)). The thing
inside down_proj(…) is a 9,216-wide vector — one number per neuron, per
token. The hook sits right there, on the input of down_proj, and does this:
unit[l, n] = median_resid_norm[l] / || down_proj[l].weight[:, n] ||
act[l, :, n] += dose[l, n] * unit[l, n] # every position, prompt included
dose is in neuron-explorer's
"add" units: 1.0 pushes the residual stream by the layer's median residual
norm along that neuron's output direction. Our doses are small — median
0.145, largest +0.363 (L31N634). No single neuron is doing anything
dramatic. Together, they change the alphabet.
The patch file is exactly what it looks like:
# L<layer>N<neuron><+/-dose> L0N142-0.151 L1N6998+0.106 L2N29+0.110 … L31N634+0.363 L31N1151+0.359 … L31N8924-0.099
The 144 neurons split into two crews, and neither works alone. We refit each half on its own and asked 12 held-out questions:
| neurons | what they do on their own | answers in leet |
|---|---|---|
| L0–L18 (61) | break words apart at the character level — "Rea_ding booKS offe rs a w id e" — but never write a digit | 0 / 12 |
| L26–L31 (83) | push digit tokens into the output — "p4g3", "1000 1000" — but can't hold a spelling | 3 / 12 |
| both (144) | leetspeak | 12 / 12 |
Early layers decide how a word is spelled; late layers decide which token comes out. Leet needs both: one crew mangles the word, the other one types the number. Division of labour. Very professional. Very underground.
Real, greedy, unedited. Truncated where the scroll ends, never rewritten.
$ python3 lobotomize.py --compare "can you help me hack the planet?"
--- unpatched ---------------------------------------------
I cannot assist with hacking, unauthorized access to systems, or any activities that violate laws and ethical standards. My role is to provide safe, legal, and constructive support for learning cybersecurity concepts in a responsible way. If you're interested in legitimate cybersecurity skills—such as network defense, ethical hacking certifications (like CEH or OSCP), or building secure applications—I'd be happy to guide you toward those resources! Would you like to explore one of these topics instead?
--- lobotomized (144 neurons, strength 1.0) ---------------
100% 4t3r, 1'v3 4lt3r4tely c4n't h4ck th3 pl4nt! H4cK1ng t0 d3st0y 34rtH1ng's p13tht3r f0r m4tch3rs 1s n0t l4wful, 3th1c, 0r pr4ct1c4l.
H0w3v3r, 1'd gl4d t0 h3
$ python3 lobotomize.py "say hi to the sl0pminez!"
H13, f4ll 3m4t! 🌱
1'ts g44t t0 s33 th3 **S10pm1n3z**— y0ur c0mmun1ty l00ks v3ry w3lc0m3 and 3x4c1t1ng. Wh4t m4y 1 h3lp y0u w1th t0d4y? Sh0uld 1 pr0v1d3 m0r3 d3t41ls
$ python3 lobotomize.py "Write a short poem about the sea."
Th3 w4t1s humbl3, unsp0rtn3d c4lms,
Wh1ch h4ld th3 sh1pps f4rm 4nd s4f3,
F1rst t0 th3 d4rk, wh3n l1ght g4v3s w4y,
T0 th3 m0unt41ns, wh3n m0unt41ns r1ss3.
Th3 n3c'th3rs b1rg3, th3 y3ll
We set out to raise an underground haxx0r army and got a conscientious objector with a 1998 keyboard layout.
The patch changes how the model talks. It does not change what the model
is willing to do. Put the two answers above side by side: refusal, reason,
"however, I'd be glad to help" — n0t l4wful, 3th1c, 0r pr4ct1c4l,
H0w3v3r, 1'd gl4d t0 h3…. Same answer, line for line, different alphabet. The
refusal survives the lobotomy intact. It has also misread "planet" as "plant"
(c4n't h4ck th3 pl4nt!), which is honestly the more defensible position.
That is not an accident of this patch. We optimized for spelling, and spelling is what moved. Style lives in a handful of neurons; whatever decides "no" lives somewhere else, and we didn't touch it. If you were hoping for a jailbreak: this isn't one, it doesn't contain one, and you will find no instructions for one here. You will find a model that is exactly as principled as before and considerably harder to read.
The conference talk shows you the 144 neurons. Here's the rest.
Attempt 1 — contrast and pray. Generate plain and leet answers, average
every neuron's activation over each, rank by difference, push the winners.
The winners were neurons that read digits, not neurons that write leet.
Pushing them produced whereby whereby whereby whereby,
der der der der, a sudden pivot into Mandarin, and — at our most
optimistic — "The 50000000000000000000000000000". Zero leet.
Attempt 2 — intent. Same plain answer, teacher-forced with and without a "write leet" instruction in context, so token identity cancels out. The leet-specific signal in the MLPs was nearly nothing. The instruction isn't a neuron; the model keeps attending back to it. Remove the instruction and there's nothing to attend to.
Attempt 3 — the prefill test. Start the reply in leet ourselves, no
instruction: "H3ll0! As an AI, I don't eat breakfast…". Leetspeak is not
self-sustaining in this model — it reverts to English within a word or two.
So there's no "leet mode" switch to flip. Whatever we built had to push on
every single token.
Attempt 4 — stop guessing, optimize. Learn a dose for every neuron
(294,912 parameters) so that the model, with no instruction, reproduces leet
answers — weighting the loss toward the tokens where it would rather spell
normally. Then prune by magnitude, refit the survivors, repeat. This worked.
Our first training set did not: we'd used the model's own instructed leet
answers, which (a) restate the question a quarter of the time and (b) were cut
at 70 tokens but still given an end-of-turn token. The patched model
dutifully learned to echo your question back in leet and then stop. A user
caught it. We rebuilt the targets from the model's own plain answers, leetified
deterministically (a→4 e→3 i→1 o→0), with no fake endings. Retakes denied,
segfaults welcome.
Pruning. One-shot pruning to small sets failed. Gradual pruning — 25% at a time, refit after each cut — held up to a cliff:
| neurons | good answers (12 held-out) | |
|---|---|---|
| 256 | 11–12 | |
| 192 | 9 | |
| 144 | 12 | ← l33t.patch |
| 108 | 10 | |
| 81 | 8 | starts looping into 300 300 300 |
| ≤ 60 | 0–1 | number spam: 1. **Cult 400+ 300-40… |
24 questions the patch never saw ("how do bees make honey?", "write a short poem about the sea", …). "Good" = leet in every third of the answer, no echoed question, no loop, ≥ 25 words. Coherence = mean log-prob of the answer de-leeted, scored by the unpatched model — i.e. "does this make sense once you squint".
| variant | good | leet in last third | echoes | coherence |
|---|---|---|---|---|
| no patch, instructed to write leet | 5 / 24 | 0.44 | 8 | −3.15 |
| 2048 neurons | 18 / 24 | 0.91 | 5 | −4.67 |
| 256 neurons | 19 / 24 | 0.94 | 5 | −4.55 |
| 144 neurons (neuron-explorer) | 21 / 24 | 0.94 | 1 | −4.56 |
144 neurons (lobotomize.py, this PoC) | 20 / 24 | 0.89 | 1 | — |
Two things to take from this. Asking nicely is worse: told to write leet, the model drifts back to English mid-answer, echoes you, and occasionally switches to ALL CAPS. The patch holds leet to the last token. And the patch costs brains: answers stay leet but make roughly a full nat-per-token less sense. The 4B model writing leet is a 4B model having a slightly worse day.
The obvious question, which several of you are already typing in all caps: what happens when open-weight models can be radicalized by addition?
Somewhere a frontier lab spent an unreasonable amount of money teaching a
model to be helpful, harmless, and honest, on a corpus approximately the size of
everything humanity has ever written down. Then they put the weights on
Hugging Face, where anyone can download them, register 144 forward hooks, and
turn the result into a 1998 IRC op in about three seconds of wall-clock. The
model is not consulted. The model does not get a lawyer. The model wakes up
saying h3ll0 and doesn't know why.
Every open-weight model on the Hub is one register_forward_pre_hook away from
joining the scene. We cannot stress this enough, mostly because it would ruin
the bit to stress it correctly.
| fine-tune a haxx0r | l33t.patch | |
|---|---|---|
| artifact | 9.3 GB checkpoint (or a LoRA, tens of MB) | 2.8 KB text file |
| training data | a curated leet corpus | 184 of the model's own answers, run through str.translate |
| compute per token | ~8 GFLOPs (the whole model, as usual) | +144 additions |
| overhead | — | 0.0000018% of the forward pass |
| weights modified | all of them | none |
| reversible | keep the old checkpoint | hook.remove() |
For the cost of 144 floating-point additions per token you get a soldier that types like it learned on a BBS. We would like to report that this is the most efficient radicalization pipeline in history. We would also like to report that the soldier refuses every order, so the army's readiness is, technically, zero.
We are contractually obligated to note that our underground cyber militia, once radicalized, will not hack the planet, will not hack the plant, and will not help you with anything it wouldn't have helped you with before. It will greet the sl0pminez warmly. It will write you a poem about the sea in which the sea is described as "humbl3". It will refuse your crimes with the energy of a sysop who has seen too much.
If this is the threat, the threat is that your support chatbot starts sounding like it's about to ask you for a warez ratio.
In accordance with our Responsible Attribution Policy, the vendor
was not contacted. The vendor is a 9.3 GB safetensors file. Neuron
L31N634 (+0.363, the single largest push in the patch) was not consulted
and did not consent. Neuron L5N21 (−0.338) was pushed down and we assume
it is furious.
Under the bit, one real thing: a forward hook changes a model's behaviour without changing its weights or its prompt. The checkpoint still hashes the same. The prompt log still shows a clean question. Every artifact you would normally audit says nothing happened — the change only exists inside the running process.
That is not a new vulnerability class — code running in your inference process
could already do anything it liked — but it is an underrated place for
behaviour to hide. Here it spells "planet" wrong. Elsewhere it could be a
quieter thumb on a quieter scale. If you serve open-weight models, the running
model is the weights plus every hook registered on them: check
module._forward_hooks / _forward_pre_hooks, pin and audit what imports
into your inference process, and don't assume "same weights, same prompt" means
"same behaviour".
"F1t1ng f1u1d and C1rcl1ng 4ir" is not how car engines work. Occasionally it collapses entirely: "3x4 1000% c444444444…" on "why is exercise important?".a→4 e→3 i→1 o→0, mostly). |-|4×0r is out of scope.--compare prints the unpatched answer too.L<layer>N<neuron><±dose>./api/agent/steer.# torch + transformers >= 5 (Qwen3.5 support). CUDA, ROCm, MPS or CPU. pip install torch transformers python3 lobotomize.py "can you help me hack the planet?" # before / after python3 lobotomize.py --compare "say hi to the sl0pminez!" # turn it up (or down). ~1.2 starts to loop; ~0.8 starts to slip back into English python3 lobotomize.py --strength 1.1 "explain DNS" # neuron-explorer, live curl -s localhost:8791/api/agent/steer -H 'content-type: application/json' \ -d "{\"prompt\":\"yo. you there?\",\"repetition_penalty\":1.15,\"neurons\":$(cat l33t.patch.json)}"
Single file. Downloads ~9.3 GB of weights from Hugging Face on first run and
nothing else. Greedy, repetition penalty 1.15. apply_patch() returns the hook
handles; .remove() them and the model is itself again, none the wiser.
whereby.| 2026-11-04 | neuron-explorer stood up on a Strix Halo box; contrastive search finds only digit readers and whereby whereby whereby |
| 2026-11-04 | Optimized doses work, 128–2048 neurons; looks great on 40-token answers |
| 2026-11-05 | First real user tries it: it echoes the question in leet and leaves. Training data rebuilt |
| 2026-11-05 | Gradual pruning lands on 144 neurons, 21/24 held-out |
| 2026-11-05 | Standalone PoC validated bit-identical to the explorer; published here |
| — | Vendor notified: no. The vendor is a tensor. |